summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Wrighton <davidwr@microsoft.com>2017-09-13 14:50:39 -0700
committerDavid Wrighton <davidwr@microsoft.com>2017-09-13 14:50:39 -0700
commitd68f0916d0a2bf3787bc85261ef4a4f1f27f1f24 (patch)
tree6c21ac239ae268096f20d98a8db16a4b80394fd9 /src
parent96fa98525e0d64459148228cde5211c475b0c25c (diff)
parente866d072042f4ad9e0811aa36e338dac781c09a5 (diff)
downloadcoreclr-d68f0916d0a2bf3787bc85261ef4a4f1f27f1f24.tar.gz
coreclr-d68f0916d0a2bf3787bc85261ef4a4f1f27f1f24.tar.bz2
coreclr-d68f0916d0a2bf3787bc85261ef4a4f1f27f1f24.zip
Merge branch 'master' into update_from_master
Diffstat (limited to 'src')
-rw-r--r--src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Linux.Microsoft.NETCore.Runtime.CoreCLR.props2
-rw-r--r--src/.nuget/dir.props6
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/NuGet.Config3
-rw-r--r--src/ToolBox/SOS/DacTableGen/main.cs2
-rw-r--r--src/ToolBox/SOS/NETCore/SOS.NETCore.csproj9
-rw-r--r--src/ToolBox/SOS/Strike/strike.cpp194
-rw-r--r--src/ToolBox/SOS/Strike/util.cpp87
-rw-r--r--src/ToolBox/SOS/Strike/util.h7
-rw-r--r--src/ToolBox/superpmi/mcs/CMakeLists.txt1
-rw-r--r--src/ToolBox/superpmi/mcs/commandline.cpp22
-rw-r--r--src/ToolBox/superpmi/mcs/commandline.h2
-rw-r--r--src/ToolBox/superpmi/mcs/mcs.cpp5
-rw-r--r--src/ToolBox/superpmi/mcs/verbsmarty.cpp97
-rw-r--r--src/ToolBox/superpmi/mcs/verbsmarty.h22
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/coreclrcommoncallbacks.h14
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h11
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/lwmlist.h3
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp320
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontext.h38
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp4
-rw-r--r--src/ToolBox/superpmi/superpmi-shared/spmirecordhelper.h2
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp8
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp3
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp18
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp8
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp14
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp8
-rw-r--r--src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp13
-rw-r--r--src/ToolBox/superpmi/superpmi/CMakeLists.txt5
-rw-r--r--src/ToolBox/superpmi/superpmi/commandline.cpp100
-rw-r--r--src/ToolBox/superpmi/superpmi/commandline.h10
-rw-r--r--src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp8
-rw-r--r--src/ToolBox/superpmi/superpmi/icorjitinfo.cpp14
-rw-r--r--src/ToolBox/superpmi/superpmi/jithost.cpp56
-rw-r--r--src/ToolBox/superpmi/superpmi/jithost.h2
-rw-r--r--src/ToolBox/superpmi/superpmi/jitinstance.cpp39
-rw-r--r--src/ToolBox/superpmi/superpmi/jitinstance.h8
-rw-r--r--src/ToolBox/superpmi/superpmi/neardiffer.cpp83
-rw-r--r--src/ToolBox/superpmi/superpmi/neardiffer.h7
-rw-r--r--src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp21
-rw-r--r--src/ToolBox/superpmi/superpmi/superpmi.cpp37
-rw-r--r--src/binder/assemblybinder.cpp82
-rw-r--r--src/binder/inc/assemblyname.inl2
-rw-r--r--src/binder/inc/assemblyversion.hpp20
-rw-r--r--src/binder/inc/assemblyversion.inl134
-rw-r--r--src/binder/textualidentityparser.cpp70
-rw-r--r--src/build.proj14
-rw-r--r--src/classlibnative/bcltype/CMakeLists.txt1
-rw-r--r--src/classlibnative/bcltype/arraynative.cpp2
-rw-r--r--src/classlibnative/bcltype/bignum.cpp599
-rw-r--r--src/classlibnative/bcltype/bignum.h152
-rw-r--r--src/classlibnative/bcltype/number.cpp292
-rw-r--r--src/classlibnative/bcltype/number.h5
-rw-r--r--src/classlibnative/bcltype/stringnative.cpp51
-rw-r--r--src/classlibnative/bcltype/stringnative.h5
-rw-r--r--src/classlibnative/bcltype/system.cpp7
-rw-r--r--src/classlibnative/inc/nlsinfo.h2
-rw-r--r--src/classlibnative/nls/nlsinfo.cpp4
-rw-r--r--src/coreclr/hosts/corerun/corerun.cpp2
-rw-r--r--src/coreclr/hosts/corerun/logger.cpp2
-rw-r--r--src/corefx/System.Globalization.Native/collation.cpp26
-rw-r--r--src/corefx/System.Globalization.Native/icushim.cpp9
-rw-r--r--src/corefx/System.Globalization.Native/icushim.h4
-rw-r--r--src/debug/SetDebugTargetCoreSysARM.props25
-rw-r--r--src/debug/SetDebugTargetCoreSysX86.props23
-rw-r--r--src/debug/SetDebugTargetLocal.props30
-rw-r--r--src/debug/SetDebugTargetWinARM.props19
-rw-r--r--src/debug/SetDebugTargetWinx86.props8
-rw-r--r--src/debug/XPlatCommon.props41
-rw-r--r--src/debug/createdump/crashinfo.cpp255
-rw-r--r--src/debug/createdump/crashinfo.h35
-rw-r--r--src/debug/createdump/datatarget.cpp9
-rw-r--r--src/debug/createdump/datatarget.h10
-rw-r--r--src/debug/createdump/memoryregion.h12
-rw-r--r--src/debug/createdump/threadinfo.cpp153
-rw-r--r--src/debug/createdump/threadinfo.h21
-rw-r--r--src/debug/daccess/daccess.cpp15
-rw-r--r--src/debug/daccess/dacdbiimpl.cpp125
-rw-r--r--src/debug/daccess/dacdbiimpl.h6
-rw-r--r--src/debug/daccess/dacfn.cpp46
-rw-r--r--src/debug/daccess/dacimpl.h14
-rw-r--r--src/debug/daccess/enummem.cpp1
-rw-r--r--src/debug/daccess/gcinterface.dac.h49
-rw-r--r--src/debug/daccess/nidump.cpp55
-rw-r--r--src/debug/daccess/request.cpp254
-rw-r--r--src/debug/di/hash.cpp2
-rw-r--r--src/debug/di/module.cpp8
-rw-r--r--src/debug/di/rsfunction.cpp16
-rw-r--r--src/debug/di/rspriv.h4
-rw-r--r--src/debug/di/rsstackwalk.cpp15
-rw-r--r--src/debug/ee/debugger.cpp1
-rw-r--r--src/debug/ee/funceval.cpp17
-rw-r--r--src/debug/inc/arm/primitives.h4
-rw-r--r--src/debug/inc/dacdbiinterface.h78
-rw-r--r--src/debug/inc/dacdbistructures.h2
-rw-r--r--src/debug/inc/dbgipcevents.h3
-rw-r--r--src/dlls/mscordac/mscordac_unixexports.src2
-rw-r--r--src/dlls/mscoree/coreclr/CMakeLists.txt11
-rw-r--r--src/dlls/mscoree/mscorwks_unixexports.src9
-rw-r--r--src/gc/CMakeLists.txt58
-rw-r--r--src/gc/WarningControl.h0
-rw-r--r--src/gc/env/gcenv.base.h445
-rw-r--r--src/gc/env/gcenv.h73
-rw-r--r--src/gc/env/gcenv.object.h30
-rw-r--r--src/gc/env/volatile.h479
-rw-r--r--src/gc/gc.cpp403
-rw-r--r--src/gc/gc.h7
-rw-r--r--src/gc/gccommon.cpp3
-rw-r--r--src/gc/gcenv.ee.standalone.inl114
-rw-r--r--src/gc/gcenv.inl9
-rw-r--r--src/gc/gcinterface.dac.h61
-rw-r--r--src/gc/gcinterface.dacvars.def1
-rw-r--r--src/gc/gcinterface.h3
-rw-r--r--src/gc/gcpriv.h58
-rw-r--r--src/gc/gcscan.h2
-rw-r--r--src/gc/gcsvr.cpp1
-rw-r--r--src/gc/gcwks.cpp1
-rw-r--r--src/gc/handletable.cpp6
-rw-r--r--src/gc/handletable.h10
-rw-r--r--src/gc/handletablecore.cpp1
-rw-r--r--src/gc/handletablepriv.h13
-rw-r--r--src/gc/objecthandle.cpp21
-rw-r--r--src/gc/objecthandle.h9
-rw-r--r--src/gc/sample/GCSample.cpp2
-rw-r--r--src/gc/sample/gcenv.ee.cpp12
-rw-r--r--src/gc/sample/gcenv.h1
-rw-r--r--src/gc/unix/cgroup.cpp203
-rw-r--r--src/gc/unix/configure.cmake2
-rw-r--r--src/gc/unix/events.cpp4
-rw-r--r--src/gc/unix/gcenv.unix.cpp24
-rw-r--r--src/gc/windows/gcenv.windows.cpp1
-rw-r--r--src/ildasm/ceeload.cpp6
-rw-r--r--src/inc/CMakeLists.txt4
-rw-r--r--src/inc/MSCOREE.IDL2
-rw-r--r--src/inc/clrconfigvalues.h25
-rw-r--r--src/inc/corcompile.h1
-rw-r--r--src/inc/corhlpr.h4
-rw-r--r--src/inc/corinfo.h32
-rw-r--r--src/inc/corjit.h4
-rw-r--r--src/inc/dacprivate.h19
-rw-r--r--src/inc/dacvars.h2
-rw-r--r--src/inc/fixuppointer.h177
-rw-r--r--src/inc/loaderheap.h23
-rw-r--r--src/inc/shash.h1
-rw-r--r--src/inc/sospriv.idl12
-rw-r--r--src/inc/utilcode.h2
-rw-r--r--src/inc/volatile.h19
-rw-r--r--src/inc/winwrap.h439
-rw-r--r--src/jit/ICorJitInfo_API_names.h1
-rw-r--r--src/jit/ICorJitInfo_API_wrapper.hpp16
-rw-r--r--src/jit/alloc.cpp54
-rw-r--r--src/jit/alloc.h73
-rw-r--r--src/jit/assertionprop.cpp15
-rw-r--r--src/jit/block.cpp17
-rw-r--r--src/jit/block.h4
-rw-r--r--src/jit/codegenarm.cpp58
-rw-r--r--src/jit/codegenarm64.cpp69
-rw-r--r--src/jit/codegenarmarch.cpp313
-rw-r--r--src/jit/codegencommon.cpp141
-rw-r--r--src/jit/codegenlegacy.cpp201
-rw-r--r--src/jit/codegenlinear.cpp47
-rw-r--r--src/jit/codegenlinear.h3
-rw-r--r--src/jit/codegenxarch.cpp203
-rw-r--r--src/jit/compiler.cpp104
-rw-r--r--src/jit/compiler.h190
-rw-r--r--src/jit/compiler.hpp122
-rw-r--r--src/jit/copyprop.cpp36
-rw-r--r--src/jit/decomposelongs.cpp53
-rw-r--r--src/jit/decomposelongs.h1
-rw-r--r--src/jit/earlyprop.cpp4
-rw-r--r--src/jit/ee_il_dll.cpp14
-rw-r--r--src/jit/emit.cpp7
-rw-r--r--src/jit/emit.h4
-rw-r--r--src/jit/emitarm.cpp12
-rw-r--r--src/jit/emitarm64.cpp2
-rw-r--r--src/jit/emitxarch.cpp287
-rw-r--r--src/jit/flowgraph.cpp1216
-rw-r--r--src/jit/gentree.cpp1153
-rw-r--r--src/jit/gentree.h347
-rw-r--r--src/jit/gtlist.h2
-rw-r--r--src/jit/gtstructs.h5
-rw-r--r--src/jit/importer.cpp685
-rw-r--r--src/jit/instr.cpp21
-rw-r--r--src/jit/jit.h3
-rw-r--r--src/jit/jitconfig.cpp12
-rw-r--r--src/jit/jitconfigvalues.h1
-rw-r--r--src/jit/jitee.h6
-rw-r--r--src/jit/lclvars.cpp142
-rw-r--r--src/jit/lir.h7
-rw-r--r--src/jit/liveness.cpp678
-rw-r--r--src/jit/loopcloning.cpp2
-rw-r--r--src/jit/lower.cpp884
-rw-r--r--src/jit/lower.h152
-rw-r--r--src/jit/lowerarm.cpp12
-rw-r--r--src/jit/lowerarm64.cpp6
-rw-r--r--src/jit/lowerarmarch.cpp100
-rw-r--r--src/jit/lowerxarch.cpp1076
-rw-r--r--src/jit/lsra.cpp771
-rw-r--r--src/jit/lsra.h91
-rw-r--r--src/jit/lsraarm.cpp200
-rw-r--r--src/jit/lsraarm64.cpp167
-rw-r--r--src/jit/lsraarmarch.cpp399
-rw-r--r--src/jit/lsraxarch.cpp1274
-rw-r--r--src/jit/morph.cpp1314
-rw-r--r--src/jit/namedintrinsiclist.h16
-rw-r--r--src/jit/nodeinfo.h4
-rw-r--r--src/jit/optcse.cpp9
-rw-r--r--src/jit/optimizer.cpp1396
-rw-r--r--src/jit/rangecheck.cpp49
-rw-r--r--src/jit/rationalize.cpp2
-rw-r--r--src/jit/regalloc.cpp46
-rw-r--r--src/jit/regset.cpp93
-rw-r--r--src/jit/simd.cpp14
-rw-r--r--src/jit/ssabuilder.cpp4
-rw-r--r--src/jit/ssaconfig.h2
-rw-r--r--src/jit/target.h9
-rw-r--r--src/jit/utils.cpp29
-rw-r--r--src/jit/valuenum.cpp5
-rw-r--r--src/jit/valuenumfuncs.h1
-rw-r--r--src/mscorlib/ILLinkTrim.xml40
-rw-r--r--src/mscorlib/MembersMustExist.AnalyzerData3
-rw-r--r--src/mscorlib/Resources/Strings.resx93
-rw-r--r--src/mscorlib/System.Private.CoreLib.csproj81
-rw-r--r--src/mscorlib/System.Private.CoreLib.sln11
-rw-r--r--src/mscorlib/Tools/Versioning/GenerateVersionInfo.targets38
-rw-r--r--src/mscorlib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs2
-rw-r--r--src/mscorlib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs2
-rw-r--r--src/mscorlib/shared/Interop/Windows/Interop.Libraries.cs1
-rw-r--r--src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs24
-rw-r--r--src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs29
-rw-r--r--src/mscorlib/shared/Interop/Windows/Kernel32/Interop.FreeLibrary.cs15
-rw-r--r--src/mscorlib/shared/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs18
-rw-r--r--src/mscorlib/shared/Interop/Windows/User32/Interop.LoadString.cs16
-rw-r--r--src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs (renamed from src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs)6
-rw-r--r--src/mscorlib/shared/System.Private.CoreLib.Shared.projitems103
-rw-r--r--src/mscorlib/shared/System.Private.CoreLib.Shared.shproj17
-rw-r--r--src/mscorlib/shared/System/AccessViolationException.cs (renamed from src/mscorlib/src/System/AccessViolationException.cs)6
-rw-r--r--src/mscorlib/shared/System/Action.cs6
-rw-r--r--src/mscorlib/shared/System/AggregateException.cs (renamed from src/mscorlib/src/System/AggregateException.cs)34
-rw-r--r--src/mscorlib/shared/System/ApplicationException.cs6
-rw-r--r--src/mscorlib/shared/System/ArgumentException.cs10
-rw-r--r--src/mscorlib/shared/System/ArgumentNullException.cs8
-rw-r--r--src/mscorlib/shared/System/ArgumentOutOfRangeException.cs10
-rw-r--r--src/mscorlib/shared/System/ArithmeticException.cs6
-rw-r--r--src/mscorlib/shared/System/ArrayTypeMismatchException.cs6
-rw-r--r--src/mscorlib/shared/System/BadImageFormatException.cs12
-rw-r--r--src/mscorlib/shared/System/BitConverter.cs185
-rw-r--r--src/mscorlib/shared/System/Boolean.cs (renamed from src/mscorlib/src/System/Boolean.cs)65
-rw-r--r--src/mscorlib/shared/System/Buffers/ArrayPoolEventSource.cs (renamed from src/mscorlib/src/System/Buffers/ArrayPoolEventSource.cs)0
-rw-r--r--src/mscorlib/shared/System/Buffers/IRetainable.cs15
-rw-r--r--src/mscorlib/shared/System/Buffers/MemoryHandle.cs44
-rw-r--r--src/mscorlib/shared/System/Buffers/OwnedMemory.cs53
-rw-r--r--src/mscorlib/shared/System/Byte.cs (renamed from src/mscorlib/src/System/Byte.cs)60
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs6
-rw-r--r--src/mscorlib/shared/System/Collections/ICollection.cs2
-rw-r--r--src/mscorlib/shared/System/Collections/ListDictionaryInternal.cs (renamed from src/mscorlib/src/System/Collections/ListDictionaryInternal.cs)7
-rw-r--r--src/mscorlib/shared/System/Collections/ObjectModel/Collection.cs (renamed from src/mscorlib/src/System/Collections/ObjectModel/Collection.cs)7
-rw-r--r--src/mscorlib/shared/System/Collections/ObjectModel/ReadOnlyCollection.cs (renamed from src/mscorlib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs)7
-rw-r--r--src/mscorlib/shared/System/Convert.cs4
-rw-r--r--src/mscorlib/shared/System/DBNull.cs10
-rw-r--r--src/mscorlib/shared/System/DataMisalignedException.cs6
-rw-r--r--src/mscorlib/shared/System/DateTime.cs8
-rw-r--r--src/mscorlib/shared/System/DateTimeOffset.cs6
-rw-r--r--src/mscorlib/shared/System/DefaultBinder.cs42
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs30
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs26
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/IEventProvider.cs2
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs10
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs2
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs12
-rw-r--r--src/mscorlib/shared/System/DivideByZeroException.cs6
-rw-r--r--src/mscorlib/shared/System/DllNotFoundException.cs6
-rw-r--r--src/mscorlib/shared/System/Double.cs (renamed from src/mscorlib/src/System/Double.cs)98
-rw-r--r--src/mscorlib/shared/System/DuplicateWaitObjectException.cs8
-rw-r--r--src/mscorlib/shared/System/EntryPointNotFoundException.cs6
-rw-r--r--src/mscorlib/shared/System/ExecutionEngineException.cs6
-rw-r--r--src/mscorlib/shared/System/FieldAccessException.cs6
-rw-r--r--src/mscorlib/shared/System/FormatException.cs6
-rw-r--r--src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs6
-rw-r--r--src/mscorlib/shared/System/Globalization/DateTimeParse.cs321
-rw-r--r--src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs2
-rw-r--r--src/mscorlib/shared/System/Globalization/HijriCalendar.Win32.cs4
-rw-r--r--src/mscorlib/shared/System/Globalization/HijriCalendar.cs4
-rw-r--r--src/mscorlib/shared/System/Globalization/JapaneseCalendar.Win32.cs7
-rw-r--r--src/mscorlib/shared/System/Globalization/NumberFormatInfo.cs31
-rw-r--r--src/mscorlib/shared/System/Globalization/TimeSpanFormat.cs (renamed from src/mscorlib/src/System/Globalization/TimeSpanFormat.cs)305
-rw-r--r--src/mscorlib/shared/System/Globalization/TimeSpanParse.cs1675
-rw-r--r--src/mscorlib/shared/System/Guid.cs (renamed from src/mscorlib/src/System/Guid.cs)768
-rw-r--r--src/mscorlib/shared/System/HResults.cs (renamed from src/mscorlib/src/System/__HResults.cs)69
-rw-r--r--src/mscorlib/shared/System/IO/BinaryWriter.cs36
-rw-r--r--src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs6
-rw-r--r--src/mscorlib/shared/System/IO/DisableMediaInsertionPrompt.cs36
-rw-r--r--src/mscorlib/shared/System/IO/DriveNotFoundException.cs (renamed from src/mscorlib/src/System/IO/DriveNotFoundException.cs)24
-rw-r--r--src/mscorlib/shared/System/IO/EndOfStreamException.cs6
-rw-r--r--src/mscorlib/shared/System/IO/FileLoadException.cs10
-rw-r--r--src/mscorlib/shared/System/IO/FileNotFoundException.cs12
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.Unix.cs445
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.Win32.cs45
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.WinRT.cs32
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.Windows.cs359
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.cs170
-rw-r--r--src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs59
-rw-r--r--src/mscorlib/shared/System/IO/IOException.cs41
-rw-r--r--src/mscorlib/shared/System/IO/Path.cs24
-rw-r--r--src/mscorlib/shared/System/IO/PathInternal.Windows.cs5
-rw-r--r--src/mscorlib/shared/System/IO/PathTooLongException.cs6
-rw-r--r--src/mscorlib/shared/System/IO/PinnedBufferMemoryStream.cs4
-rw-r--r--src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs156
-rw-r--r--src/mscorlib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs20
-rw-r--r--src/mscorlib/shared/System/IO/Win32Marshal.cs20
-rw-r--r--src/mscorlib/shared/System/IndexOutOfRangeException.cs6
-rw-r--r--src/mscorlib/shared/System/InsufficientExecutionStackException.cs6
-rw-r--r--src/mscorlib/shared/System/Int16.cs (renamed from src/mscorlib/src/System/Int16.cs)56
-rw-r--r--src/mscorlib/shared/System/Int32.cs (renamed from src/mscorlib/src/System/Int32.cs)67
-rw-r--r--src/mscorlib/shared/System/Int64.cs (renamed from src/mscorlib/src/System/Int64.cs)51
-rw-r--r--src/mscorlib/shared/System/InvalidCastException.cs6
-rw-r--r--src/mscorlib/shared/System/InvalidOperationException.cs6
-rw-r--r--src/mscorlib/shared/System/InvalidProgramException.cs6
-rw-r--r--src/mscorlib/shared/System/Lazy.cs2
-rw-r--r--src/mscorlib/shared/System/MemberAccessException.cs6
-rw-r--r--src/mscorlib/shared/System/Memory.cs284
-rw-r--r--src/mscorlib/shared/System/MethodAccessException.cs6
-rw-r--r--src/mscorlib/shared/System/MissingMethodException.cs6
-rw-r--r--src/mscorlib/shared/System/MulticastNotSupportedException.cs6
-rw-r--r--src/mscorlib/shared/System/NonSerializedAttribute.cs14
-rw-r--r--src/mscorlib/shared/System/NotFiniteNumberException.cs12
-rw-r--r--src/mscorlib/shared/System/NotImplementedException.cs6
-rw-r--r--src/mscorlib/shared/System/NotSupportedException.cs6
-rw-r--r--src/mscorlib/shared/System/NullReferenceException.cs6
-rw-r--r--src/mscorlib/shared/System/ObjectDisposedException.cs4
-rw-r--r--src/mscorlib/shared/System/OperationCanceledException.cs6
-rw-r--r--src/mscorlib/shared/System/OverflowException.cs6
-rw-r--r--src/mscorlib/shared/System/PlatformNotSupportedException.cs6
-rw-r--r--src/mscorlib/shared/System/Random.cs38
-rw-r--r--src/mscorlib/shared/System/RankException.cs6
-rw-r--r--src/mscorlib/shared/System/ReadOnlyMemory.cs270
-rw-r--r--src/mscorlib/shared/System/ReadOnlySpan.cs50
-rw-r--r--src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs6
-rw-r--r--src/mscorlib/shared/System/Reflection/BindingFlags.cs1
-rw-r--r--src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs2
-rw-r--r--src/mscorlib/shared/System/Reflection/IReflect.cs4
-rw-r--r--src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs2
-rw-r--r--src/mscorlib/shared/System/Reflection/MethodInfo.Internal.cs17
-rw-r--r--src/mscorlib/shared/System/Reflection/MethodInfo.cs2
-rw-r--r--src/mscorlib/shared/System/Reflection/Pointer.cs2
-rw-r--r--src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs4
-rw-r--r--src/mscorlib/shared/System/Reflection/SignatureArrayType.cs46
-rw-r--r--src/mscorlib/shared/System/Reflection/SignatureByRefType.cs27
-rw-r--r--src/mscorlib/shared/System/Reflection/SignatureConstructedGenericType.cs73
-rw-r--r--src/mscorlib/shared/System/Reflection/SignatureGenericMethodParameterType.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/SignatureGenericParameterType.cs46
-rw-r--r--src/mscorlib/shared/System/Reflection/SignatureHasElementType.cs48
-rw-r--r--src/mscorlib/shared/System/Reflection/SignaturePointerType.cs27
-rw-r--r--src/mscorlib/shared/System/Reflection/SignatureType.cs139
-rw-r--r--src/mscorlib/shared/System/Reflection/SignatureTypeExtensions.cs227
-rw-r--r--src/mscorlib/shared/System/Reflection/TargetException.cs2
-rw-r--r--src/mscorlib/shared/System/Reflection/TargetInvocationException.cs4
-rw-r--r--src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs6
-rw-r--r--src/mscorlib/shared/System/Reflection/TypeDelegator.cs1
-rw-r--r--src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs6
-rw-r--r--src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs8
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs21
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs111
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs71
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/CustomConstantAttribute.cs12
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs19
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/DecimalConstantAttribute.cs39
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs13
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/MethodImplAttribute.cs29
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeWrappedException.cs33
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs37
-rw-r--r--src/mscorlib/shared/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs (renamed from src/mscorlib/src/System/Runtime/ExceptionServices/CorruptingExceptionCommon.cs)14
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/BestFitMappingAttribute.cs19
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/DefaultCharSetAttribute.cs17
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs17
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/DllImportAttribute.cs26
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/DllImportSearchPath.cs18
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs6
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/FieldOffsetAttribute.cs17
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/InAttribute.cs14
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/MarshalAsAttribute.cs39
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/OptionalAttribute.cs14
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/OutAttribute.cs14
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/PreserveSigAttribute.cs14
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/StructLayoutAttribute.cs26
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs10
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs6
-rw-r--r--src/mscorlib/shared/System/SByte.cs (renamed from src/mscorlib/src/System/SByte.cs)71
-rw-r--r--src/mscorlib/shared/System/Security/SecurityException.cs10
-rw-r--r--src/mscorlib/shared/System/Security/VerificationException.cs6
-rw-r--r--src/mscorlib/shared/System/SerializableAttribute.cs12
-rw-r--r--src/mscorlib/shared/System/Single.cs (renamed from src/mscorlib/src/System/Single.cs)96
-rw-r--r--src/mscorlib/shared/System/Span.NonGeneric.cs2
-rw-r--r--src/mscorlib/shared/System/Span.cs55
-rw-r--r--src/mscorlib/shared/System/StackOverflowException.cs6
-rw-r--r--src/mscorlib/shared/System/StringSpanHelpers.cs93
-rw-r--r--src/mscorlib/shared/System/SystemException.cs6
-rw-r--r--src/mscorlib/shared/System/Text/Decoder.cs26
-rw-r--r--src/mscorlib/shared/System/Text/DecoderExceptionFallback.cs6
-rw-r--r--src/mscorlib/shared/System/Text/Encoder.cs26
-rw-r--r--src/mscorlib/shared/System/Text/EncoderExceptionFallback.cs6
-rw-r--r--src/mscorlib/shared/System/Text/Encoding.cs44
-rw-r--r--src/mscorlib/shared/System/Text/Normalization.cs15
-rw-r--r--src/mscorlib/shared/System/Text/StringBuilder.cs77
-rw-r--r--src/mscorlib/shared/System/Text/UTF32Encoding.cs7
-rw-r--r--src/mscorlib/shared/System/Text/UTF8Encoding.cs10
-rw-r--r--src/mscorlib/shared/System/Text/UnicodeEncoding.cs7
-rw-r--r--src/mscorlib/shared/System/Threading/AbandonedMutexException.cs12
-rw-r--r--src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs2
-rw-r--r--src/mscorlib/shared/System/Threading/LazyInitializer.cs2
-rw-r--r--src/mscorlib/shared/System/Threading/ReaderWriterLockSlim.cs240
-rw-r--r--src/mscorlib/shared/System/Threading/SpinWait.cs98
-rw-r--r--src/mscorlib/shared/System/Threading/SynchronizationLockException.cs6
-rw-r--r--src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs169
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadAbortException.cs2
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadStartException.cs4
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadStateException.cs6
-rw-r--r--src/mscorlib/shared/System/Threading/TimeoutHelper.cs4
-rw-r--r--src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs6
-rw-r--r--src/mscorlib/shared/System/TimeSpan.cs80
-rw-r--r--src/mscorlib/shared/System/TimeoutException.cs6
-rw-r--r--src/mscorlib/shared/System/Type.cs32
-rw-r--r--src/mscorlib/shared/System/TypeAccessException.cs6
-rw-r--r--src/mscorlib/shared/System/TypeInitializationException.cs6
-rw-r--r--src/mscorlib/shared/System/TypeUnloadedException.cs6
-rw-r--r--src/mscorlib/shared/System/UInt16.cs (renamed from src/mscorlib/src/System/UInt16.cs)59
-rw-r--r--src/mscorlib/shared/System/UInt32.cs (renamed from src/mscorlib/src/System/UInt32.cs)56
-rw-r--r--src/mscorlib/shared/System/UInt64.cs (renamed from src/mscorlib/src/System/UInt64.cs)55
-rw-r--r--src/mscorlib/shared/System/UnauthorizedAccessException.cs6
-rw-r--r--src/mscorlib/shared/System/UnitySerializationHolder.cs66
-rw-r--r--src/mscorlib/shared/System/Version.cs340
-rw-r--r--src/mscorlib/src/Internal/Padding.cs27
-rw-r--r--src/mscorlib/src/Internal/Runtime/Augments/RuntimeThread.cs29
-rw-r--r--src/mscorlib/src/Interop/Unix/Interop.Libraries.cs14
-rw-r--r--src/mscorlib/src/Microsoft/Win32/RegistryKey.cs58
-rw-r--r--src/mscorlib/src/Microsoft/Win32/UnsafeNativeMethods.cs27
-rw-r--r--src/mscorlib/src/Microsoft/Win32/Win32Native.cs260
-rw-r--r--src/mscorlib/src/System/Activator.cs18
-rw-r--r--src/mscorlib/src/System/AppContext/AppContextDefaultValues.cs2
-rw-r--r--src/mscorlib/src/System/AppDomain.cs6
-rw-r--r--src/mscorlib/src/System/AppDomainSetup.cs35
-rw-r--r--src/mscorlib/src/System/AppDomainUnloadedException.cs2
-rw-r--r--src/mscorlib/src/System/Array.cs61
-rw-r--r--src/mscorlib/src/System/Buffer.cs14
-rw-r--r--src/mscorlib/src/System/ByReference.cs2
-rw-r--r--src/mscorlib/src/System/Collections/Concurrent/ConcurrentDictionary.cs614
-rw-r--r--src/mscorlib/src/System/Collections/Concurrent/ConcurrentQueue.cs53
-rw-r--r--src/mscorlib/src/System/Collections/Concurrent/ConcurrentStack.cs94
-rw-r--r--src/mscorlib/src/System/Collections/Generic/Dictionary.cs17
-rw-r--r--src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs5
-rw-r--r--src/mscorlib/src/System/Collections/Hashtable.cs3
-rw-r--r--src/mscorlib/src/System/Decimal.cs37
-rw-r--r--src/mscorlib/src/System/Diagnostics/Contracts/Contracts.cs2
-rw-r--r--src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs2
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs6
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs25
-rw-r--r--src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs2
-rw-r--r--src/mscorlib/src/System/Diagnostics/Stacktrace.cs3
-rw-r--r--src/mscorlib/src/System/Enum.cs17
-rw-r--r--src/mscorlib/src/System/Environment.cs77
-rw-r--r--src/mscorlib/src/System/Exception.cs4
-rw-r--r--src/mscorlib/src/System/Globalization/CompareInfo.Unix.cs18
-rw-r--r--src/mscorlib/src/System/Globalization/CompareInfo.Windows.cs4
-rw-r--r--src/mscorlib/src/System/Globalization/CultureData.Windows.cs16
-rw-r--r--src/mscorlib/src/System/Globalization/CultureData.cs114
-rw-r--r--src/mscorlib/src/System/Globalization/CultureInfo.cs68
-rw-r--r--src/mscorlib/src/System/Globalization/GlobalizationAssembly.cs64
-rw-r--r--src/mscorlib/src/System/Globalization/TextInfo.Unix.cs1
-rw-r--r--src/mscorlib/src/System/Globalization/TextInfo.Windows.cs4
-rw-r--r--src/mscorlib/src/System/Globalization/TextInfo.cs36
-rw-r--r--src/mscorlib/src/System/Globalization/TimeSpanParse.cs1810
-rw-r--r--src/mscorlib/src/System/Guid.CoreCLR.cs27
-rw-r--r--src/mscorlib/src/System/HResults.cs235
-rw-r--r--src/mscorlib/src/System/IO/BinaryReader.cs41
-rw-r--r--src/mscorlib/src/System/IO/Directory.cs6
-rw-r--r--src/mscorlib/src/System/IO/File.cs2
-rw-r--r--src/mscorlib/src/System/IO/FileLoadException.CoreCLR.cs2
-rw-r--r--src/mscorlib/src/System/IO/FileSystemEnumerable.cs2
-rw-r--r--src/mscorlib/src/System/IO/IOException.cs73
-rw-r--r--src/mscorlib/src/System/IO/MemoryStream.cs121
-rw-r--r--src/mscorlib/src/System/IO/Stream.cs123
-rw-r--r--src/mscorlib/src/System/IO/StreamReader.cs14
-rw-r--r--src/mscorlib/src/System/IO/__Error.cs66
-rw-r--r--src/mscorlib/src/System/IO/__HResults.cs31
-rw-r--r--src/mscorlib/src/System/InsufficientMemoryException.cs6
-rw-r--r--src/mscorlib/src/System/MissingFieldException.cs6
-rw-r--r--src/mscorlib/src/System/MissingMemberException.cs6
-rw-r--r--src/mscorlib/src/System/MulticastDelegate.cs71
-rw-r--r--src/mscorlib/src/System/NonSerializedAttribute.cs36
-rw-r--r--src/mscorlib/src/System/Number.cs92
-rw-r--r--src/mscorlib/src/System/OutOfMemoryException.cs6
-rw-r--r--src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs6
-rw-r--r--src/mscorlib/src/System/Reflection/CustomAttribute.cs376
-rw-r--r--src/mscorlib/src/System/Reflection/Emit/AssemblyBuilder.cs2
-rw-r--r--src/mscorlib/src/System/Reflection/Emit/DynamicMethod.cs11
-rw-r--r--src/mscorlib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs2
-rw-r--r--src/mscorlib/src/System/Reflection/Emit/ILGenerator.cs2
-rw-r--r--src/mscorlib/src/System/Reflection/Emit/MethodBuilderInstantiation.cs2
-rw-r--r--src/mscorlib/src/System/Reflection/Emit/ModuleBuilder.cs2
-rw-r--r--src/mscorlib/src/System/Reflection/Emit/ModuleBuilderData.cs8
-rw-r--r--src/mscorlib/src/System/Reflection/Emit/TypeBuilderInstantiation.cs2
-rw-r--r--src/mscorlib/src/System/Reflection/Emit/XXXOnTypeBuilderInstantiation.cs4
-rw-r--r--src/mscorlib/src/System/Reflection/ExceptionHandlingClause.cs2
-rw-r--r--src/mscorlib/src/System/Reflection/INVOCATION_FLAGS.cs2
-rw-r--r--src/mscorlib/src/System/Reflection/MethodBody.cs2
-rw-r--r--src/mscorlib/src/System/Reflection/RtFieldInfo.cs21
-rw-r--r--src/mscorlib/src/System/Reflection/RuntimeAssembly.cs74
-rw-r--r--src/mscorlib/src/System/Reflection/RuntimeConstructorInfo.cs28
-rw-r--r--src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs35
-rw-r--r--src/mscorlib/src/System/Reflection/RuntimeParameterInfo.cs88
-rw-r--r--src/mscorlib/src/System/Resources/FileBasedResourceGroveler.cs7
-rw-r--r--src/mscorlib/src/System/Resources/ManifestBasedResourceGroveler.cs36
-rw-r--r--src/mscorlib/src/System/Resources/ResourceManager.cs28
-rw-r--r--src/mscorlib/src/System/Resources/ResourceSet.cs8
-rw-r--r--src/mscorlib/src/System/Resources/__HResults.cs24
-rw-r--r--src/mscorlib/src/System/RtType.cs89
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs11
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/CustomConstantAttribute.cs29
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs46
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/DecimalConstantAttribute.cs97
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/MethodImplAttribute.cs47
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/RuntimeWrappedException.cs42
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/Unsafe.cs27
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs430
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/COMException.cs6
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/ComEventsHelper.cs2
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/CriticalHandle.cs12
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/InvalidComObjectException.cs6
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs6
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs11
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs6
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/SEHException.cs6
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs6
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs6
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/SafeBuffer.cs17
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/BindableVectorToListAdapter.cs8
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs46
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ConstantSplittableMap.cs2
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryToMapAdapter.cs4
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EnumeratorToIteratorAdapter.cs4
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IMapViewToIReadOnlyDictionaryAdapter.cs4
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyDictionaryToIMapViewAdapter.cs2
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListToIVectorViewAdapter.cs4
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IVectorViewToIReadOnlyListAdapter.cs2
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs2
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorAdapter.cs12
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorViewAdapter.cs4
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToVectorAdapter.cs12
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapToDictionaryAdapter.cs4
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorToListAdapter.cs8
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs2
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/Vector128.cs11
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/Vector256.cs11
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Aes.cs74
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Avx.cs996
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Avx2.cs1362
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Bmi1.cs80
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Bmi2.cs54
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Enums.cs189
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Fma.cs119
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Lzcnt.cs27
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Pclmulqdq.cs27
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Popcnt.cs27
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse.cs218
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse2.cs1057
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse3.cs78
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse41.cs408
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse42.cs233
-rw-r--r--src/mscorlib/src/System/Runtime/Intrinsics/X86/Ssse3.cs92
-rw-r--r--src/mscorlib/src/System/Runtime/MemoryFailPoint.cs50
-rw-r--r--src/mscorlib/src/System/Runtime/Reliability/CriticalFinalizerObject.cs1
-rw-r--r--src/mscorlib/src/System/Runtime/Reliability/PrePrepareMethodAttribute.cs33
-rw-r--r--src/mscorlib/src/System/RuntimeHandles.cs13
-rw-r--r--src/mscorlib/src/System/SerializableAttribute.cs35
-rw-r--r--src/mscorlib/src/System/String.Comparison.cs36
-rw-r--r--src/mscorlib/src/System/String.Searching.cs91
-rw-r--r--src/mscorlib/src/System/String.cs40
-rw-r--r--src/mscorlib/src/System/Threading/CancellationToken.cs2
-rw-r--r--src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs17
-rw-r--r--src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.Windows.cs5
-rw-r--r--src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.cs2
-rw-r--r--src/mscorlib/src/System/Threading/EventWaitHandle.cs13
-rw-r--r--src/mscorlib/src/System/Threading/ManualResetEventSlim.cs47
-rw-r--r--src/mscorlib/src/System/Threading/Mutex.cs7
-rw-r--r--src/mscorlib/src/System/Threading/PinnableBufferCache.cs6
-rw-r--r--src/mscorlib/src/System/Threading/Semaphore.cs8
-rw-r--r--src/mscorlib/src/System/Threading/SemaphoreSlim.cs83
-rw-r--r--src/mscorlib/src/System/Threading/SpinLock.cs70
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs6
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs2
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs19
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs6
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/Task.cs81
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs4
-rw-r--r--src/mscorlib/src/System/Threading/Thread.cs9
-rw-r--r--src/mscorlib/src/System/Threading/ThreadInterruptedException.cs6
-rw-r--r--src/mscorlib/src/System/Threading/ThreadLocal.cs2
-rw-r--r--src/mscorlib/src/System/Threading/ThreadPool.cs204
-rw-r--r--src/mscorlib/src/System/ThrowHelper.cs37
-rw-r--r--src/mscorlib/src/System/TimeZoneInfo.AdjustmentRule.cs2
-rw-r--r--src/mscorlib/src/System/TimeZoneInfo.StringSerializer.cs2
-rw-r--r--src/mscorlib/src/System/TimeZoneInfo.Unix.cs28
-rw-r--r--src/mscorlib/src/System/TimeZoneInfo.Win32.cs12
-rw-r--r--src/mscorlib/src/System/TimeZoneInfo.cs2
-rw-r--r--src/mscorlib/src/System/Type.CoreCLR.cs35
-rw-r--r--src/mscorlib/src/System/TypeLoadException.cs8
-rw-r--r--src/pal/inc/pal.h313
-rw-r--r--src/pal/inc/palprivate.h7
-rw-r--r--src/pal/inc/unixasmmacrosarm.inc1
-rw-r--r--src/pal/prebuilt/idl/sospriv_i.cpp3
-rw-r--r--src/pal/prebuilt/inc/mscoree.h2
-rw-r--r--src/pal/prebuilt/inc/sospriv.h86
-rw-r--r--src/pal/src/CMakeLists.txt3
-rw-r--r--src/pal/src/arch/arm/callsignalhandlerwrapper.S21
-rw-r--r--src/pal/src/arch/arm/signalhandlerhelper.cpp15
-rw-r--r--src/pal/src/config.h.in2
-rw-r--r--src/pal/src/configure.cmake2
-rw-r--r--src/pal/src/cruntime/filecrt.cpp2
-rw-r--r--src/pal/src/cruntime/misctls.cpp165
-rw-r--r--src/pal/src/cruntime/wchar.cpp12
-rw-r--r--src/pal/src/exception/remote-unwind.cpp1086
-rw-r--r--src/pal/src/exception/seh-unwind.cpp252
-rw-r--r--src/pal/src/exception/signal.cpp36
-rw-r--r--src/pal/src/file/disk.cpp180
-rw-r--r--src/pal/src/file/file.cpp500
-rw-r--r--src/pal/src/file/filetime.cpp426
-rw-r--r--src/pal/src/file/shmfilelockmgr.cpp40
-rw-r--r--src/pal/src/include/pal/context.h10
-rw-r--r--src/pal/src/include/pal/corunix.hpp60
-rw-r--r--src/pal/src/include/pal/event.hpp9
-rw-r--r--src/pal/src/include/pal/file.hpp47
-rw-r--r--src/pal/src/include/pal/identity.hpp57
-rw-r--r--src/pal/src/include/pal/init.h2
-rw-r--r--src/pal/src/include/pal/map.hpp11
-rw-r--r--src/pal/src/include/pal/palinternal.h3
-rw-r--r--src/pal/src/include/pal/semaphore.hpp9
-rw-r--r--src/pal/src/include/pal/shm.hpp45
-rw-r--r--src/pal/src/include/pal/shmemory.h212
-rw-r--r--src/pal/src/include/pal/synchcache.hpp16
-rw-r--r--src/pal/src/include/pal/synchobjects.hpp5
-rw-r--r--src/pal/src/init/pal.cpp69
-rw-r--r--src/pal/src/locale/unicode.cpp55
-rw-r--r--src/pal/src/map/map.cpp195
-rw-r--r--src/pal/src/memory/heap.cpp22
-rw-r--r--src/pal/src/misc/cgroup.cpp214
-rw-r--r--src/pal/src/misc/eventlog.cpp423
-rw-r--r--src/pal/src/misc/identity.cpp410
-rw-r--r--src/pal/src/misc/sysinfo.cpp89
-rw-r--r--src/pal/src/objmgr/palobjbase.cpp10
-rw-r--r--src/pal/src/objmgr/shmobject.cpp358
-rw-r--r--src/pal/src/objmgr/shmobject.hpp26
-rw-r--r--src/pal/src/objmgr/shmobjectmanager.cpp351
-rw-r--r--src/pal/src/objmgr/shmobjectmanager.hpp7
-rw-r--r--src/pal/src/shmemory/shmemory.cpp1342
-rw-r--r--src/pal/src/synchmgr/synchcontrollers.cpp20
-rw-r--r--src/pal/src/synchmgr/synchmanager.cpp33
-rw-r--r--src/pal/src/synchmgr/synchmanager.hpp2
-rw-r--r--src/pal/src/synchobj/event.cpp88
-rw-r--r--src/pal/src/synchobj/mutex.cpp6
-rw-r--r--src/pal/src/synchobj/semaphore.cpp85
-rw-r--r--src/pal/src/thread/process.cpp11
-rw-r--r--src/pal/src/thread/thread.cpp95
-rw-r--r--src/pal/tests/palsuite/README.txt6
-rw-r--r--src/pal/tests/palsuite/c_runtime/CMakeLists.txt1
-rw-r--r--src/pal/tests/palsuite/c_runtime/_ecvt/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/c_runtime/_ecvt/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/c_runtime/_ecvt/test1/test1.cpp135
-rw-r--r--src/pal/tests/palsuite/c_runtime/_ecvt/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/c_runtime/wcsstr/test1/test1.cpp10
-rw-r--r--src/pal/tests/palsuite/file_io/AreFileApisANSI/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/AreFileApisANSI.cpp40
-rw-r--r--src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/file_io/CMakeLists.txt9
-rw-r--r--src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/test1.cpp116
-rw-r--r--src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/file_io/GetConsoleCP/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/file_io/GetConsoleCP/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetConsoleCP/test1/GetConsoleCP.cpp35
-rw-r--r--src/pal/tests/palsuite/file_io/GetConsoleCP/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/CMakeLists.txt5
-rw-r--r--src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/GetDiskFreeSpaceW.cpp94
-rw-r--r--src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/getdiskfreespacew.cpp65
-rw-r--r--src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/testinfo.dat12
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileAttributesExW/test1/test1.cpp53
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/CMakeLists.txt10
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test1/GetFileTime.cpp180
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test1/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test2/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test2/GetFileTime.cpp195
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test2/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test3/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test3/GetFileTime.cpp142
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test3/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test4/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test4/GetFileTime.cpp98
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test4/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test5/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test5/getfiletime.cpp224
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test5/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test6/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test6/getfiletime.cpp281
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test6/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test7/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test7/getfiletime.cpp279
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileTime/test7/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileType/CMakeLists.txt6
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileType/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileType/test1/GetFileType.cpp76
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileType/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileType/test2/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileType/test2/getfiletype.cpp95
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileType/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileType/test3/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileType/test3/getfiletype.cpp72
-rw-r--r--src/pal/tests/palsuite/file_io/GetFileType/test3/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/file_io/GetStdHandle/test1/GetStdHandle.cpp9
-rw-r--r--src/pal/tests/palsuite/file_io/MoveFileA/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/file_io/MoveFileA/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/MoveFileA/test1/ExpectedResults.txt1
-rw-r--r--src/pal/tests/palsuite/file_io/MoveFileA/test1/MoveFileA.cpp469
-rw-r--r--src/pal/tests/palsuite/file_io/MoveFileA/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/file_io/MoveFileW/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/file_io/MoveFileW/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/MoveFileW/test1/ExpectedResults.txt1
-rw-r--r--src/pal/tests/palsuite/file_io/MoveFileW/test1/MoveFileW.cpp478
-rw-r--r--src/pal/tests/palsuite/file_io/MoveFileW/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/CMakeLists.txt7
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test1/SetFileTime.cpp129
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test2/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test2/SetFileTime.cpp101
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test2/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test3/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test3/SetFileTime.cpp66
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test3/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test4/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test4/SetFileTime.cpp108
-rw-r--r--src/pal/tests/palsuite/file_io/SetFileTime/test4/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/file_io/errorpathnotfound/CMakeLists.txt1
-rw-r--r--src/pal/tests/palsuite/file_io/errorpathnotfound/test2/test2.cpp76
-rw-r--r--src/pal/tests/palsuite/file_io/errorpathnotfound/test2/testinfo.dat2
-rw-r--r--src/pal/tests/palsuite/file_io/errorpathnotfound/test4/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/file_io/errorpathnotfound/test4/test4.cpp351
-rw-r--r--src/pal/tests/palsuite/file_io/errorpathnotfound/test4/testinfo.dat34
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/CMakeLists.txt2
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/CMakeLists.txt10
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/LockFile.h152
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/CMakeLists.txt32
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/helper.cpp86
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/test1.cpp140
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/test2.cpp94
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/CMakeLists.txt32
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/helper.cpp102
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/test3.cpp71
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/test4.cpp231
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/testinfo.dat14
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/CMakeLists.txt32
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/helper.cpp122
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/test5.cpp161
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/CMakeLists.txt32
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/helper.cpp71
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/test6.cpp146
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/testinfo.dat15
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/test7.cpp135
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/CMakeLists.txt7
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/UnlockFile.h112
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/CMakeLists.txt32
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/helper.cpp92
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/test1.cpp154
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/test2.cpp154
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/CMakeLists.txt32
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/helper.cpp103
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/test3.cpp142
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/testinfo.dat17
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/test4.cpp187
-rw-r--r--src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/testinfo.dat16
-rw-r--r--src/pal/tests/palsuite/miscellaneous/CMakeLists.txt1
-rw-r--r--src/pal/tests/palsuite/miscellaneous/GetComputerNameW/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/test.cpp53
-rw-r--r--src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/miscellaneous/GetUserNameW/CMakeLists.txt4
-rw-r--r--src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/test.cpp52
-rw-r--r--src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/testinfo.dat13
-rw-r--r--src/pal/tests/palsuite/paltestlist.txt24
-rw-r--r--src/pal/tests/palsuite/paltestlist_to_be_reviewed.txt13
-rw-r--r--src/pal/tests/palsuite/palverify.dat20
-rw-r--r--src/scripts/genEventPipe.py39
-rw-r--r--src/scripts/pgocheck.py77
-rw-r--r--src/unwinder/unwinder.cpp2
-rw-r--r--src/utilcode/loaderheap.cpp35
-rw-r--r--src/utilcode/securityutil.cpp2
-rw-r--r--src/utilcode/util.cpp65
-rw-r--r--src/utilcode/winfix.cpp39
-rw-r--r--src/vm/CMakeLists.txt31
-rw-r--r--src/vm/amd64/asmhelpers.S308
-rw-r--r--src/vm/amd64/cgenamd64.cpp13
-rw-r--r--src/vm/amd64/cgencpu.h1
-rw-r--r--src/vm/amd64/profiler.cpp25
-rw-r--r--src/vm/amd64/unixstubs.cpp15
-rw-r--r--src/vm/appdomain.cpp61
-rw-r--r--src/vm/appdomain.hpp35
-rw-r--r--src/vm/appdomainnative.cpp1
-rw-r--r--src/vm/arm/armsinglestepper.cpp12
-rw-r--r--src/vm/arm/cgencpu.h5
-rw-r--r--src/vm/arm/stubs.cpp40
-rw-r--r--src/vm/arm64/asmconstants.h34
-rw-r--r--src/vm/arm64/asmhelpers.S278
-rw-r--r--src/vm/arm64/asmhelpers.asm104
-rw-r--r--src/vm/arm64/cgencpu.h11
-rw-r--r--src/vm/arm64/stubs.cpp52
-rw-r--r--src/vm/arm64/virtualcallstubcpu.hpp4
-rw-r--r--src/vm/armsinglestepper.h4
-rw-r--r--src/vm/array.cpp11
-rw-r--r--src/vm/assembly.cpp1
-rw-r--r--src/vm/assemblyname.cpp6
-rw-r--r--src/vm/assemblynative.cpp35
-rw-r--r--src/vm/assemblynative.hpp5
-rw-r--r--src/vm/assemblyspec.cpp81
-rw-r--r--src/vm/binder.cpp101
-rw-r--r--src/vm/binder.h1
-rw-r--r--src/vm/callcounter.cpp19
-rw-r--r--src/vm/callcounter.h3
-rw-r--r--src/vm/ceeload.cpp314
-rw-r--r--src/vm/ceeload.h148
-rw-r--r--src/vm/ceeload.inl14
-rw-r--r--src/vm/ceemain.cpp26
-rw-r--r--src/vm/class.cpp247
-rw-r--r--src/vm/class.h162
-rw-r--r--src/vm/class.inl18
-rw-r--r--src/vm/classcompat.cpp7
-rw-r--r--src/vm/classnames.h1
-rw-r--r--src/vm/clrex.cpp2
-rw-r--r--src/vm/clrex.h2
-rw-r--r--src/vm/clsload.cpp456
-rw-r--r--src/vm/clsload.hpp47
-rw-r--r--src/vm/clsload.inl4
-rw-r--r--src/vm/codeman.cpp40
-rw-r--r--src/vm/codeman.h4
-rw-r--r--src/vm/codepitchingmanager.cpp522
-rw-r--r--src/vm/codeversion.cpp2862
-rw-r--r--src/vm/codeversion.h689
-rw-r--r--src/vm/comcallablewrapper.cpp43
-rw-r--r--src/vm/comcallablewrapper.h8
-rw-r--r--src/vm/comdelegate.cpp182
-rw-r--r--src/vm/comdelegate.h6
-rw-r--r--src/vm/commodule.cpp1
-rw-r--r--src/vm/compile.cpp1
-rw-r--r--src/vm/comsynchronizable.cpp50
-rw-r--r--src/vm/comsynchronizable.h1
-rw-r--r--src/vm/comthreadpool.cpp1
-rw-r--r--src/vm/comtoclrcall.cpp76
-rw-r--r--src/vm/comtoclrcall.h16
-rw-r--r--src/vm/comutilnative.cpp374
-rw-r--r--src/vm/comutilnative.h17
-rw-r--r--src/vm/crossgen/CMakeLists.txt2
-rw-r--r--src/vm/crossgen_mscorlib/CMakeLists.txt1
-rw-r--r--src/vm/crossgencompile.cpp5
-rw-r--r--src/vm/crst.h9
-rw-r--r--src/vm/customattribute.cpp1
-rw-r--r--src/vm/dataimage.cpp6
-rw-r--r--src/vm/dataimage.h78
-rw-r--r--src/vm/debughelp.cpp2
-rw-r--r--src/vm/dispatchinfo.cpp45
-rw-r--r--src/vm/dllimport.cpp43
-rw-r--r--src/vm/dllimport.h7
-rw-r--r--src/vm/dllimportcallback.cpp12
-rw-r--r--src/vm/dllimportcallback.h4
-rw-r--r--src/vm/domainfile.cpp5
-rw-r--r--src/vm/dynamicmethod.cpp1
-rw-r--r--src/vm/dynamicmethod.h7
-rw-r--r--src/vm/ecall.cpp8
-rw-r--r--src/vm/ecall.h1
-rw-r--r--src/vm/ecalllist.h16
-rw-r--r--src/vm/eeconfig.cpp23
-rw-r--r--src/vm/eeconfig.h25
-rw-r--r--src/vm/eventpipe.cpp268
-rw-r--r--src/vm/eventpipe.h85
-rw-r--r--src/vm/eventpipebuffer.cpp11
-rw-r--r--src/vm/eventpipebuffer.h3
-rw-r--r--src/vm/eventpipebuffermanager.cpp71
-rw-r--r--src/vm/eventpipebuffermanager.h4
-rw-r--r--src/vm/eventpipeconfiguration.cpp67
-rw-r--r--src/vm/eventpipeconfiguration.h8
-rw-r--r--src/vm/eventpipeeventinstance.cpp12
-rw-r--r--src/vm/eventpipefile.cpp3
-rw-r--r--src/vm/eventpipeprovider.cpp13
-rw-r--r--src/vm/eventpipeprovider.h9
-rw-r--r--src/vm/eventtrace.cpp6
-rw-r--r--src/vm/exceptionhandling.cpp35
-rw-r--r--src/vm/fastserializableobject.h35
-rw-r--r--src/vm/fastserializer.cpp4
-rw-r--r--src/vm/finalizerthread.cpp2
-rw-r--r--src/vm/frames.cpp1
-rw-r--r--src/vm/gccover.cpp10
-rw-r--r--src/vm/gcenv.ee.cpp18
-rw-r--r--src/vm/gchandleutilities.h5
-rw-r--r--src/vm/gdbjit.cpp1354
-rw-r--r--src/vm/gdbjit.h36
-rw-r--r--src/vm/genericdict.cpp2
-rw-r--r--src/vm/generics.cpp11
-rw-r--r--src/vm/genmeth.cpp28
-rw-r--r--src/vm/hosting.cpp15
-rw-r--r--src/vm/i386/cgencpu.h1
-rw-r--r--src/vm/i386/cgenx86.cpp8
-rw-r--r--src/vm/i386/stublinkerx86.cpp3
-rw-r--r--src/vm/ilinstrumentation.cpp90
-rw-r--r--src/vm/ilinstrumentation.h116
-rw-r--r--src/vm/interpreter.cpp106
-rw-r--r--src/vm/interpreter.h3
-rw-r--r--src/vm/invokeutil.cpp3
-rw-r--r--src/vm/invokeutil.h8
-rw-r--r--src/vm/jithelpers.cpp10
-rw-r--r--src/vm/jitinterface.cpp505
-rw-r--r--src/vm/jitinterface.h19
-rw-r--r--src/vm/listlock.cpp96
-rw-r--r--src/vm/listlock.h179
-rw-r--r--src/vm/listlock.inl51
-rw-r--r--src/vm/loaderallocator.cpp4
-rw-r--r--src/vm/loaderallocator.hpp4
-rw-r--r--src/vm/marshalnative.cpp1
-rw-r--r--src/vm/memberload.cpp2
-rw-r--r--src/vm/metasig.h15
-rw-r--r--src/vm/method.cpp238
-rw-r--r--src/vm/method.hpp408
-rw-r--r--src/vm/method.inl14
-rw-r--r--src/vm/methodtable.cpp226
-rw-r--r--src/vm/methodtable.h218
-rw-r--r--src/vm/methodtable.inl76
-rw-r--r--src/vm/methodtablebuilder.cpp285
-rw-r--r--src/vm/methodtablebuilder.h2
-rw-r--r--src/vm/mngstdinterfaces.cpp4
-rw-r--r--src/vm/mscorlib.h25
-rw-r--r--src/vm/multicorejit.cpp4
-rw-r--r--src/vm/multicorejit.h2
-rw-r--r--src/vm/multicorejitplayer.cpp28
-rw-r--r--src/vm/object.h6
-rw-r--r--src/vm/olevariant.cpp25
-rw-r--r--src/vm/pefile.cpp1
-rw-r--r--src/vm/perfmap.cpp43
-rw-r--r--src/vm/perfmap.h6
-rw-r--r--src/vm/precode.cpp11
-rw-r--r--src/vm/prestub.cpp1372
-rw-r--r--src/vm/profilingenumerators.cpp2
-rw-r--r--src/vm/proftoeeinterfaceimpl.cpp35
-rw-r--r--src/vm/readytoruninfo.cpp6
-rw-r--r--src/vm/reflectioninvocation.cpp221
-rw-r--r--src/vm/reflectioninvocation.h3
-rw-r--r--src/vm/rejit.cpp3248
-rw-r--r--src/vm/rejit.h482
-rw-r--r--src/vm/rejit.inl253
-rw-r--r--src/vm/runtimehandles.cpp63
-rw-r--r--src/vm/runtimehandles.h10
-rw-r--r--src/vm/sampleprofiler.cpp4
-rw-r--r--src/vm/sampleprofiler.h2
-rw-r--r--src/vm/security.cpp48
-rw-r--r--src/vm/security.h93
-rw-r--r--src/vm/siginfo.cpp10
-rw-r--r--src/vm/stdinterfaces.cpp21
-rw-r--r--src/vm/stubhelpers.cpp3
-rw-r--r--src/vm/syncblk.inl2
-rw-r--r--src/vm/threadpoolrequest.cpp7
-rw-r--r--src/vm/threadpoolrequest.h22
-rw-r--r--src/vm/threads.cpp99
-rw-r--r--src/vm/threads.h71
-rw-r--r--src/vm/tieredcompilation.cpp257
-rw-r--r--src/vm/tieredcompilation.h19
-rw-r--r--src/vm/typehandle.cpp11
-rw-r--r--src/vm/typehandle.h5
-rw-r--r--src/vm/util.cpp39
-rw-r--r--src/vm/util.hpp13
-rw-r--r--src/vm/vars.cpp1
-rw-r--r--src/vm/vars.hpp1
-rw-r--r--src/vm/virtualcallstub.cpp20
-rw-r--r--src/vm/win32threadpool.cpp18
-rw-r--r--src/vm/win32threadpool.h33
-rw-r--r--src/zap/zapimage.cpp5
-rw-r--r--src/zap/zapinfo.cpp31
-rw-r--r--src/zap/zapinfo.h7
-rw-r--r--src/zap/zapperstats.cpp4
1002 files changed, 41997 insertions, 37378 deletions
diff --git a/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Linux.Microsoft.NETCore.Runtime.CoreCLR.props b/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Linux.Microsoft.NETCore.Runtime.CoreCLR.props
index d62f4d645c..dd34d3dae5 100644
--- a/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Linux.Microsoft.NETCore.Runtime.CoreCLR.props
+++ b/src/.nuget/Microsoft.NETCore.Runtime.CoreCLR/runtime.Linux.Microsoft.NETCore.Runtime.CoreCLR.props
@@ -4,11 +4,13 @@
<_PlatformDoesNotSupportCreatedump Condition="'$(Platform)' == 'arm'">true</_PlatformDoesNotSupportCreatedump>
<_PlatformDoesNotSupportCreatedump Condition="'$(Platform)' == 'armel'">true</_PlatformDoesNotSupportCreatedump>
<_PlatformDoesNotSupportCreatedump Condition="'$(Platform)' == 'x86'">true</_PlatformDoesNotSupportCreatedump>
+ <_PlatformDoesNotSupportCreatedump Condition="'$(Platform)' == 'arm64'">true</_PlatformDoesNotSupportCreatedump>
<_PlatformDoesNotSupportCreatedump Condition="'$(_runtimeOSFamily)' == 'tizen'">true</_PlatformDoesNotSupportCreatedump>
<_PlatformDoesNotSupportEventTrace Condition="'$(_runtimeOSFamily)' == 'tizen'">true</_PlatformDoesNotSupportEventTrace>
<_PlatformDoesNotSupportEventTrace Condition="'$(Platform)' == 'arm64'">true</_PlatformDoesNotSupportEventTrace>
<_PlatformDoesNotSupportEventTrace Condition="'$(Platform)' == 'x86'">true</_PlatformDoesNotSupportEventTrace>
<_PlatformDoesNotSupportSosPlugin Condition="'$(_runtimeOSFamily)' == 'android'">true</_PlatformDoesNotSupportSosPlugin>
+ <_PlatformDoesNotSupportSosPlugin Condition="'$(Platform)' == 'arm64'">true</_PlatformDoesNotSupportSosPlugin>
</PropertyGroup>
<ItemGroup>
<NativeBinary Include="$(BinDir)libcoreclr.so" />
diff --git a/src/.nuget/dir.props b/src/.nuget/dir.props
index 52e94365dc..33adec27d7 100644
--- a/src/.nuget/dir.props
+++ b/src/.nuget/dir.props
@@ -92,7 +92,7 @@
</When>
<When Condition="'$(_runtimeOSFamily)' == 'rhel'">
<PropertyGroup>
- <PackageRID>rhel.7-$(ArchGroup)</PackageRID>
+ <PackageRID>$(OSRid)-$(ArchGroup)</PackageRID>
<!-- Set the platform part of the RID if we are doing a portable build -->
<PackageRID Condition="'$(PortableBuild)' == 'true'">linux-$(ArchGroup)</PackageRID>
</PropertyGroup>
@@ -132,9 +132,13 @@
<ItemGroup Condition="$(SupportedPackageOSGroups.Contains(';Linux;'))">
<OfficialBuildRID Include="linux-x64" />
+ <OfficialBuildRID Include="rhel.6-x64" />
<OfficialBuildRID Include="linux-arm">
<Platform>arm</Platform>
</OfficialBuildRID>
+ <OfficialBuildRID Include="linux-arm64">
+ <Platform>arm64</Platform>
+ </OfficialBuildRID>
<OfficialBuildRID Include="tizen.4.0.0-armel">
<Platform>armel</Platform>
</OfficialBuildRID>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ba17dc05ce..900ba96f53 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -155,9 +155,6 @@ add_subdirectory(utilcode)
add_subdirectory(gcinfo)
add_subdirectory(coreclr)
add_subdirectory(jit)
-if(FEATURE_STANDALONE_GC)
- add_subdirectory(gc)
-endif(FEATURE_STANDALONE_GC)
add_subdirectory(vm)
add_subdirectory(md)
add_subdirectory(debug)
diff --git a/src/NuGet.Config b/src/NuGet.Config
index 0054b4914e..57f1b8bbe9 100644
--- a/src/NuGet.Config
+++ b/src/NuGet.Config
@@ -4,10 +4,11 @@
<add key="enabled" value="True" />
</packageRestore>
<packageSources>
+ <clear />
<add key="myget.org dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
</packageSources>
<activePackageSource>
<add key="All" value="(Aggregate source)" />
</activePackageSource>
-</configuration> \ No newline at end of file
+</configuration>
diff --git a/src/ToolBox/SOS/DacTableGen/main.cs b/src/ToolBox/SOS/DacTableGen/main.cs
index 03d1bfde89..3386a34596 100644
--- a/src/ToolBox/SOS/DacTableGen/main.cs
+++ b/src/ToolBox/SOS/DacTableGen/main.cs
@@ -319,7 +319,7 @@ Required:
// appear to be any mechanism for turning this sub-directory probing off, but all other searching mechanisms
// should be turned off by the DiaLoadCallback. This could also happen if the user specified an incorrect
// (but still existing) filename in a path containing the real PDB. Since DIA loaded it, it must match the DLL,
- // and so should only be an exact copy of the requested PDB (if the requested PDB actuall matches the DLL). So
+ // and so should only be an exact copy of the requested PDB (if the requested PDB actually matches the DLL). So
// go ahead and use it anyway with a warning. To be less confusing, we could update the command-line syntax
// to take a PDB search path instead of a filename, but that inconsistent with the map path, and probably not
// worth changing semantics for. In practice this warning will probably never be hit.
diff --git a/src/ToolBox/SOS/NETCore/SOS.NETCore.csproj b/src/ToolBox/SOS/NETCore/SOS.NETCore.csproj
index c579b59dc0..ae5660fb78 100644
--- a/src/ToolBox/SOS/NETCore/SOS.NETCore.csproj
+++ b/src/ToolBox/SOS/NETCore/SOS.NETCore.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
@@ -48,14 +48,11 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NETCore.Platforms">
- <Version>$(RuntimeIdGraphDefinitionVersion)</Version>
- </PackageReference>
<PackageReference Include="System.IO.FileSystem">
- <Version>4.0.1</Version>
+ <Version>4.3.0</Version>
</PackageReference>
<PackageReference Include="System.Runtime.InteropServices">
- <Version>4.1.0</Version>
+ <Version>4.3.0</Version>
</PackageReference>
<PackageReference Include="System.Reflection.Metadata">
<Version>1.4.1</Version>
diff --git a/src/ToolBox/SOS/Strike/strike.cpp b/src/ToolBox/SOS/Strike/strike.cpp
index 7e01635100..1663e3f032 100644
--- a/src/ToolBox/SOS/Strike/strike.cpp
+++ b/src/ToolBox/SOS/Strike/strike.cpp
@@ -320,18 +320,23 @@ DECLARE_API(IP2MD)
// (MAX_STACK_FRAMES is also used by x86 to prevent infinite loops in _EFN_StackTrace)
#define MAX_STACK_FRAMES 1000
-#ifdef _TARGET_WIN64_
+#if defined(_TARGET_WIN64_)
+#define DEBUG_STACK_CONTEXT AMD64_CONTEXT
+#elif defined(_TARGET_ARM_) // _TARGET_WIN64_
+#define DEBUG_STACK_CONTEXT ARM_CONTEXT
+#endif // _TARGET_ARM_
+#ifdef DEBUG_STACK_CONTEXT
// I use a global set of frames for stack walking on win64 because the debugger's
// GetStackTrace function doesn't provide a way to find out the total size of a stackwalk,
// and I'd like to have a reasonably big maximum without overflowing the stack by declaring
// the buffer locally and I also want to get a managed trace in a low memory environment
// (so no dynamic allocation if possible).
DEBUG_STACK_FRAME g_Frames[MAX_STACK_FRAMES];
-AMD64_CONTEXT g_X64FrameContexts[MAX_STACK_FRAMES];
+DEBUG_STACK_CONTEXT g_FrameContexts[MAX_STACK_FRAMES];
static HRESULT
-GetContextStackTrace(PULONG pnumFrames)
+GetContextStackTrace(ULONG osThreadId, PULONG pnumFrames)
{
PDEBUG_CONTROL4 debugControl4;
HRESULT hr;
@@ -339,7 +344,15 @@ GetContextStackTrace(PULONG pnumFrames)
// Do we have advanced capability?
if ((hr = g_ExtControl->QueryInterface(__uuidof(IDebugControl4), (void **)&debugControl4)) == S_OK)
{
- // GetContextStackTrace fills g_X64FrameContexts as an array of
+ ULONG oldId, id;
+ g_ExtSystem->GetCurrentThreadId(&oldId);
+
+ if ((hr = g_ExtSystem->GetThreadIdBySystemId(osThreadId, &id)) != S_OK) {
+ return hr;
+ }
+ g_ExtSystem->SetCurrentThreadId(id);
+
+ // GetContextStackTrace fills g_FrameContexts as an array of
// contexts packed as target architecture contexts. We cannot
// safely cast this as an array of CROSS_PLATFORM_CONTEXT, since
// sizeof(CROSS_PLATFORM_CONTEXT) != sizeof(TGT_CONTEXT)
@@ -348,17 +361,18 @@ GetContextStackTrace(PULONG pnumFrames)
0,
g_Frames,
MAX_STACK_FRAMES,
- g_X64FrameContexts,
+ g_FrameContexts,
MAX_STACK_FRAMES*g_targetMachine->GetContextSize(),
g_targetMachine->GetContextSize(),
pnumFrames);
+ g_ExtSystem->SetCurrentThreadId(oldId);
debugControl4->Release();
}
return hr;
}
-#endif // _TARGET_WIN64_
+#endif // DEBUG_STACK_CONTEXT
/**********************************************************************\
* Routine Description: *
@@ -418,6 +432,9 @@ void DumpStackInternal(DumpStackFlag *pDSFlag)
DumpStackWorker(*pDSFlag);
}
+#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_)
+static BOOL UnwindStackFrames(ULONG32 osThreadId);
+#endif
DECLARE_API(DumpStack)
{
@@ -431,11 +448,13 @@ DECLARE_API(DumpStack)
DSFlag.top = 0;
DSFlag.end = 0;
+ BOOL unwind = FALSE;
BOOL dml = FALSE;
CMDOption option[] = {
// name, vptr, type, hasValue
{"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
{"-n", &DSFlag.fSuppressSrcInfo, COBOOL, FALSE},
+ {"-unwind", &unwind, COBOOL, FALSE},
#ifndef FEATURE_PAL
{"/d", &dml, COBOOL, FALSE}
#endif
@@ -459,13 +478,22 @@ DECLARE_API(DumpStack)
EnableDMLHolder enabledml(dml);
- ULONG id = 0;
- g_ExtSystem->GetCurrentThreadSystemId(&id);
- ExtOut("OS Thread Id: 0x%x ", id);
+ ULONG sysId = 0, id = 0;
+ g_ExtSystem->GetCurrentThreadSystemId(&sysId);
+ ExtOut("OS Thread Id: 0x%x ", sysId);
g_ExtSystem->GetCurrentThreadId(&id);
ExtOut("(%d)\n", id);
- DumpStackInternal(&DSFlag);
+#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_)
+ if (unwind)
+ {
+ UnwindStackFrames(sysId);
+ }
+ else
+#endif
+ {
+ DumpStackInternal(&DSFlag);
+ }
return Status;
}
@@ -12040,12 +12068,12 @@ public:
return;
}
-#ifdef _TARGET_WIN64_
+#ifdef DEBUG_STACK_CONTEXT
PDEBUG_STACK_FRAME currentNativeFrame = NULL;
ULONG numNativeFrames = 0;
if (bFull)
{
- hr = GetContextStackTrace(&numNativeFrames);
+ hr = GetContextStackTrace(osID, &numNativeFrames);
if (FAILED(hr))
{
ExtOut("Failed to get native stack frames: %lx\n", hr);
@@ -12053,7 +12081,7 @@ public:
}
currentNativeFrame = &g_Frames[0];
}
-#endif // _TARGET_WIN64_
+#endif // DEBUG_STACK_CONTEXT
unsigned int refCount = 0, errCount = 0;
ArrayHolder<SOSStackRefData> pRefs = NULL;
@@ -12079,7 +12107,7 @@ public:
if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
sp = FrameData.frameAddr;
-#ifdef _TARGET_WIN64_
+#ifdef DEBUG_STACK_CONTEXT
while ((numNativeFrames > 0) && (currentNativeFrame->StackOffset <= sp))
{
if (currentNativeFrame->StackOffset != sp)
@@ -12089,7 +12117,7 @@ public:
currentNativeFrame++;
numNativeFrames--;
}
-#endif // _TARGET_WIN64_
+#endif // DEBUG_STACK_CONTEXT
// Print the stack pointer.
out.WriteColumn(0, sp);
@@ -12138,14 +12166,14 @@ public:
} while (pStackWalk->Next() == S_OK);
-#ifdef _TARGET_WIN64_
+#ifdef DEBUG_STACK_CONTEXT
while (numNativeFrames > 0)
{
PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines);
currentNativeFrame++;
numNativeFrames--;
}
-#endif // _TARGET_WIN64_
+#endif // DEBUG_STACK_CONTEXT
}
static HRESULT PrintManagedFrameContext(IXCLRDataStackWalk *pStackWalk)
@@ -12282,15 +12310,43 @@ public:
PrintThread(osid, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
}
-private:
+ static void PrintAllThreads(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals)
+ {
+ HRESULT Status;
+
+ DacpThreadStoreData ThreadStore;
+ if ((Status = ThreadStore.Request(g_sos)) != S_OK)
+ {
+ ExtErr("Failed to request ThreadStore\n");
+ return;
+ }
+
+ DacpThreadData Thread;
+ CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
+ while (CurThread != 0)
+ {
+ if (IsInterrupt())
+ break;
+
+ if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
+ {
+ ExtErr("Failed to request thread at %p\n", CurThread);
+ return;
+ }
+ ExtOut("OS Thread Id: 0x%x\n", Thread.osThreadId);
+ PrintThread(Thread.osThreadId, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
+ CurThread = Thread.nextThread;
+ }
+ }
+
+private:
static HRESULT CreateStackWalk(ULONG osID, IXCLRDataStackWalk **ppStackwalk)
{
HRESULT hr = S_OK;
ToRelease<IXCLRDataTask> pTask;
- if ((hr = g_ExtSystem->GetCurrentThreadSystemId(&osID)) != S_OK ||
- (hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK)
+ if ((hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK)
{
ExtOut("Unable to walk the managed stack. The current thread is likely not a \n");
ExtOut("managed thread. You can run !threads to get a list of managed threads in\n");
@@ -12693,6 +12749,7 @@ DECLARE_API(ClrStack)
BOOL dml = FALSE;
BOOL bFull = FALSE;
BOOL bDisplayRegVals = FALSE;
+ BOOL bAllThreads = FALSE;
DWORD frameToDumpVariablesFor = -1;
StringHolder cvariableName;
ArrayHolder<WCHAR> wvariableName = new NOTHROW WCHAR[mdNameLen];
@@ -12708,6 +12765,7 @@ DECLARE_API(ClrStack)
CMDOption option[] =
{ // name, vptr, type, hasValue
{"-a", &bAll, COBOOL, FALSE},
+ {"-all", &bAllThreads, COBOOL, FALSE},
{"-p", &bParams, COBOOL, FALSE},
{"-l", &bLocals, COBOOL, FALSE},
{"-n", &bSuppressLines, COBOOL, FALSE},
@@ -12765,7 +12823,12 @@ DECLARE_API(ClrStack)
return ClrStackImplWithICorDebug::ClrStackFromPublicInterface(bParams, bLocals, FALSE, wvariableName, frameToDumpVariablesFor);
}
- ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
+ if (bAllThreads) {
+ ClrStackImpl::PrintAllThreads(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
+ }
+ else {
+ ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
+ }
return S_OK;
}
@@ -13297,7 +13360,7 @@ HRESULT CALLBACK ImplementEFNStackTrace(
ULONG numFrames = 0;
BOOL bInNative = TRUE;
- Status = GetContextStackTrace(&numFrames);
+ Status = GetContextStackTrace(ThreadId, &numFrames);
if (FAILED(Status))
{
goto Exit;
@@ -13321,7 +13384,7 @@ HRESULT CALLBACK ImplementEFNStackTrace(
{
// below we cast the i-th AMD64_CONTEXT to CROSS_PLATFORM_CONTEXT
AppendContext (pTransitionContexts, *puiTransitionContextCount,
- &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
+ &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_FrameContexts[i])));
}
else
{
@@ -13354,7 +13417,7 @@ HRESULT CALLBACK ImplementEFNStackTrace(
if (puiTransitionContextCount)
{
AppendContext (pTransitionContexts, *puiTransitionContextCount,
- &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
+ &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_FrameContexts[i])));
}
else
{
@@ -14597,3 +14660,86 @@ DECLARE_API(Help)
return S_OK;
}
+
+#if defined(FEATURE_PAL) && defined(_TARGET_AMD64_)
+
+static BOOL
+ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size)
+{
+ ULONG fetched;
+ HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(address), buffer, size, &fetched);
+ return SUCCEEDED(hr);
+}
+
+static BOOL
+GetStackFrame(CONTEXT* context, ULONG numNativeFrames)
+{
+ KNONVOLATILE_CONTEXT_POINTERS contextPointers;
+ memset(&contextPointers, 0, sizeof(contextPointers));
+
+ ULONG64 baseAddress;
+ HRESULT hr = g_ExtSymbols->GetModuleByOffset(context->Rip, 0, NULL, &baseAddress);
+ if (FAILED(hr))
+ {
+ PDEBUG_STACK_FRAME frame = &g_Frames[0];
+ for (int i = 0; i < numNativeFrames; i++, frame++) {
+ if (frame->InstructionOffset == context->Rip)
+ {
+ if ((i + 1) >= numNativeFrames) {
+ return FALSE;
+ }
+ memcpy(context, &(g_FrameContexts[i + 1]), sizeof(*context));
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+ if (!PAL_VirtualUnwindOutOfProc(context, &contextPointers, baseAddress, ReadMemoryAdapter))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL
+UnwindStackFrames(ULONG32 osThreadId)
+{
+ ULONG numNativeFrames = 0;
+ HRESULT hr = GetContextStackTrace(osThreadId, &numNativeFrames);
+ if (FAILED(hr))
+ {
+ return FALSE;
+ }
+ CONTEXT context;
+ memset(&context, 0, sizeof(context));
+ context.ContextFlags = CONTEXT_FULL;
+
+ hr = g_ExtSystem->GetThreadContextById(osThreadId, CONTEXT_FULL, sizeof(context), (PBYTE)&context);
+ if (FAILED(hr))
+ {
+ return FALSE;
+ }
+ TableOutput out(3, POINTERSIZE_HEX, AlignRight);
+ out.WriteRow("RSP", "RIP", "Call Site");
+
+ DEBUG_STACK_FRAME nativeFrame;
+ memset(&nativeFrame, 0, sizeof(nativeFrame));
+
+ do
+ {
+ if (context.Rip == 0)
+ {
+ break;
+ }
+ nativeFrame.InstructionOffset = context.Rip;
+ nativeFrame.ReturnOffset = context.Rip;
+ nativeFrame.FrameOffset = context.Rbp;
+ nativeFrame.StackOffset = context.Rsp;
+ ClrStackImpl::PrintNativeStackFrame(out, &nativeFrame, FALSE);
+
+ } while (GetStackFrame(&context, numNativeFrames));
+
+ return TRUE;
+}
+
+#endif // FEATURE_PAL && _TARGET_AMD64_
diff --git a/src/ToolBox/SOS/Strike/util.cpp b/src/ToolBox/SOS/Strike/util.cpp
index 3cdebcf0f4..c5dfef2399 100644
--- a/src/ToolBox/SOS/Strike/util.cpp
+++ b/src/ToolBox/SOS/Strike/util.cpp
@@ -88,8 +88,8 @@ ICorDebugProcess *g_pCorDebugProcess = NULL;
#endif // IfFailGo
// Max number of reverted rejit versions that !dumpmd and !ip2md will print
-const UINT kcMaxRevertedRejitData = 10;
-
+const UINT kcMaxRevertedRejitData = 10;
+const UINT kcMaxTieredVersions = 10;
#ifndef FEATURE_PAL
// ensure we always allocate on the process heap
@@ -3252,10 +3252,53 @@ const char *EHTypeName(EHClauseType et)
return "UNKNOWN";
}
-void DumpRejitData(DacpReJitData * pReJitData)
+void DumpTieredNativeCodeAddressInfo(struct DacpTieredVersionData * pTieredVersionData, const UINT cTieredVersionData)
+{
+ ExtOut("Code Version History:\n");
+
+ for(int i = cTieredVersionData - 1; i >= 0; --i)
+ {
+ const char *descriptor = NULL;
+ switch(pTieredVersionData[i].TieredInfo)
+ {
+ case DacpTieredVersionData::TIERED_UNKNOWN:
+ default:
+ _ASSERTE(!"Update SOS to understand the new tier");
+ descriptor = "Unknown Tier";
+ break;
+ case DacpTieredVersionData::NON_TIERED:
+ descriptor = "Non-Tiered";
+ break;
+ case DacpTieredVersionData::TIERED_0:
+ descriptor = "Tier 0";
+ break;
+ case DacpTieredVersionData::TIERED_1:
+ descriptor = "Tier 1";
+ break;
+ }
+
+ DMLOut(" CodeAddr: %s (%s)\n", DMLIP(pTieredVersionData[i].NativeCodeAddr), descriptor);
+ ExtOut(" NativeCodeVersion: %p\n", SOS_PTR(pTieredVersionData[i].NativeCodeVersionNodePtr));
+ }
+}
+
+void DumpRejitData(CLRDATA_ADDRESS pMethodDesc, DacpReJitData * pReJitData)
{
ExtOut(" ReJITID %p: ", SOS_PTR(pReJitData->rejitID));
- DMLOut("CodeAddr = %s", DMLIP(pReJitData->NativeCodeAddr));
+
+ struct DacpTieredVersionData codeAddrs[kcMaxTieredVersions];
+ int cCodeAddrs;
+
+ ReleaseHolder<ISOSDacInterface5> sos5;
+ if (SUCCEEDED(g_sos->QueryInterface(__uuidof(ISOSDacInterface5), &sos5)) &&
+ SUCCEEDED(sos5->GetTieredVersions(pMethodDesc,
+ (int)pReJitData->rejitID,
+ codeAddrs,
+ kcMaxTieredVersions,
+ &cCodeAddrs)))
+ {
+ DumpTieredNativeCodeAddressInfo(codeAddrs, cCodeAddrs);
+ }
LPCSTR szFlags;
switch (pReJitData->flags)
@@ -3277,6 +3320,7 @@ void DumpRejitData(DacpReJitData * pReJitData)
szFlags = " (reverted)";
break;
}
+
ExtOut("%s\n", szFlags);
}
@@ -3314,18 +3358,18 @@ void DumpAllRejitDataIfNecessary(DacpMethodDescData * pMethodDescData, DacpReJit
ExtOut("ReJITed versions:\n");
// Dump CURRENT rejit info
- DumpRejitData(&pMethodDescData->rejitDataCurrent);
+ DumpRejitData(pMethodDescData->MethodDescPtr, &pMethodDescData->rejitDataCurrent);
// Dump reverted rejit infos
for (ULONG i=0; i < cRevertedRejitData; i++)
{
- DumpRejitData(&pRevertedRejitData[i]);
+ DumpRejitData(pMethodDescData->MethodDescPtr, &pRevertedRejitData[i]);
}
// For !ip2md, ensure we dump the rejit version corresponding to the specified IP
// (if not already dumped)
if (ShouldDumpRejitDataRequested(pMethodDescData, pRevertedRejitData, cRevertedRejitData))
- DumpRejitData(&pMethodDescData->rejitDataRequested);
+ DumpRejitData(pMethodDescData->MethodDescPtr, &pMethodDescData->rejitDataRequested);
// If we maxed out the reverted versions we dumped, let user know there may be more
if (cRevertedRejitData == kcMaxRevertedRejitData)
@@ -3344,20 +3388,35 @@ void DumpMDInfoFromMethodDescData(DacpMethodDescData * pMethodDescData, DacpReJi
if (!fStackTraceFormat)
{
- ExtOut("Method Name: %S\n", wszNameBuffer);
+ ExtOut("Method Name: %S\n", wszNameBuffer);
DacpMethodTableData mtdata;
if (SUCCEEDED(mtdata.Request(g_sos, pMethodDescData->MethodTablePtr)))
{
- DMLOut("Class: %s\n", DMLClass(mtdata.Class));
+ DMLOut("Class: %s\n", DMLClass(mtdata.Class));
}
- DMLOut("MethodTable: %s\n", DMLMethodTable(pMethodDescData->MethodTablePtr));
- ExtOut("mdToken: %p\n", SOS_PTR(pMethodDescData->MDToken));
- DMLOut("Module: %s\n", DMLModule(pMethodDescData->ModulePtr));
- ExtOut("IsJitted: %s\n", pMethodDescData->bHasNativeCode ? "yes" : "no");
- DMLOut("CodeAddr: %s\n", DMLIP(pMethodDescData->NativeCodeAddr));
+ DMLOut("MethodTable: %s\n", DMLMethodTable(pMethodDescData->MethodTablePtr));
+ ExtOut("mdToken: %p\n", SOS_PTR(pMethodDescData->MDToken));
+ DMLOut("Module: %s\n", DMLModule(pMethodDescData->ModulePtr));
+ ExtOut("IsJitted: %s\n", pMethodDescData->bHasNativeCode ? "yes" : "no");
+
+ DMLOut("Current CodeAddr: %s\n", DMLIP(pMethodDescData->NativeCodeAddr));
+
+ struct DacpTieredVersionData codeAddrs[kcMaxTieredVersions];
+ int cCodeAddrs;
+ ReleaseHolder<ISOSDacInterface5> sos5;
+ if (SUCCEEDED(g_sos->QueryInterface(__uuidof(ISOSDacInterface5), &sos5)) &&
+ SUCCEEDED(sos5->GetTieredVersions(pMethodDescData->MethodDescPtr,
+ (int)pMethodDescData->rejitDataCurrent.rejitID,
+ codeAddrs,
+ kcMaxTieredVersions,
+ &cCodeAddrs)))
+ {
+ DumpTieredNativeCodeAddressInfo(codeAddrs, cCodeAddrs);
+ }
+
DacpMethodDescTransparencyData transparency;
if (SUCCEEDED(transparency.Request(g_sos, pMethodDescData->MethodDescPtr)))
{
diff --git a/src/ToolBox/SOS/Strike/util.h b/src/ToolBox/SOS/Strike/util.h
index 6d0e79622c..bbf97b94e7 100644
--- a/src/ToolBox/SOS/Strike/util.h
+++ b/src/ToolBox/SOS/Strike/util.h
@@ -2519,8 +2519,8 @@ typedef struct{
/// ARM Context
#define ARM_MAX_BREAKPOINTS_CONST 8
-#define ARM_MAX_WATCHPOINTS_CONST 4
-typedef struct {
+#define ARM_MAX_WATCHPOINTS_CONST 1
+typedef DECLSPEC_ALIGN(8) struct {
DWORD ContextFlags;
@@ -2544,6 +2544,7 @@ typedef struct {
DWORD Cpsr;
DWORD Fpscr;
+ DWORD Padding;
union {
M128A_XPLAT Q[16];
ULONGLONG D[32];
@@ -2555,6 +2556,8 @@ typedef struct {
DWORD Wvr[ARM_MAX_WATCHPOINTS_CONST];
DWORD Wcr[ARM_MAX_WATCHPOINTS_CONST];
+ DWORD Padding2[2];
+
} ARM_CONTEXT;
// On ARM this mask is or'ed with the address of code to get an instruction pointer
diff --git a/src/ToolBox/superpmi/mcs/CMakeLists.txt b/src/ToolBox/superpmi/mcs/CMakeLists.txt
index 6ad1ff35ed..ebacd0761c 100644
--- a/src/ToolBox/superpmi/mcs/CMakeLists.txt
+++ b/src/ToolBox/superpmi/mcs/CMakeLists.txt
@@ -24,7 +24,6 @@ set(MCS_SOURCES
verbinteg.cpp
verbmerge.cpp
verbremovedup.cpp
- verbsmarty.cpp
verbstat.cpp
verbstrip.cpp
verbtoc.cpp
diff --git a/src/ToolBox/superpmi/mcs/commandline.cpp b/src/ToolBox/superpmi/mcs/commandline.cpp
index cc2244d870..161122815a 100644
--- a/src/ToolBox/superpmi/mcs/commandline.cpp
+++ b/src/ToolBox/superpmi/mcs/commandline.cpp
@@ -107,10 +107,6 @@ void CommandLine::DumpHelp(const char* program)
printf(" e.g. -removeDup -thin a.mc b.mc\n");
printf(" e.g. -removeDup -legacy -thin a.mc b.mc\n");
printf("\n");
- printf(" -smarty range inputfile outputfile\n");
- printf(" Write smarty Test IDs of the range from inputfile to outputfile.\n");
- printf(" e.g. -smarty 2 a.mc b.mc\n");
- printf("\n");
printf(" -stat {optional range} inputfile outputfile\n");
printf(" Report various statistics per method context.\n");
printf(" inputfile is read and statistics are written into outputfile\n");
@@ -344,14 +340,6 @@ bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o)
{
o->legacyCompare = true;
}
- else if ((_strnicmp(&argv[i][1], "smarty", argLen) == 0))
- {
- tempLen = strlen(argv[i]);
- o->actionSmarty = true;
- foundVerb = true;
- if (i + 1 < argc) // Peek to see if we have an mcl file or an integer next
- goto processMCL;
- }
else if ((_strnicmp(&argv[i][1], "verbosity", argLen) == 0))
{
if (++i >= argc)
@@ -564,16 +552,6 @@ bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o)
}
return true;
}
- if (o->actionSmarty)
- {
- if ((!foundFile1) || (!foundFile2))
- {
- LogError("CommandLine::Parse() '-smarty' needs one input file and one output file.");
- DumpHelp(argv[0]);
- return false;
- }
- return true;
- }
DumpHelp(argv[0]);
return false;
diff --git a/src/ToolBox/superpmi/mcs/commandline.h b/src/ToolBox/superpmi/mcs/commandline.h
index 442f2a4098..4e594934d2 100644
--- a/src/ToolBox/superpmi/mcs/commandline.h
+++ b/src/ToolBox/superpmi/mcs/commandline.h
@@ -27,7 +27,6 @@ public:
, actionInteg(false)
, actionMerge(false)
, actionRemoveDup(false)
- , actionSmarty(false)
, actionStat(false)
, actionStrip(false)
, actionTOC(false)
@@ -53,7 +52,6 @@ public:
bool actionInteg;
bool actionMerge;
bool actionRemoveDup;
- bool actionSmarty;
bool actionStat;
bool actionStrip;
bool actionTOC;
diff --git a/src/ToolBox/superpmi/mcs/mcs.cpp b/src/ToolBox/superpmi/mcs/mcs.cpp
index 7026ff55a3..4ed72cac27 100644
--- a/src/ToolBox/superpmi/mcs/mcs.cpp
+++ b/src/ToolBox/superpmi/mcs/mcs.cpp
@@ -19,7 +19,6 @@
#include "verbconcat.h"
#include "verbmerge.h"
#include "verbstrip.h"
-#include "verbsmarty.h"
#include "logging.h"
int __cdecl main(int argc, char* argv[])
@@ -98,10 +97,6 @@ int __cdecl main(int argc, char* argv[])
{
exitCode = verbTOC::DoWork(o.nameOfFile1);
}
- if (o.actionSmarty)
- {
- exitCode = verbSmarty::DoWork(o.nameOfFile1, o.nameOfFile2, o.indexCount, o.indexes);
- }
Logger::Shutdown();
return exitCode;
diff --git a/src/ToolBox/superpmi/mcs/verbsmarty.cpp b/src/ToolBox/superpmi/mcs/verbsmarty.cpp
deleted file mode 100644
index a9425bd11c..0000000000
--- a/src/ToolBox/superpmi/mcs/verbsmarty.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-//
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-
-#include "standardpch.h"
-#include "verbsmarty.h"
-#include "simpletimer.h"
-#include "methodcontext.h"
-#include "methodcontextiterator.h"
-
-//
-// Constructs a new verbSmarty.
-//
-// Arguments:
-// hFile - A handle to the output file that we are writing the Smarty Test IDs
-//
-// Assumptions:
-// hFile refers to an open and writeable file handle.
-//
-verbSmarty::verbSmarty(HANDLE hFile)
-{
- m_hFile = hFile;
-}
-
-//
-// Dumps the Smarty TestID to file
-//
-// Arguments:
-// testID - Smarty Test ID
-//
-void verbSmarty::DumpTestInfo(int testID)
-{
-#define bufflen 4096
- DWORD bytesWritten;
-
- char buff[bufflen];
- int buff_offset = 0;
- ZeroMemory(buff, bufflen * sizeof(char));
-
- buff_offset += sprintf_s(&buff[buff_offset], bufflen - buff_offset, "%i\r\n", testID);
- WriteFile(m_hFile, buff, buff_offset * sizeof(char), &bytesWritten, nullptr);
-}
-
-int verbSmarty::DoWork(const char* nameOfInput, const char* nameOfOutput, int indexCount, const int* indexes)
-{
- LogVerbose("Reading from '%s' reading Smarty ID for the Mc Indexes and writing into '%s'", nameOfInput,
- nameOfOutput);
-
- MethodContextIterator mci(indexCount, indexes);
- if (!mci.Initialize(nameOfInput))
- return -1;
-
- int savedCount = 0;
-
- HANDLE hFileOut = CreateFileA(nameOfOutput, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
- if (hFileOut == INVALID_HANDLE_VALUE)
- {
- LogError("Failed to open input 1 '%s'. GetLastError()=%u", nameOfOutput, GetLastError());
- return -1;
- }
-
- verbSmarty* verbList = new verbSmarty(hFileOut);
-
- // TODO-Cleanup: look to use toc for this
- while (mci.MoveNext())
- {
- MethodContext* mc = mci.Current();
-
- int testID = mc->repGetTestID();
- if (testID != -1)
- {
- // write to the file
- verbList->DumpTestInfo(testID);
- }
- else
- {
- LogError("Smarty ID not found for '%s'", mc->cr->repProcessName());
- }
- }
-
- delete verbList;
-
- if (!CloseHandle(hFileOut))
- {
- LogError("2nd CloseHandle failed. GetLastError()=%u", GetLastError());
- return -1;
- }
-
- LogInfo("Loaded %d, Saved %d", mci.MethodContextNumber(), savedCount);
-
- if (!mci.Destroy())
- return -1;
-
- return 0;
-}
diff --git a/src/ToolBox/superpmi/mcs/verbsmarty.h b/src/ToolBox/superpmi/mcs/verbsmarty.h
deleted file mode 100644
index c595ecbf4e..0000000000
--- a/src/ToolBox/superpmi/mcs/verbsmarty.h
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-
-//----------------------------------------------------------
-// verbSmarty.h - verb that outputs Smarty test ID for mc
-//----------------------------------------------------------
-#ifndef _verbSmarty
-#define _verbSmarty
-
-class verbSmarty
-{
-public:
- verbSmarty(HANDLE hFile);
- void DumpTestInfo(int testID);
- static int DoWork(const char* nameOfInput, const char* nameofOutput, int indexCount, const int* indexes);
-
-private:
- HANDLE m_hFile;
-};
-#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/coreclrcommoncallbacks.h b/src/ToolBox/superpmi/superpmi-shared/coreclrcommoncallbacks.h
index ca542fb234..254981cada 100644
--- a/src/ToolBox/superpmi/superpmi-shared/coreclrcommoncallbacks.h
+++ b/src/ToolBox/superpmi/superpmi-shared/coreclrcommoncallbacks.h
@@ -8,13 +8,13 @@
#include "runtimedetails.h"
-IExecutionEngine* IEE_t();
-HRESULT GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength);
-LPVOID EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes);
-BOOL EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem);
-void* GetCLRFunction(LPCSTR functionName);
+IExecutionEngine* STDMETHODCALLTYPE IEE_t();
+HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer, DWORD* pdwlength);
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes);
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem);
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName);
-typedef LPVOID (*pfnEEHeapAllocInProcessHeap)(DWORD dwFlags, SIZE_T dwBytes);
-typedef BOOL (*pfnEEHeapFreeInProcessHeap)(DWORD dwFlags, LPVOID lpMem);
+typedef LPVOID (STDMETHODCALLTYPE *pfnEEHeapAllocInProcessHeap)(DWORD dwFlags, SIZE_T dwBytes);
+typedef BOOL (STDMETHODCALLTYPE *pfnEEHeapFreeInProcessHeap)(DWORD dwFlags, LPVOID lpMem);
#endif
diff --git a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
index 00b5c1b965..4ce775d177 100644
--- a/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
+++ b/src/ToolBox/superpmi/superpmi-shared/icorjitinfoimpl.h
@@ -109,7 +109,8 @@ CORINFO_MODULE_HANDLE getMethodModule(CORINFO_METHOD_HANDLE method);
// vtable of it's owning class or interface.
void getMethodVTableOffset(CORINFO_METHOD_HANDLE method, /* IN */
unsigned* offsetOfIndirection, /* OUT */
- unsigned* offsetAfterIndirection /* OUT */
+ unsigned* offsetAfterIndirection,/* OUT */
+ bool* isRelative /* OUT */
);
// Find the virtual method in implementingClass that overrides virtualMethod.
@@ -669,6 +670,14 @@ const char* getMethodName(CORINFO_METHOD_HANDLE ftn, /* IN */
const char** moduleName /* OUT */
);
+// Return method name as in metadata, or nullptr if there is none,
+// and optionally return the class name as in metadata.
+// Suitable for non-debugging use.
+const char* getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char** className, /* OUT */
+ const char** namespaceName /* OUT */
+ );
+
// this function is for debugging only. It returns a value that
// is will always be the same for a given method. It is used
// to implement the 'jitRange' functionality
diff --git a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
index 23cdf70922..898641c790 100644
--- a/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
+++ b/src/ToolBox/superpmi/superpmi-shared/lwmlist.h
@@ -100,9 +100,10 @@ LWM(GetMethodDefFromMethod, DWORDLONG, DWORD)
LWM(GetMethodHash, DWORDLONG, DWORD)
LWM(GetMethodInfo, DWORDLONG, Agnostic_GetMethodInfo)
LWM(GetMethodName, DLD, DD)
+LWM(GetMethodNameFromMetadata, DLDD, DDD)
LWM(GetMethodSig, DLDL, Agnostic_CORINFO_SIG_INFO)
LWM(GetMethodSync, DWORDLONG, DLDL)
-LWM(GetMethodVTableOffset, DWORDLONG, DD)
+LWM(GetMethodVTableOffset, DWORDLONG, DDD)
LWM(GetNewArrHelper, DWORDLONG, DWORD)
LWM(GetNewHelper, Agnostic_GetNewHelper, DWORD)
LWM(GetParentType, DWORDLONG, DWORDLONG)
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
index f522265e11..4968461996 100644
--- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
@@ -481,7 +481,7 @@ bool MethodContext::Equal(MethodContext* other)
// `ICJI::compileMethod`).
//
// This method is intended to be called as part of initializing a method
-// during collection, similar to `methodContext::recEnvironment`.
+// during collection.
void MethodContext::recGlobalContext(const MethodContext& other)
{
Assert(GetIntConfigValue == nullptr);
@@ -498,128 +498,8 @@ void MethodContext::recGlobalContext(const MethodContext& other)
}
}
-void MethodContext::recEnvironment()
-{
- if (Environment == nullptr)
- Environment = new DenseLightWeightMap<Agnostic_Environment>();
-
- char* l_EnvStr;
- char* l_val;
-
-#ifdef FEATURE_PAL
- l_EnvStr = GetEnvironmentStringsA();
-#else // !FEATURE_PAL
- l_EnvStr = GetEnvironmentStrings();
-#endif // !FEATURE_PAL
-
- l_val = l_EnvStr;
-
- char* l_str = l_EnvStr;
-
- int count = 0;
- while (true)
- {
- if (*l_str == 0)
- break;
- while (*l_str != 0)
- l_str++;
- l_str++;
- count++;
- }
-
- for (int i = 0; i < count; i++)
- {
- if ((_strnicmp(l_EnvStr, "complus_", 8) == 0) || (_strnicmp(l_EnvStr, "dbflag", 6) == 0) ||
- (_strnicmp(l_EnvStr, "BVT_TEST_ID", 11) == 0))
- {
- char* val = l_EnvStr;
- while (*val != '=')
- val++;
- *val++ = 0;
- int nameind = Environment->AddBuffer((unsigned char*)l_EnvStr, (int)strlen(l_EnvStr) + 1);
- int valind = Environment->AddBuffer((unsigned char*)val, (int)strlen(val) + 1);
- Agnostic_Environment value;
- value.name_index = nameind;
- value.val_index = valind;
- DWORD key = (DWORD)Environment->GetCount();
- Environment->Append(value);
- DEBUG_REC(dmpEnvironment(key, value));
- l_EnvStr = val;
- }
- while (*l_EnvStr != '\0')
- l_EnvStr++;
- l_EnvStr++;
- }
- FreeEnvironmentStringsA(l_val);
-}
void MethodContext::dmpEnvironment(DWORD key, const Agnostic_Environment& value)
{
- printf("Environment key %u, value '%s' '%s'", key, (LPCSTR)Environment->GetBuffer(value.name_index),
- (LPCSTR)Environment->GetBuffer(value.val_index));
- Environment->Unlock();
-}
-void MethodContext::repEnvironmentSet()
-{
- if (Environment == nullptr)
- return;
- Agnostic_Environment val;
- for (unsigned int i = 0; i < Environment->GetCount(); i++)
- {
- val = Environment->Get((DWORD)i);
- DEBUG_REP(dmpEnvironment(i, val));
-
- SetEnvironmentVariableA((LPCSTR)Environment->GetBuffer(val.name_index),
- (LPCSTR)Environment->GetBuffer(val.val_index));
- }
-}
-
-int MethodContext::repGetTestID()
-{
- // CLR Test asset only - we capture the testID via smarty-environnent (BVT_TEST_ID) during record time
- // This procedure returns the test id if found and -1 otherwise
-
- if (Environment == nullptr)
- return -1;
- Agnostic_Environment val;
- LPCSTR key;
- int value = -1;
- for (unsigned int i = 0; i < Environment->GetCount(); i++)
- {
- val = Environment->Get((DWORD)i);
- key = (LPCSTR)Environment->GetBuffer(val.name_index);
-
- if (_strnicmp(key, "BVT_TEST_ID", 11) == 0)
- {
- value = atoi((LPCSTR)Environment->GetBuffer(val.val_index));
- break;
- }
- }
-
- if (value == -1)
- {
- LogError("Couldn't find Smarty test ID");
- }
-
- return value;
-}
-
-void MethodContext::repEnvironmentUnset()
-{
- if (Environment == nullptr)
- return;
- Agnostic_Environment val;
- for (unsigned int i = 0; i < Environment->GetCount(); i++)
- {
- val = Environment->Get((DWORD)i);
- SetEnvironmentVariableA((LPCSTR)Environment->GetBuffer(val.name_index), nullptr);
- }
-}
-int MethodContext::repEnvironmentGetCount()
-{
- int result = 0;
- if (Environment != nullptr)
- result = Environment->GetCount();
- return result;
}
void MethodContext::dumpToConsole(int mcNumber)
@@ -1121,6 +1001,7 @@ void MethodContext::dmpGetMethodName(DLD key, DD value)
moduleName);
GetMethodName->Unlock();
}
+
const char* MethodContext::repGetMethodName(CORINFO_METHOD_HANDLE ftn, const char** moduleName)
{
const char* result = "hackishMethodName";
@@ -1148,6 +1029,85 @@ const char* MethodContext::repGetMethodName(CORINFO_METHOD_HANDLE ftn, const cha
return result;
}
+
+void MethodContext::recGetMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn,
+ char* methodName, const char** className, const char **namespaceName)
+{
+ if (GetMethodNameFromMetadata == nullptr)
+ GetMethodNameFromMetadata = new LightWeightMap<DLDD, DDD>();
+ DDD value;
+ DLDD key;
+ key.A = (DWORDLONG)ftn;
+ key.B = (className != nullptr);
+ key.C = (namespaceName != nullptr);
+
+ if (methodName != nullptr)
+ value.A = GetMethodNameFromMetadata->AddBuffer((unsigned char*)methodName, (DWORD)strlen(methodName) + 1);
+ else
+ value.A = (DWORD)-1;
+
+ if (className != nullptr)
+ value.B = GetMethodNameFromMetadata->AddBuffer((unsigned char*)*className, (DWORD)strlen(*className) + 1);
+ else
+ value.B = (DWORD)-1;
+
+ if (namespaceName != nullptr)
+ value.C = GetMethodNameFromMetadata->AddBuffer((unsigned char*)*namespaceName, (DWORD)strlen(*namespaceName) + 1);
+ else
+ value.C = (DWORD)-1;
+
+ GetMethodNameFromMetadata->Add(key, value);
+ DEBUG_REC(dmpGetMethodNameFromMetadata(key, value));
+}
+
+void MethodContext::dmpGetMethodNameFromMetadata(DLDD key, DDD value)
+{
+ unsigned char* methodName = (unsigned char*)GetMethodName->GetBuffer(value.A);
+ unsigned char* className = (unsigned char*)GetMethodName->GetBuffer(value.B);
+ unsigned char* namespaceName = (unsigned char*)GetMethodName->GetBuffer(value.C);
+ printf("GetMethodNameFromMetadata key - ftn-%016llX classNonNull-%u namespaceNonNull-%u, value meth-'%s', class-'%s', namespace-'%s'",
+ key.A, key.B, key.C, methodName, className, namespaceName);
+ GetMethodNameFromMetadata->Unlock();
+}
+
+const char* MethodContext::repGetMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, const char** moduleName, const char** namespaceName)
+{
+ const char* result = nullptr;
+ DDD value;
+ DLDD key;
+ key.A = (DWORDLONG)ftn;
+ key.B = (moduleName != nullptr);
+ key.C = (namespaceName != nullptr);
+
+ int itemIndex = -1;
+ if (GetMethodNameFromMetadata != nullptr)
+ itemIndex = GetMethodNameFromMetadata->GetIndex(key);
+ if (itemIndex < 0)
+ {
+ if (moduleName != nullptr)
+ {
+ *moduleName = nullptr;
+ }
+ }
+ else
+ {
+ value = GetMethodNameFromMetadata->Get(key);
+ result = (const char*)GetMethodNameFromMetadata->GetBuffer(value.A);
+
+ if (moduleName != nullptr)
+ {
+ *moduleName = (const char*)GetMethodNameFromMetadata->GetBuffer(value.B);
+ }
+
+ if (namespaceName != nullptr)
+ {
+ *namespaceName = (const char*)GetMethodNameFromMetadata->GetBuffer(value.C);
+ }
+ }
+ DEBUG_REP(dmpGetMethodNameFromMetadata(key, value));
+ return result;
+}
+
void MethodContext::recGetJitFlags(CORJIT_FLAGS* jitFlags, DWORD sizeInBytes, DWORD result)
{
if (GetJitFlags == nullptr)
@@ -1504,6 +1464,7 @@ void MethodContext::repGetCallInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken,
pResult->stubLookup.runtimeLookup.testForNull = value.stubLookup.runtimeLookup.testForNull != 0;
pResult->stubLookup.runtimeLookup.testForFixup = value.stubLookup.runtimeLookup.testForFixup != 0;
pResult->stubLookup.runtimeLookup.indirectFirstOffset = value.stubLookup.runtimeLookup.indirectFirstOffset != 0;
+ pResult->stubLookup.runtimeLookup.indirectSecondOffset = value.stubLookup.runtimeLookup.indirectSecondOffset != 0;
for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
pResult->stubLookup.runtimeLookup.offsets[i] = (SIZE_T)value.stubLookup.runtimeLookup.offsets[i];
}
@@ -2970,26 +2931,29 @@ void MethodContext::repGetEHinfo(CORINFO_METHOD_HANDLE ftn, unsigned EHnumber, C
void MethodContext::recGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
unsigned* offsetOfIndirection,
- unsigned* offsetAfterIndirection)
+ unsigned* offsetAfterIndirection,
+ bool* isRelative)
{
if (GetMethodVTableOffset == nullptr)
- GetMethodVTableOffset = new LightWeightMap<DWORDLONG, DD>();
+ GetMethodVTableOffset = new LightWeightMap<DWORDLONG, DDD>();
- DD value;
+ DDD value;
value.A = (DWORD)*offsetOfIndirection;
value.B = (DWORD)*offsetAfterIndirection;
+ value.C = *isRelative ? 1 : 0;
GetMethodVTableOffset->Add((DWORDLONG)method, value);
DEBUG_REC(dmpGetMethodVTableOffset((DWORDLONG)method, value));
}
-void MethodContext::dmpGetMethodVTableOffset(DWORDLONG key, DD value)
+void MethodContext::dmpGetMethodVTableOffset(DWORDLONG key, DDD value)
{
- printf("GetMethodVTableOffset key ftn-%016llX, value offi-%u, offa-%u", key, value.A, value.B);
+ printf("GetMethodVTableOffset key ftn-%016llX, value offi-%u, offa-%u. offr-%d", key, value.A, value.B, value.C);
}
void MethodContext::repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
unsigned* offsetOfIndirection,
- unsigned* offsetAfterIndirection)
+ unsigned* offsetAfterIndirection,
+ bool* isRelative)
{
- DD value;
+ DDD value;
AssertCodeMsg(GetMethodVTableOffset != nullptr, EXCEPTIONCODE_MC, "Didn't find anything for %016llX",
(DWORDLONG)method);
@@ -2999,6 +2963,7 @@ void MethodContext::repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
*offsetOfIndirection = (unsigned)value.A;
*offsetAfterIndirection = (unsigned)value.B;
+ *isRelative = (value.C != 0);
DEBUG_REP(dmpGetMethodVTableOffset((DWORDLONG)method, value));
}
@@ -5889,15 +5854,6 @@ const wchar_t* MethodContext::repGetStringConfigValue(const wchar_t* name)
return value;
}
-struct EnvironmentVariable
-{
- char* name;
- DWORD val_index;
-};
-int __cdecl compareEnvironmentVariable(const void* arg1, const void* arg2)
-{
- return _stricmp(((EnvironmentVariable*)arg1)->name, ((EnvironmentVariable*)arg2)->name);
-}
int MethodContext::dumpMethodIdentityInfoToBuffer(char* buff, int len)
{
char* obuff = buff;
@@ -5922,46 +5878,6 @@ int MethodContext::dumpMethodIdentityInfoToBuffer(char* buff, int len)
buff += t;
len -= t;
- // Add COMPLUS_* & dbflag environment variables to method Identity
- // except complus_version and complus_defaultversion
- // since they change the compilation behaviour of JIT
- // we also need to sort them to ensure we don't produce a different
- // hash based on the order of these variables
- if (Environment != nullptr)
- {
- Agnostic_Environment val;
- EnvironmentVariable* envValues = new EnvironmentVariable[Environment->GetCount()];
- int envValCount = 0;
-
- for (unsigned int i = 0; i < Environment->GetCount(); i++)
- {
- val = Environment->Get((DWORD)i);
- char* envVariable = (char*)Environment->GetBuffer(val.name_index);
- if ((_strnicmp(envVariable, "complus_", 8) == 0 || _strnicmp(envVariable, "dbflag", 6) == 0) &&
- (_stricmp(envVariable, "complus_version") != 0) &&
- (_stricmp(envVariable, "complus_defaultversion") != 0))
- {
- envValues[envValCount].name = envVariable;
- envValues[envValCount++].val_index = val.val_index;
- }
- }
-
- // Do a quick sort on envValues if needed
- if (envValCount > 1)
- qsort(envValues, envValCount, sizeof(EnvironmentVariable), compareEnvironmentVariable);
-
- // Append these values to the IdentityInfobuffer
- for (int i = 0; i < envValCount; i++)
- {
- t = sprintf_s(buff, len, "%s=%s ", _strlwr(envValues[i].name),
- _strlwr((char*)Environment->GetBuffer(envValues[i].val_index)));
- buff += t;
- len -= t;
- }
-
- delete[] envValues;
- }
-
// Hash the IL Code for this method and append it to the ID info
char ilHash[MD5_HASH_BUFFER_SIZE];
dumpMD5HashToBuffer(info.ILCode, info.ILCodeSize, ilHash, MD5_HASH_BUFFER_SIZE);
@@ -6056,3 +5972,53 @@ OnError:
#endif // !FEATURE_PAL
}
+
+DenseLightWeightMap<MethodContext::Agnostic_Environment>* MethodContext::prevEnviroment = nullptr;
+
+bool MethodContext::wasEnviromentChanged()
+{
+ bool changed = false;
+ if (prevEnviroment == nullptr)
+ {
+ changed = true;
+ }
+ else if (Environment->GetCount() != prevEnviroment->GetCount())
+ {
+ changed = true;
+ }
+ else
+ {
+ for (unsigned int i = 0; i < Environment->GetCount(); i++)
+ {
+ Agnostic_Environment currEnvValue = Environment->Get(i);
+ LPCSTR currKey = (LPCSTR)Environment->GetBuffer(currEnvValue.name_index);
+ LPCSTR currVal = (LPCSTR)Environment->GetBuffer(currEnvValue.val_index);
+
+ Agnostic_Environment prevEnvValue = prevEnviroment->Get(i);
+ LPCSTR prevKey = (LPCSTR)prevEnviroment->GetBuffer(prevEnvValue.name_index);
+ LPCSTR prevVal = (LPCSTR)prevEnviroment->GetBuffer(prevEnvValue.val_index);
+ if (strcmp(currKey, prevKey) != 0 || strcmp(currVal, prevVal) != 0)
+ {
+ changed = true;
+ break;
+ }
+ }
+ }
+ if (changed)
+ {
+ if (prevEnviroment != nullptr)
+ {
+ delete prevEnviroment;
+ }
+ if (Environment != nullptr)
+ {
+ prevEnviroment = new DenseLightWeightMap<Agnostic_Environment>(*Environment);
+ }
+ else
+ {
+ prevEnviroment = nullptr;
+ }
+ return true;
+ }
+ return false;
+}
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
index 4b42fa7be4..7f79b2b9f6 100644
--- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
@@ -89,6 +89,12 @@ public:
DWORDLONG A;
DWORD B;
};
+ struct DLDD
+ {
+ DWORDLONG A;
+ DWORD B;
+ DWORD C;
+ };
struct Agnostic_CORINFO_RESOLVED_TOKENin
{
DWORDLONG tokenContext;
@@ -211,6 +217,7 @@ public:
DWORD testForFixup;
DWORDLONG offsets[CORINFO_MAXINDIRECTIONS];
DWORD indirectFirstOffset;
+ DWORD indirectSecondOffset;
};
struct Agnostic_CORINFO_LOOKUP
{
@@ -236,6 +243,12 @@ public:
DWORD A;
DWORD B;
};
+ struct DDD
+ {
+ DWORD A;
+ DWORD B;
+ DWORD C;
+ };
struct Agnostic_CanTailCall
{
DWORDLONG callerHnd;
@@ -557,12 +570,7 @@ public:
void recGlobalContext(const MethodContext& other);
- void recEnvironment();
void dmpEnvironment(DWORD key, const Agnostic_Environment& value);
- void repEnvironmentSet();
- void repEnvironmentUnset();
- int repEnvironmentGetCount();
- int repGetTestID();
void recCompileMethod(CORINFO_METHOD_INFO* info, unsigned flags);
void dmpCompileMethod(DWORD key, const Agnostic_CompileMethod& value);
@@ -609,6 +617,10 @@ public:
void dmpGetMethodName(DLD key, DD value);
const char* repGetMethodName(CORINFO_METHOD_HANDLE ftn, const char** moduleName);
+ void recGetMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, char* methodname, const char** moduleName, const char** namespaceName);
+ void dmpGetMethodNameFromMetadata(DLDD key, DDD value);
+ const char* repGetMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, const char** className, const char** namespaceName);
+
void recGetJitFlags(CORJIT_FLAGS* jitFlags, DWORD sizeInBytes, DWORD result);
void dmpGetJitFlags(DWORD key, DD value);
DWORD repGetJitFlags(CORJIT_FLAGS* jitFlags, DWORD sizeInBytes);
@@ -843,11 +855,13 @@ public:
void recGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
unsigned* offsetOfIndirection,
- unsigned* offsetAfterIndirection);
- void dmpGetMethodVTableOffset(DWORDLONG key, DD value);
+ unsigned* offsetAfterIndirection,
+ bool* isRelative);
+ void dmpGetMethodVTableOffset(DWORDLONG key, DDD value);
void repGetMethodVTableOffset(CORINFO_METHOD_HANDLE method,
unsigned* offsetOfIndirection,
- unsigned* offsetAfterIndirection);
+ unsigned* offsetAfterIndirection,
+ bool* isRelative);
void recResolveVirtualMethod(CORINFO_METHOD_HANDLE virtMethod,
CORINFO_CLASS_HANDLE implClass,
@@ -1224,6 +1238,9 @@ public:
void dmpGetStringConfigValue(DWORD nameIndex, DWORD result);
const wchar_t* repGetStringConfigValue(const wchar_t* name);
+ bool wasEnviromentChanged();
+ static DenseLightWeightMap<Agnostic_Environment>* prevEnviroment;
+
CompileResult* cr;
CompileResult* originalCR;
int index;
@@ -1235,7 +1252,7 @@ private:
};
// ********************* Please keep this up-to-date to ease adding more ***************
-// Highest packet number: 160
+// Highest packet number: 161
// *************************************************************************************
enum mcPackets
{
@@ -1265,7 +1282,7 @@ enum mcPackets
Packet_EmbedMethodHandle = 19,
Packet_EmbedModuleHandle = 20,
Packet_EmptyStringLiteral = 21,
- Packet_Environment = 136, // Added 4/3/2013
+ Packet_Environment = 136, // Deprecated 7/29/2017
Packet_ErrorList = 22,
Packet_FilterException = 134,
Packet_FindCallSiteSig = 23,
@@ -1330,6 +1347,7 @@ enum mcPackets
Packet_GetMethodHash = 73,
Packet_GetMethodInfo = 74,
Packet_GetMethodName = 75,
+ Packet_GetMethodNameFromMetadata = 161, // Added 9/6/17
Packet_GetMethodSig = 76,
Packet_GetMethodSync = 77,
Packet_GetMethodVTableOffset = 78,
diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp
index 6b40839a63..8d1930fb57 100644
--- a/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp
+++ b/src/ToolBox/superpmi/superpmi-shared/methodcontextreader.cpp
@@ -121,6 +121,10 @@ MethodContextReader::MethodContextReader(
MethodContextReader::~MethodContextReader()
{
+ if (MethodContext::prevEnviroment != nullptr)
+ {
+ delete MethodContext::prevEnviroment;
+ }
if (fileHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(this->fileHandle);
diff --git a/src/ToolBox/superpmi/superpmi-shared/spmirecordhelper.h b/src/ToolBox/superpmi/superpmi-shared/spmirecordhelper.h
index 0030a2bbd5..c3988f8487 100644
--- a/src/ToolBox/superpmi/superpmi-shared/spmirecordhelper.h
+++ b/src/ToolBox/superpmi/superpmi-shared/spmirecordhelper.h
@@ -297,6 +297,7 @@ inline MethodContext::Agnostic_CORINFO_RUNTIME_LOOKUP SpmiRecordsHelper::StoreAg
runtimeLookup.testForNull = (DWORD)pLookup->testForNull;
runtimeLookup.testForFixup = (DWORD)pLookup->testForFixup;
runtimeLookup.indirectFirstOffset = (DWORD)pLookup->indirectFirstOffset;
+ runtimeLookup.indirectSecondOffset = (DWORD)pLookup->indirectSecondOffset;
for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
runtimeLookup.offsets[i] = (DWORDLONG)pLookup->offsets[i];
return runtimeLookup;
@@ -312,6 +313,7 @@ inline CORINFO_RUNTIME_LOOKUP SpmiRecordsHelper::RestoreCORINFO_RUNTIME_LOOKUP(
runtimeLookup.testForNull = lookup.testForNull != 0;
runtimeLookup.testForFixup = lookup.testForFixup != 0;
runtimeLookup.indirectFirstOffset = lookup.indirectFirstOffset != 0;
+ runtimeLookup.indirectSecondOffset = lookup.indirectSecondOffset != 0;
for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
runtimeLookup.offsets[i] = (size_t)lookup.offsets[i];
return CORINFO_RUNTIME_LOOKUP();
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp
index 14a55b5d79..f0bc271311 100644
--- a/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/coreclrcallbacks.cpp
@@ -11,7 +11,7 @@ CoreClrCallbacks* original_CoreClrCallbacks = nullptr;
pfnEEHeapAllocInProcessHeap original_EEHeapAllocInProcessHeap = nullptr;
pfnEEHeapFreeInProcessHeap original_EEHeapFreeInProcessHeap = nullptr;
-IExecutionEngine* IEE_t()
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
{
interceptor_IEE* iee = new interceptor_IEE();
iee->original_IEE = original_CoreClrCallbacks->m_pfnIEE();
@@ -26,21 +26,21 @@ HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer,
}
*/
-LPVOID EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes)
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes)
{
if (original_EEHeapAllocInProcessHeap == nullptr)
__debugbreak();
return original_EEHeapAllocInProcessHeap(dwFlags, dwBytes);
}
-BOOL EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem)
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem)
{
if (original_EEHeapFreeInProcessHeap == nullptr)
__debugbreak();
return original_EEHeapFreeInProcessHeap(dwFlags, lpMem);
}
-void* GetCLRFunction(LPCSTR functionName)
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
{
if (strcmp(functionName, "EEHeapAllocInProcessHeap") == 0)
{
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp
index eaa6b8cfb7..19f96385e4 100644
--- a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitcompiler.cpp
@@ -63,9 +63,6 @@ CorJitResult __stdcall interceptor_ICJC::compileMethod(ICorJitInfo*
our_ICorJitInfo.mc->recGlobalContext(*g_globalContext);
}
- // Record a simple view of the environment
- our_ICorJitInfo.mc->recEnvironment();
-
CorJitResult temp =
original_ICorJitCompiler->compileMethod(&our_ICorJitInfo, info, flags, nativeEntry, nativeSizeOfCode);
diff --git a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
index 1ba0ed2a65..2a477a07e3 100644
--- a/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
+++ b/src/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp
@@ -214,12 +214,13 @@ CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule(CORINFO_METHOD_HANDLE me
// vtable of it's owning class or interface.
void interceptor_ICJI::getMethodVTableOffset(CORINFO_METHOD_HANDLE method, /* IN */
unsigned* offsetOfIndirection, /* OUT */
- unsigned* offsetAfterIndirection /* OUT */
+ unsigned* offsetAfterIndirection,/* OUT */
+ bool* isRelative /* OUT */
)
{
mc->cr->AddCall("getMethodVTableOffset");
- original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
- mc->recGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
+ mc->recGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
}
// Find the virtual method in implementingClass that overrides virtualMethod.
@@ -1366,6 +1367,17 @@ const char* interceptor_ICJI::getMethodName(CORINFO_METHOD_HANDLE ftn, /*
return temp;
}
+const char* interceptor_ICJI::getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char** className, /* OUT */
+ const char** namespaceName /* OUT */
+ )
+{
+ mc->cr->AddCall("getMethodNameFromMetadata");
+ const char* temp = original_ICorJitInfo->getMethodNameFromMetadata(ftn, className, namespaceName);
+ mc->recGetMethodNameFromMetadata(ftn, (char*)temp, className, namespaceName);
+ return temp;
+}
+
// this function is for debugging only. It returns a value that
// is will always be the same for a given method. It is used
// to implement the 'jitRange' functionality
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp
index 14a55b5d79..f0bc271311 100644
--- a/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/coreclrcallbacks.cpp
@@ -11,7 +11,7 @@ CoreClrCallbacks* original_CoreClrCallbacks = nullptr;
pfnEEHeapAllocInProcessHeap original_EEHeapAllocInProcessHeap = nullptr;
pfnEEHeapFreeInProcessHeap original_EEHeapFreeInProcessHeap = nullptr;
-IExecutionEngine* IEE_t()
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
{
interceptor_IEE* iee = new interceptor_IEE();
iee->original_IEE = original_CoreClrCallbacks->m_pfnIEE();
@@ -26,21 +26,21 @@ HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer,
}
*/
-LPVOID EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes)
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes)
{
if (original_EEHeapAllocInProcessHeap == nullptr)
__debugbreak();
return original_EEHeapAllocInProcessHeap(dwFlags, dwBytes);
}
-BOOL EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem)
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem)
{
if (original_EEHeapFreeInProcessHeap == nullptr)
__debugbreak();
return original_EEHeapFreeInProcessHeap(dwFlags, lpMem);
}
-void* GetCLRFunction(LPCSTR functionName)
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
{
if (strcmp(functionName, "EEHeapAllocInProcessHeap") == 0)
{
diff --git a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
index 6760800d59..a54ead5661 100644
--- a/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
+++ b/src/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp
@@ -145,11 +145,12 @@ CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule(CORINFO_METHOD_HANDLE me
// vtable of it's owning class or interface.
void interceptor_ICJI::getMethodVTableOffset(CORINFO_METHOD_HANDLE method, /* IN */
unsigned* offsetOfIndirection, /* OUT */
- unsigned* offsetAfterIndirection /* OUT */
+ unsigned* offsetAfterIndirection,/* OUT */
+ bool* isRelative /* OUT */
)
{
mcs->AddCall("getMethodVTableOffset");
- original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
}
// Find the virtual method in implementingClass that overrides virtualMethod.
@@ -1079,6 +1080,15 @@ const char* interceptor_ICJI::getMethodName(CORINFO_METHOD_HANDLE ftn, /*
return original_ICorJitInfo->getMethodName(ftn, moduleName);
}
+const char* interceptor_ICJI::getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char** className, /* OUT */
+ const char** namespaceName /* OUT */
+ )
+{
+ mcs->AddCall("getMethodNameFromMetadata");
+ return original_ICorJitInfo->getMethodNameFromMetadata(ftn, className, namespaceName);
+}
+
// this function is for debugging only. It returns a value that
// is will always be the same for a given method. It is used
// to implement the 'jitRange' functionality
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp
index 85ceaa215f..149fe069af 100644
--- a/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/coreclrcallbacks.cpp
@@ -11,7 +11,7 @@ CoreClrCallbacks* original_CoreClrCallbacks = nullptr;
pfnEEHeapAllocInProcessHeap original_EEHeapAllocInProcessHeap = nullptr;
pfnEEHeapFreeInProcessHeap original_EEHeapFreeInProcessHeap = nullptr;
-IExecutionEngine* IEE_t()
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
{
interceptor_IEE* iee = new interceptor_IEE();
iee->original_IEE = original_CoreClrCallbacks->m_pfnIEE();
@@ -26,17 +26,17 @@ HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer,
}
*/
-LPVOID EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes)
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes)
{
return original_EEHeapAllocInProcessHeap(dwFlags, dwBytes);
}
-BOOL EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem)
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem)
{
return original_EEHeapFreeInProcessHeap(dwFlags, lpMem);
}
-void* GetCLRFunction(LPCSTR functionName)
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
{
if (strcmp(functionName, "EEHeapAllocInProcessHeap") == 0)
{
diff --git a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
index b098eb6b46..7e0ded9f0f 100644
--- a/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
+++ b/src/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp
@@ -134,10 +134,11 @@ CORINFO_MODULE_HANDLE interceptor_ICJI::getMethodModule(CORINFO_METHOD_HANDLE me
// vtable of it's owning class or interface.
void interceptor_ICJI::getMethodVTableOffset(CORINFO_METHOD_HANDLE method, /* IN */
unsigned* offsetOfIndirection, /* OUT */
- unsigned* offsetAfterIndirection /* OUT */
+ unsigned* offsetAfterIndirection,/* OUT */
+ bool* isRelative /* OUT */
)
{
- original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+ original_ICorJitInfo->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
}
// Find the virtual method in implementingClass that overrides virtualMethod.
@@ -973,6 +974,14 @@ const char* interceptor_ICJI::getMethodName(CORINFO_METHOD_HANDLE ftn, /*
return original_ICorJitInfo->getMethodName(ftn, moduleName);
}
+const char* interceptor_ICJI::getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char** className, /* OUT */
+ const char** namespaceName /* OUT */
+ )
+{
+ return original_ICorJitInfo->getMethodNameFromMetadata(ftn, className, namespaceName);
+}
+
// this function is for debugging only. It returns a value that
// is will always be the same for a given method. It is used
// to implement the 'jitRange' functionality
diff --git a/src/ToolBox/superpmi/superpmi/CMakeLists.txt b/src/ToolBox/superpmi/superpmi/CMakeLists.txt
index eb3f08a3e5..5b9897e02b 100644
--- a/src/ToolBox/superpmi/superpmi/CMakeLists.txt
+++ b/src/ToolBox/superpmi/superpmi/CMakeLists.txt
@@ -5,6 +5,7 @@ remove_definitions(-D_UNICODE)
add_definitions(-DFEATURE_NO_HOST)
add_definitions(-DSELF_NO_HOST)
+add_definitions(-DUSE_COREDISTOOLS)
if(WIN32)
#use static crt
@@ -14,10 +15,6 @@ endif(WIN32)
include_directories(.)
include_directories(../superpmi-shared)
-# When it is ready (the build works on all platforms, referencing the coredistools
-# package), define this:
-# add_definitions(-DUSE_COREDISTOOLS)
-
set(SUPERPMI_SOURCES
commandline.cpp
coreclrcallbacks.cpp
diff --git a/src/ToolBox/superpmi/superpmi/commandline.cpp b/src/ToolBox/superpmi/superpmi/commandline.cpp
index 8546afae7c..47ba0940c2 100644
--- a/src/ToolBox/superpmi/superpmi/commandline.cpp
+++ b/src/ToolBox/superpmi/superpmi/commandline.cpp
@@ -8,6 +8,7 @@
//----------------------------------------------------------
#include "standardpch.h"
+#include "lightweightmap.h"
#include "commandline.h"
#include "superpmi.h"
#include "mclist.h"
@@ -108,11 +109,22 @@ void CommandLine::DumpHelp(const char* program)
printf(" Used by the assembly differences calculator. This specifies the target\n");
printf(" architecture for cross-compilation. Currently allowed <target> value: arm64\n");
printf("\n");
-#ifdef USE_COREDISTOOLS
printf(" -coredistools\n");
printf(" Use disassembly tools from the CoreDisTools library\n");
- printf("\n");
+#if defined(USE_MSVCDIS)
+ printf(" Default: use MSVCDIS.\n");
+#elif defined(USE_COREDISTOOLS)
+ printf(" Ignored: MSVCDIS is not available, so CoreDisTools will be used.\n");
+#else
+ printf(" Ignored: neither MSVCDIS nor CoreDisTools is available.\n");
#endif // USE_COREDISTOOLS
+ printf("\n");
+ printf(" -jitoption key=value\n");
+ printf(" Set the JIT option named \"key\" to \"value\" for JIT 1. NOTE: do not use a \"COMPlus_\" prefix!\n");
+ printf("\n");
+ printf(" -jit2option key=value\n");
+ printf(" Set the JIT option named \"key\" to \"value\" for JIT 2. NOTE: do not use a \"COMPlus_\" prefix!\n");
+ printf("\n");
printf("Inputs are case sensitive.\n");
printf("\n");
printf("SuperPMI method contexts are stored in files with extension .MC, implying\n");
@@ -135,6 +147,40 @@ void CommandLine::DumpHelp(const char* program)
printf(" ; if there are any failures, record their MC numbers in the file fail.mcl\n");
}
+static bool ParseJitOption(const char* optionString, wchar_t** key, wchar_t **value)
+{
+ char tempKey[1024];
+
+ unsigned i;
+ for (i = 0; optionString[i] != '='; i++)
+ {
+ if ((i >= 1023) || (optionString[i] == '\0'))
+ {
+ return false;
+ }
+ tempKey[i] = optionString[i];
+ }
+ tempKey[i] = '\0';
+
+ const char* tempVal = &optionString[i + 1];
+ if (tempVal[0] == '\0')
+ {
+ return false;
+ }
+
+ const unsigned keyLen = i;
+ wchar_t* keyBuf = new wchar_t[keyLen + 1];
+ MultiByteToWideChar(CP_UTF8, 0, tempKey, keyLen + 1, keyBuf, keyLen + 1);
+
+ const unsigned valLen = (unsigned)strlen(tempVal);
+ wchar_t* valBuf = new wchar_t[valLen + 1];
+ MultiByteToWideChar(CP_UTF8, 0, tempVal, valLen + 1, valBuf, valLen + 1);
+
+ *key = keyBuf;
+ *value = valBuf;
+ return true;
+}
+
// Assumption: All inputs are initialized to default or real value. we'll just set the stuff we see on the command
// line. Assumption: Single byte names are passed in.. mb stuff doesnt cause an obvious problem... but it might have
// issues... Assumption: Values larger than 2^31 aren't expressible from the commandline.... (atoi) Unless you pass in
@@ -353,12 +399,12 @@ bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o)
o->compileList = argv[i]; // Save this in case we need it for -parallel.
}
-#ifdef USE_COREDISTOOLS
else if ((_strnicmp(&argv[i][1], "coredistools", argLen) == 0))
{
+#ifndef USE_COREDISTOOLS // If USE_COREDISTOOLS is not defined, then allow the switch, but ignore it.
o->useCoreDisTools = true;
- }
#endif // USE_COREDISTOOLS
+ }
else if ((_strnicmp(&argv[i][1], "matchHash", argLen) == 0))
{
if (++i >= argc)
@@ -476,6 +522,52 @@ bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o)
return false;
}
}
+ else if ((_strnicmp(&argv[i][1], "jitoption", argLen) == 0))
+ {
+ i++;
+
+ wchar_t *key, *value;
+ if ((i >= argc) || !ParseJitOption(argv[i], &key, &value))
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ if (o->jitOptions == nullptr)
+ {
+ o->jitOptions = new LightWeightMap<DWORD, DWORD>();
+ }
+
+ DWORD keyIndex = (DWORD)o->jitOptions->AddBuffer((unsigned char*)key, sizeof(wchar_t) * ((unsigned int)wcslen(key) + 1));
+ DWORD valueIndex = (DWORD)o->jitOptions->AddBuffer((unsigned char*)value, sizeof(wchar_t) * ((unsigned int)wcslen(value) + 1));
+ o->jitOptions->Add(keyIndex, valueIndex);
+
+ delete[] key;
+ delete[] value;
+ }
+ else if ((_strnicmp(&argv[i][1], "jit2option", argLen) == 0))
+ {
+ i++;
+
+ wchar_t *key, *value;
+ if ((i >= argc) || !ParseJitOption(argv[i], &key, &value))
+ {
+ DumpHelp(argv[0]);
+ return false;
+ }
+
+ if (o->jit2Options == nullptr)
+ {
+ o->jit2Options = new LightWeightMap<DWORD, DWORD>();
+ }
+
+ DWORD keyIndex = (DWORD)o->jit2Options->AddBuffer((unsigned char*)key, sizeof(wchar_t) * ((unsigned int)wcslen(key) + 1));
+ DWORD valueIndex = (DWORD)o->jit2Options->AddBuffer((unsigned char*)value, sizeof(wchar_t) * ((unsigned int)wcslen(value) + 1));
+ o->jit2Options->Add(keyIndex, valueIndex);
+
+ delete[] key;
+ delete[] value;
+ }
else
{
LogError("Unknown switch '%s' passed as argument.", argv[i]);
diff --git a/src/ToolBox/superpmi/superpmi/commandline.h b/src/ToolBox/superpmi/superpmi/commandline.h
index 9937c3c036..0881f088b0 100644
--- a/src/ToolBox/superpmi/superpmi/commandline.h
+++ b/src/ToolBox/superpmi/superpmi/commandline.h
@@ -25,7 +25,11 @@ public:
, breakOnAssert(false)
, applyDiff(false)
, parallel(false)
- , useCoreDisTools(false)
+#if !defined(USE_MSVCDIS) && defined(USE_COREDISTOOLS)
+ , useCoreDisTools(true) // if CoreDisTools is available (but MSVCDIS is not), use it.
+#else
+ , useCoreDisTools(false) // Otherwise, use MSVCDIS if that is available (else no diffs are available).
+#endif
, skipCleanup(false)
, workerCount(-1)
, indexCount(-1)
@@ -38,6 +42,8 @@ public:
, compileList(nullptr)
, offset(-1)
, increment(-1)
+ , jitOptions(nullptr)
+ , jit2Options(nullptr)
{
}
@@ -63,6 +69,8 @@ public:
char* compileList;
int offset;
int increment;
+ LightWeightMap<DWORD, DWORD>* jitOptions;
+ LightWeightMap<DWORD, DWORD>* jit2Options;
};
static bool Parse(int argc, char* argv[], /* OUT */ Options* o);
diff --git a/src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp b/src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp
index 98fbe6980d..d96e2be7df 100644
--- a/src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp
+++ b/src/ToolBox/superpmi/superpmi/coreclrcallbacks.cpp
@@ -8,7 +8,7 @@
#include "coreclrcallbacks.h"
#include "iexecutionengine.h"
-IExecutionEngine* IEE_t()
+IExecutionEngine* STDMETHODCALLTYPE IEE_t()
{
MyIEE* iee = InitIExecutionEngine();
return iee;
@@ -24,7 +24,7 @@ HRESULT STDMETHODCALLTYPE GetCORSystemDirectory(LPWSTR pbuffer, DWORD cchBuffer,
HANDLE ourHeap = nullptr;
-LPVOID EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes)
+LPVOID STDMETHODCALLTYPE EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes)
{
if (ourHeap == nullptr)
ourHeap = HeapCreate(0, 4096, 0);
@@ -39,13 +39,13 @@ LPVOID EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes)
return result;
}
-BOOL EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem)
+BOOL STDMETHODCALLTYPE EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem)
{
// return true;
return HeapFree(ourHeap, dwFlags, lpMem);
}
-void* GetCLRFunction(LPCSTR functionName)
+void* STDMETHODCALLTYPE GetCLRFunction(LPCSTR functionName)
{
if (strcmp(functionName, "EEHeapAllocInProcessHeap") == 0)
return (void*)EEHeapAllocInProcessHeap;
diff --git a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
index d88e35299a..2e78113991 100644
--- a/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
+++ b/src/ToolBox/superpmi/superpmi/icorjitinfo.cpp
@@ -165,11 +165,12 @@ CORINFO_MODULE_HANDLE MyICJI::getMethodModule(CORINFO_METHOD_HANDLE method)
// vtable of it's owning class or interface.
void MyICJI::getMethodVTableOffset(CORINFO_METHOD_HANDLE method, /* IN */
unsigned* offsetOfIndirection, /* OUT */
- unsigned* offsetAfterIndirection /* OUT */
+ unsigned* offsetAfterIndirection,/* OUT */
+ bool* isRelative /* OUT */
)
{
jitInstance->mc->cr->AddCall("getMethodVTableOffset");
- jitInstance->mc->repGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+ jitInstance->mc->repGetMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
}
// Find the virtual method in implementingClass that overrides virtualMethod.
@@ -1182,6 +1183,15 @@ const char* MyICJI::getMethodName(CORINFO_METHOD_HANDLE ftn, /* IN */
return jitInstance->mc->repGetMethodName(ftn, moduleName);
}
+const char* MyICJI::getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char** className, /* OUT */
+ const char** namespaceName /* OUT */
+ )
+{
+ jitInstance->mc->cr->AddCall("getMethodNameFromMetadata");
+ return jitInstance->mc->repGetMethodNameFromMetadata(ftn, className, namespaceName);
+}
+
// this function is for debugging only. It returns a value that
// is will always be the same for a given method. It is used
// to implement the 'jitRange' functionality
diff --git a/src/ToolBox/superpmi/superpmi/jithost.cpp b/src/ToolBox/superpmi/superpmi/jithost.cpp
index d14909bae1..a515d51cf1 100644
--- a/src/ToolBox/superpmi/superpmi/jithost.cpp
+++ b/src/ToolBox/superpmi/superpmi/jithost.cpp
@@ -77,19 +77,30 @@ int JitHost::getIntConfigValue(const wchar_t* key, int defaultValue)
return jitInstance.mc->index;
}
- // If the result is the default value, probe the environment for
- // a COMPlus variable with the same name.
- wchar_t* complus = GetCOMPlusVariable(key, jitInstance);
- if (complus == nullptr)
- {
- return defaultValue;
- }
+ // If the result is the default value, probe the JIT options and then the environment. If a value is found, parse
+ // it as a hex integer.
- // Parse the value as a hex integer.
wchar_t* endPtr;
- result = static_cast<int>(wcstoul(complus, &endPtr, 16));
- bool succeeded = (errno != ERANGE) && (endPtr != complus);
- jitInstance.freeLongLivedArray(complus);
+ bool succeeded;
+
+ const wchar_t* value = jitInstance.getOption(key);
+ if (value != nullptr)
+ {
+ result = static_cast<int>(wcstoul(value, &endPtr, 16));
+ succeeded = (errno != ERANGE) && (endPtr != value);
+ }
+ else
+ {
+ wchar_t* complus = GetCOMPlusVariable(key, jitInstance);
+ if (complus == nullptr)
+ {
+ return defaultValue;
+ }
+
+ result = static_cast<int>(wcstoul(complus, &endPtr, 16));
+ succeeded = (errno != ERANGE) && (endPtr != complus);
+ jitInstance.freeLongLivedArray(complus);
+ }
return succeeded ? result : defaultValue;
}
@@ -99,19 +110,22 @@ const wchar_t* JitHost::getStringConfigValue(const wchar_t* key)
jitInstance.mc->cr->AddCall("getStringConfigValue");
const wchar_t* result = jitInstance.mc->repGetStringConfigValue(key);
- if (result != nullptr)
+ // If the result is the default value, probe the JIT options and then the environment.
+ if (result == nullptr)
{
- // Now we need to dup it, so you can call freeStringConfigValue() on what we return.
- size_t resultLenInChars = wcslen(result) + 1;
- wchar_t* dupResult = (wchar_t*)jitInstance.allocateLongLivedArray((ULONG)(sizeof(wchar_t) * resultLenInChars));
- wcscpy_s(dupResult, resultLenInChars, result);
-
- return dupResult;
+ result = jitInstance.getOption(key);
+ if (result == nullptr)
+ {
+ return GetCOMPlusVariable(key, jitInstance);
+ }
}
- // If the result is the default value, probe the environment for
- // a COMPlus variable with the same name.
- return GetCOMPlusVariable(key, jitInstance);
+ // Now we need to dup it, so you can call freeStringConfigValue() on what we return.
+ size_t resultLenInChars = wcslen(result) + 1;
+ wchar_t* dupResult = (wchar_t*)jitInstance.allocateLongLivedArray((ULONG)(sizeof(wchar_t) * resultLenInChars));
+ wcscpy_s(dupResult, resultLenInChars, result);
+
+ return dupResult;
}
void JitHost::freeStringConfigValue(const wchar_t* value)
diff --git a/src/ToolBox/superpmi/superpmi/jithost.h b/src/ToolBox/superpmi/superpmi/jithost.h
index 60181cd907..9e31e4362d 100644
--- a/src/ToolBox/superpmi/superpmi/jithost.h
+++ b/src/ToolBox/superpmi/superpmi/jithost.h
@@ -6,7 +6,7 @@
#ifndef _JITHOST
#define _JITHOST
-class JitHost : public ICorJitHost
+class JitHost final: public ICorJitHost
{
public:
JitHost(JitInstance& jitInstance);
diff --git a/src/ToolBox/superpmi/superpmi/jitinstance.cpp b/src/ToolBox/superpmi/superpmi/jitinstance.cpp
index e463f82139..2d84cfd658 100644
--- a/src/ToolBox/superpmi/superpmi/jitinstance.cpp
+++ b/src/ToolBox/superpmi/superpmi/jitinstance.cpp
@@ -12,7 +12,7 @@
#include "errorhandling.h"
#include "spmiutil.h"
-JitInstance* JitInstance::InitJit(char* nameOfJit, bool breakOnAssert, SimpleTimer* st1, MethodContext* firstContext)
+JitInstance* JitInstance::InitJit(char* nameOfJit, bool breakOnAssert, SimpleTimer* st1, MethodContext* firstContext, LightWeightMap<DWORD, DWORD>* options)
{
JitInstance* jit = new JitInstance();
if (jit == nullptr)
@@ -21,6 +21,8 @@ JitInstance* JitInstance::InitJit(char* nameOfJit, bool breakOnAssert, SimpleTim
return nullptr;
}
+ jit->options = options;
+
if (st1 != nullptr)
st1->Start();
HRESULT hr = jit->StartUp(nameOfJit, false, breakOnAssert, firstContext);
@@ -33,6 +35,7 @@ JitInstance* JitInstance::InitJit(char* nameOfJit, bool breakOnAssert, SimpleTim
}
if (st1 != nullptr)
LogVerbose("Jit startup took %fms", st1->GetMilliseconds());
+
return jit;
}
@@ -284,7 +287,6 @@ JitInstance::Result JitInstance::CompileMethod(MethodContext* MethodToCompile, i
times[0] = 0;
times[1] = 0;
- mc->repEnvironmentSet(); // Sets envvars
stj.Start();
PAL_TRY(Param*, pParam, &param)
@@ -349,7 +351,6 @@ JitInstance::Result JitInstance::CompileMethod(MethodContext* MethodToCompile, i
// If we get here, we know it compiles
timeResult(param.info, param.flags);
}
- mc->repEnvironmentUnset(); // Unsets envvars
mc->cr->secondsToCompile = stj.GetSeconds();
@@ -398,6 +399,23 @@ void JitInstance::timeResult(CORINFO_METHOD_INFO info, unsigned flags)
/*-------------------------- Misc ---------------------------------------*/
+const wchar_t* JitInstance::getOption(const wchar_t* key)
+{
+ if (options == nullptr)
+ {
+ return nullptr;
+ }
+
+ size_t keyLenInBytes = sizeof(wchar_t) * (wcslen(key) + 1);
+ int keyIndex = options->Contains((unsigned char*)key, (unsigned int)keyLenInBytes);
+ if (keyIndex == -1)
+ {
+ return nullptr;
+ }
+
+ return (const wchar_t*)options->GetBuffer(options->Get(keyIndex));
+}
+
// Used to allocate memory that needs to handed to the EE.
// For eg, use this to allocated memory for reporting debug info,
// which will be handed to the EE by setVars() and setBoundaries()
@@ -429,3 +447,18 @@ void JitInstance::freeLongLivedArray(void* array)
{
HeapFree(ourHeap, 0, array);
}
+
+// Reset JitConfig, that stores Enviroment variables.
+bool JitInstance::resetConfig(MethodContext* firstContext)
+{
+ if (pnjitStartup != nullptr)
+ {
+ mc = firstContext;
+ ICorJitHost* newHost = new JitHost(*this);
+ pnjitStartup(newHost);
+ delete static_cast<JitHost*>(jitHost);
+ jitHost = newHost;
+ return true;
+ }
+ return false;
+}
diff --git a/src/ToolBox/superpmi/superpmi/jitinstance.h b/src/ToolBox/superpmi/superpmi/jitinstance.h
index 9db17dc1ad..0baf401803 100644
--- a/src/ToolBox/superpmi/superpmi/jitinstance.h
+++ b/src/ToolBox/superpmi/superpmi/jitinstance.h
@@ -25,6 +25,8 @@ private:
ICorJitInfo* icji;
SimpleTimer stj;
+ LightWeightMap<DWORD, DWORD>* options;
+
JitInstance(){};
void timeResult(CORINFO_METHOD_INFO info, unsigned flags);
@@ -41,13 +43,17 @@ public:
ICorJitCompiler* pJitInstance;
// Allocate and initialize the jit provided
- static JitInstance* InitJit(char* nameOfJit, bool breakOnAssert, SimpleTimer* st1, MethodContext* firstContext);
+ static JitInstance* InitJit(char* nameOfJit, bool breakOnAssert, SimpleTimer* st1, MethodContext* firstContext, LightWeightMap<DWORD, DWORD>* options);
HRESULT StartUp(char* PathToJit, bool copyJit, bool breakOnDebugBreakorAV, MethodContext* firstContext);
bool reLoad(MethodContext* firstContext);
+ bool resetConfig(MethodContext* firstContext);
+
Result CompileMethod(MethodContext* MethodToCompile, int mcIndex, bool collectThroughput);
+ const wchar_t* getOption(const wchar_t* key);
+
void* allocateArray(ULONG size);
void* allocateLongLivedArray(ULONG size);
void freeArray(void* array);
diff --git a/src/ToolBox/superpmi/superpmi/neardiffer.cpp b/src/ToolBox/superpmi/superpmi/neardiffer.cpp
index d3ccae5ccc..d31a4066b6 100644
--- a/src/ToolBox/superpmi/superpmi/neardiffer.cpp
+++ b/src/ToolBox/superpmi/superpmi/neardiffer.cpp
@@ -29,38 +29,89 @@ static void LogFromCoreDisToolsHelper(LogLevel level, const char* msg, va_list a
}
#define LOGGER(L) \
- \
-static void Log##L(const char* msg, ...) \
- \
-{ \
- va_list argList; \
- va_start(argList, msg); \
- LogFromCoreDisToolsHelper(LOGLEVEL_##L, msg, argList); \
- va_end(argList); \
- \
+static void __cdecl CorDisToolsLog##L(const char* msg, ...) \
+{ \
+ va_list argList; \
+ va_start(argList, msg); \
+ LogFromCoreDisToolsHelper(LOGLEVEL_##L, msg, argList); \
+ va_end(argList); \
}
LOGGER(VERBOSE)
LOGGER(ERROR)
LOGGER(WARNING)
-const PrintControl CorPrinter = {LogERROR, LogWARNING, LogVERBOSE, LogVERBOSE};
+const PrintControl CorPrinter = {CorDisToolsLogERROR, CorDisToolsLogWARNING, CorDisToolsLogVERBOSE, CorDisToolsLogVERBOSE};
#endif // USE_COREDISTOOLS
+#ifdef USE_COREDISTOOLS
+NewDiffer_t* g_PtrNewDiffer = nullptr;
+FinishDiff_t* g_PtrFinishDiff = nullptr;
+NearDiffCodeBlocks_t* g_PtrNearDiffCodeBlocks = nullptr;
+DumpDiffBlocks_t* g_PtrDumpDiffBlocks = nullptr;
+#endif // USE_COREDISTOOLS
+
//
-// The NearDiff Disassembler Initialization
+// The NearDiff Disassembler initialization.
+//
+// Returns true on success, false on failure.
//
-void NearDiffer::InitAsmDiff()
+bool NearDiffer::InitAsmDiff()
{
#ifdef USE_COREDISTOOLS
+
if (UseCoreDisTools)
{
- corAsmDiff = NewDiffer(Target_Host, &CorPrinter, NearDiffer::compareOffsets);
+ const char* coreDisToolsLibrary = MAKEDLLNAME_A("coredistools");
+
+ HMODULE hCoreDisToolsLib = ::LoadLibraryA(coreDisToolsLibrary);
+ if (hCoreDisToolsLib == 0)
+ {
+ LogError("LoadLibrary(%s) failed (0x%08x)", coreDisToolsLibrary, ::GetLastError());
+ return false;
+ }
+ g_PtrNewDiffer = (NewDiffer_t*)::GetProcAddress(hCoreDisToolsLib, "NewDiffer");
+ if (g_PtrNewDiffer == nullptr)
+ {
+ LogError("GetProcAddress 'NewDiffer' failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+ g_PtrFinishDiff = (FinishDiff_t*)::GetProcAddress(hCoreDisToolsLib, "FinishDiff");
+ if (g_PtrFinishDiff == nullptr)
+ {
+ LogError("GetProcAddress 'FinishDiff' failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+ g_PtrNearDiffCodeBlocks = (NearDiffCodeBlocks_t*)::GetProcAddress(hCoreDisToolsLib, "NearDiffCodeBlocks");
+ if (g_PtrNearDiffCodeBlocks == nullptr)
+ {
+ LogError("GetProcAddress 'NearDiffCodeBlocks' failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+ g_PtrDumpDiffBlocks = (DumpDiffBlocks_t*)::GetProcAddress(hCoreDisToolsLib, "DumpDiffBlocks");
+ if (g_PtrDumpDiffBlocks == nullptr)
+ {
+ LogError("GetProcAddress 'DumpDiffBlocks' failed (0x%08x)", ::GetLastError());
+ return false;
+ }
+
+ corAsmDiff = (*g_PtrNewDiffer)(Target_Host, &CorPrinter, NearDiffer::CoreDisCompareOffsetsCallback);
}
#endif // USE_COREDISTOOLS
+
+ return true;
}
+#ifdef USE_COREDISTOOLS
+// static
+bool __cdecl NearDiffer::CoreDisCompareOffsetsCallback(
+ const void* payload, size_t blockOffset, size_t instrLen, uint64_t offset1, uint64_t offset2)
+{
+ return compareOffsets(payload, blockOffset, instrLen, offset1, offset2);
+}
+#endif // USE_COREDISTOOLS
+
//
// The NearDiff destructor
//
@@ -69,7 +120,7 @@ NearDiffer::~NearDiffer()
#ifdef USE_COREDISTOOLS
if (corAsmDiff != nullptr)
{
- FinishDiff(corAsmDiff);
+ (*g_PtrFinishDiff)(corAsmDiff);
}
#endif // USE_COREDISTOOLS
}
@@ -403,12 +454,12 @@ bool NearDiffer::compareCodeSection(MethodContext* mc,
#ifdef USE_COREDISTOOLS
if (UseCoreDisTools)
{
- bool areSame = NearDiffCodeBlocks(corAsmDiff, &data, (const uint8_t*)originalBlock1, block1, blocksize1,
+ bool areSame = (*g_PtrNearDiffCodeBlocks)(corAsmDiff, &data, (const uint8_t*)originalBlock1, block1, blocksize1,
(const uint8_t*)originalBlock2, block2, blocksize2);
if (!areSame)
{
- DumpDiffBlocks(corAsmDiff, (const uint8_t*)originalBlock1, block1, blocksize1,
+ (*g_PtrDumpDiffBlocks)(corAsmDiff, (const uint8_t*)originalBlock1, block1, blocksize1,
(const uint8_t*)originalBlock2, block2, blocksize2);
}
diff --git a/src/ToolBox/superpmi/superpmi/neardiffer.h b/src/ToolBox/superpmi/superpmi/neardiffer.h
index 100f87cce2..a2c7f8f648 100644
--- a/src/ToolBox/superpmi/superpmi/neardiffer.h
+++ b/src/ToolBox/superpmi/superpmi/neardiffer.h
@@ -26,7 +26,7 @@ public:
~NearDiffer();
- void InitAsmDiff();
+ bool InitAsmDiff();
bool compare(MethodContext* mc, CompileResult* cr1, CompileResult* cr2);
@@ -74,7 +74,12 @@ private:
const void* payload, size_t blockOffset, size_t instrLen, uint64_t offset1, uint64_t offset2);
#ifdef USE_COREDISTOOLS
+
+ static bool __cdecl CoreDisCompareOffsetsCallback(
+ const void* payload, size_t blockOffset, size_t instrLen, uint64_t offset1, uint64_t offset2);
+
CorAsmDiff* corAsmDiff;
+
#endif // USE_COREDISTOOLS
#ifdef USE_MSVCDIS
diff --git a/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp b/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp
index 0f2d31d3e5..41b27deebc 100644
--- a/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp
+++ b/src/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp
@@ -7,6 +7,7 @@
#include "superpmi.h"
#include "simpletimer.h"
#include "mclist.h"
+#include "lightweightmap.h"
#include "commandline.h"
#include "errorhandling.h"
@@ -360,6 +361,26 @@ char* ConstructChildProcessArgs(const CommandLine::Options& o)
ADDARG_STRING(o.targetArchitecture, "-target");
ADDARG_STRING(o.compileList, "-compile");
+ if (o.jitOptions != nullptr)
+ {
+ for (unsigned i = 0; i < o.jitOptions->GetCount(); i++)
+ {
+ wchar_t* key = (wchar_t*)o.jitOptions->GetBuffer(o.jitOptions->GetKey(i));
+ wchar_t* value = (wchar_t*)o.jitOptions->GetBuffer(o.jitOptions->GetItem(i));
+ bytesWritten += sprintf_s(spmiArgs + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -jitoption %S=%S", key, value);
+ }
+ }
+
+ if (o.jit2Options != nullptr)
+ {
+ for (unsigned i = 0; i < o.jit2Options->GetCount(); i++)
+ {
+ wchar_t* key = (wchar_t*)o.jit2Options->GetBuffer(o.jit2Options->GetKey(i));
+ wchar_t* value = (wchar_t*)o.jit2Options->GetBuffer(o.jit2Options->GetItem(i));
+ bytesWritten += sprintf_s(spmiArgs + bytesWritten, MAX_CMDLINE_SIZE - bytesWritten, " -jit2option %S=%S", key, value);
+ }
+ }
+
ADDSTRING(o.nameOfJit);
ADDSTRING(o.nameOfJit2);
ADDSTRING(o.nameOfInputMethodContextFile);
diff --git a/src/ToolBox/superpmi/superpmi/superpmi.cpp b/src/ToolBox/superpmi/superpmi/superpmi.cpp
index c1225e419e..a8363a608d 100644
--- a/src/ToolBox/superpmi/superpmi/superpmi.cpp
+++ b/src/ToolBox/superpmi/superpmi/superpmi.cpp
@@ -9,6 +9,7 @@
#include "coredistools.h"
#endif // USE_COREDISTOOLS
+#include "lightweightmap.h"
#include "commandline.h"
#include "superpmi.h"
#include "jitinstance.h"
@@ -106,8 +107,8 @@ void InvokeNearDiffer(NearDiffer* nearDiffer,
{
SpmiException e(&param.exceptionPointers);
- LogError("main method %d of size %d failed to load and compile correctly. EnvCnt=%d",
- (*reader)->GetMethodContextIndex(), (*mc)->methodSize, (*mc)->repEnvironmentGetCount());
+ LogError("main method %d of size %d failed to load and compile correctly.",
+ (*reader)->GetMethodContextIndex(), (*mc)->methodSize);
e.ShowAndDeleteMessage();
if ((*o).mclFilename != nullptr)
(*failingMCL).AddMethodToMCL((*reader)->GetMethodContextIndex());
@@ -240,7 +241,10 @@ int __cdecl main(int argc, char* argv[])
if (o.applyDiff)
{
- nearDiffer.InitAsmDiff();
+ if (!nearDiffer.InitAsmDiff())
+ {
+ return -1;
+ }
}
while (true)
@@ -283,7 +287,7 @@ int __cdecl main(int argc, char* argv[])
{
SimpleTimer st4;
- jit = JitInstance::InitJit(o.nameOfJit, o.breakOnAssert, &st4, mc);
+ jit = JitInstance::InitJit(o.nameOfJit, o.breakOnAssert, &st4, mc, o.jitOptions);
if (jit == nullptr)
{
// InitJit already printed a failure message
@@ -292,7 +296,7 @@ int __cdecl main(int argc, char* argv[])
if (o.nameOfJit2 != nullptr)
{
- jit2 = JitInstance::InitJit(o.nameOfJit2, o.breakOnAssert, &st4, mc);
+ jit2 = JitInstance::InitJit(o.nameOfJit2, o.breakOnAssert, &st4, mc, o.jit2Options);
if (jit2 == nullptr)
{
// InitJit already printed a failure message
@@ -310,6 +314,21 @@ int __cdecl main(int argc, char* argv[])
mc->cr = new CompileResult();
mc->originalCR = crl;
+ if (mc->wasEnviromentChanged())
+ {
+ if (!jit->resetConfig(mc))
+ {
+ LogError("JIT can't reset enviroment");
+ }
+ if (o.nameOfJit2 != nullptr)
+ {
+ if (!jit2->resetConfig(mc))
+ {
+ LogError("JIT2 can't reset enviroment");
+ }
+ }
+ }
+
jittedCount++;
st3.Start();
res = jit->CompileMethod(mc, reader->GetMethodContextIndex(), collectThroughput);
@@ -342,8 +361,8 @@ int __cdecl main(int argc, char* argv[])
if (res2 == JitInstance::RESULT_ERROR)
{
- LogError("JIT2 main method %d of size %d failed to load and compile correctly. EnvCnt=%d",
- reader->GetMethodContextIndex(), mc->methodSize, mc->repEnvironmentGetCount());
+ LogError("JIT2 main method %d of size %d failed to load and compile correctly.",
+ reader->GetMethodContextIndex(), mc->methodSize);
}
// Methods that don't compile due to missing JIT-EE information
@@ -485,8 +504,8 @@ int __cdecl main(int argc, char* argv[])
// to, for instance, failures caused by missing JIT-EE details).
if (res == JitInstance::RESULT_ERROR)
{
- LogError("main method %d of size %d failed to load and compile correctly. EnvCnt=%d",
- reader->GetMethodContextIndex(), mc->methodSize, mc->repEnvironmentGetCount());
+ LogError("main method %d of size %d failed to load and compile correctly.",
+ reader->GetMethodContextIndex(), mc->methodSize);
if ((o.reproName != nullptr) && (o.indexCount == -1))
{
char buff[500];
diff --git a/src/binder/assemblybinder.cpp b/src/binder/assemblybinder.cpp
index 73ea025ae8..7c0aa8077e 100644
--- a/src/binder/assemblybinder.cpp
+++ b/src/binder/assemblybinder.cpp
@@ -89,38 +89,66 @@ namespace BINDER_SPACE
AssemblyVersion *pRequestedVersion = pRequestedName->GetVersion();
AssemblyVersion *pFoundVersion = pFoundName->GetVersion();
- //
- // If the AssemblyRef has no version, we can treat it as requesting the most accommodating version (0.0.0.0). In
- // that case, skip version checking and allow the bind.
- //
- if (!pRequestedName->HaveAssemblyVersion())
+ do
{
- return hr;
- }
+ if (!pRequestedVersion->HasMajor())
+ {
+ // An unspecified requested version component matches any value for the same component in the found version,
+ // regardless of lesser-order version components
+ break;
+ }
+ if (!pFoundVersion->HasMajor() || pRequestedVersion->GetMajor() > pFoundVersion->GetMajor())
+ {
+ // - A specific requested version component does not match an unspecified value for the same component in
+ // the found version, regardless of lesser-order version components
+ // - Or, the requested version is greater than the found version
+ hr = FUSION_E_APP_DOMAIN_LOCKED;
+ break;
+ }
+ if (pRequestedVersion->GetMajor() < pFoundVersion->GetMajor())
+ {
+ // The requested version is less than the found version
+ break;
+ }
- //
- // This if condition is paired with the one above that checks for pRequestedName
- // not having an assembly version. If we didn't exit in the above if condition,
- // and satisfy this one's requirements, we're in a situation where the assembly
- // Ref has a version, but the Def doesn't, which cannot succeed a bind
- //
- _ASSERTE(pRequestedName->HaveAssemblyVersion());
- if (!pFoundName->HaveAssemblyVersion())
- {
- hr = FUSION_E_APP_DOMAIN_LOCKED;
- }
- else if (pRequestedVersion->IsEqualFeatureVersion(pFoundVersion))
- {
- // Now service version matters
- if (pRequestedVersion->IsLargerServiceVersion(pFoundVersion))
+ if (!pRequestedVersion->HasMinor())
+ {
+ break;
+ }
+ if (!pFoundVersion->HasMinor() || pRequestedVersion->GetMinor() > pFoundVersion->GetMinor())
{
hr = FUSION_E_APP_DOMAIN_LOCKED;
+ break;
}
- }
- else if (pRequestedVersion->IsLargerFeatureVersion(pFoundVersion))
- {
- hr = FUSION_E_APP_DOMAIN_LOCKED;
- }
+ if (pRequestedVersion->GetMinor() < pFoundVersion->GetMinor())
+ {
+ break;
+ }
+
+ if (!pRequestedVersion->HasBuild())
+ {
+ break;
+ }
+ if (!pFoundVersion->HasBuild() || pRequestedVersion->GetBuild() > pFoundVersion->GetBuild())
+ {
+ hr = FUSION_E_APP_DOMAIN_LOCKED;
+ break;
+ }
+ if (pRequestedVersion->GetBuild() < pFoundVersion->GetBuild())
+ {
+ break;
+ }
+
+ if (!pRequestedVersion->HasRevision())
+ {
+ break;
+ }
+ if (!pFoundVersion->HasRevision() || pRequestedVersion->GetRevision() > pFoundVersion->GetRevision())
+ {
+ hr = FUSION_E_APP_DOMAIN_LOCKED;
+ break;
+ }
+ } while (false);
if (pApplicationContext->IsTpaListProvided() && hr == FUSION_E_APP_DOMAIN_LOCKED)
{
diff --git a/src/binder/inc/assemblyname.inl b/src/binder/inc/assemblyname.inl
index 3acd39ce0a..796b33661d 100644
--- a/src/binder/inc/assemblyname.inl
+++ b/src/binder/inc/assemblyname.inl
@@ -133,7 +133,7 @@ void AssemblyName::SetHave(DWORD dwIdentityFlags)
BOOL AssemblyName::HaveAssemblyVersion()
{
- return (m_version.GetMajor() != static_cast<DWORD>(-1));
+ return m_version.HasMajor();
}
BOOL AssemblyName::HaveNeutralCulture()
diff --git a/src/binder/inc/assemblyversion.hpp b/src/binder/inc/assemblyversion.hpp
index 20ef4ee48c..a6be617a30 100644
--- a/src/binder/inc/assemblyversion.hpp
+++ b/src/binder/inc/assemblyversion.hpp
@@ -22,10 +22,19 @@ namespace BINDER_SPACE
{
class AssemblyVersion
{
+ private:
+ static const DWORD Unspecified = (DWORD)-1;
+ static const USHORT UnspecifiedShort = (USHORT)-1;
+
public:
inline AssemblyVersion();
inline ~AssemblyVersion();
+ inline BOOL HasMajor();
+ inline BOOL HasMinor();
+ inline BOOL HasBuild();
+ inline BOOL HasRevision();
+
inline DWORD GetMajor();
inline DWORD GetMinor();
inline DWORD GetBuild();
@@ -35,19 +44,10 @@ namespace BINDER_SPACE
/* in */ DWORD dwMinor);
inline void SetServiceVersion(/* in */ DWORD dwBuild,
/* in */ DWORD dwRevision);
- inline BOOL SetServiceVersion(/* in */ LPCWSTR pwzVersionStr);
- inline BOOL SetVersion(/* in */ LPCWSTR pwzVersionStr);
inline void SetVersion(AssemblyVersion *pAssemblyVersion);
- inline BOOL IsLargerFeatureVersion(/* in */ AssemblyVersion *pAssemblyVersion);
- inline BOOL IsEqualFeatureVersion(/* in */ AssemblyVersion *pAssemblyVersion);
- inline BOOL IsSmallerFeatureVersion(/* in */ AssemblyVersion *pAssemblyVersion);
- inline BOOL IsEqualServiceVersion(/* in */ AssemblyVersion *pAssemblyVersion);
- inline BOOL IsLargerServiceVersion(/* in */ AssemblyVersion *pAssemblyVersion);
inline BOOL Equals(AssemblyVersion *pAssemblyVersion);
- inline BOOL IsSmallerOrEqual(AssemblyVersion *pAssemblyVersion);
- inline BOOL IsLargerOrEqual(AssemblyVersion *pAssemblyVersion);
- protected:
+ private:
DWORD m_dwMajor;
DWORD m_dwMinor;
DWORD m_dwBuild;
diff --git a/src/binder/inc/assemblyversion.inl b/src/binder/inc/assemblyversion.inl
index 795f8c1765..d093808a1a 100644
--- a/src/binder/inc/assemblyversion.inl
+++ b/src/binder/inc/assemblyversion.inl
@@ -25,6 +25,26 @@ AssemblyVersion::~AssemblyVersion()
// Noting to do here
}
+BOOL AssemblyVersion::HasMajor()
+{
+ return m_dwMajor != Unspecified;
+}
+
+BOOL AssemblyVersion::HasMinor()
+{
+ return m_dwMinor != Unspecified;
+}
+
+BOOL AssemblyVersion::HasBuild()
+{
+ return m_dwBuild != Unspecified;
+}
+
+BOOL AssemblyVersion::HasRevision()
+{
+ return m_dwRevision != Unspecified;
+}
+
DWORD AssemblyVersion::GetMajor()
{
return m_dwMajor;
@@ -48,22 +68,21 @@ DWORD AssemblyVersion::GetRevision()
void AssemblyVersion::SetFeatureVersion(DWORD dwMajor,
DWORD dwMinor)
{
- m_dwMajor = dwMajor;
- m_dwMinor = dwMinor;
+ // BaseAssemblySpec and AssemblyName properties store uint16 components for the version. Version and AssemblyVersion store
+ // int32 or uint32. When the former are initialized from the latter, the components are truncated to uint16 size. When the
+ // latter are initialized from the former, they are zero-extended to int32 size. For uint16 components, the max value is
+ // used to indicate an unspecified component. For int32 components, -1 is used. Since we're treating the version as an
+ // assembly version here, map the uint16 unspecified value to the int32 size.
+ m_dwMajor = dwMajor == UnspecifiedShort ? Unspecified : dwMajor;
+ m_dwMinor = dwMinor == UnspecifiedShort ? Unspecified : dwMinor;
}
void AssemblyVersion::SetServiceVersion(DWORD dwBuild,
DWORD dwRevision)
{
- m_dwBuild = dwBuild;
- m_dwRevision = dwRevision;
-}
-
-BOOL AssemblyVersion::SetVersion(LPCWSTR pwzVersionStr)
-{
- SmallStackSString versionString(pwzVersionStr);
-
- return TextualIdentityParser::ParseVersion(versionString, this);
+ // See comment in SetFeatureVersion, the same applies here
+ m_dwBuild = dwBuild == UnspecifiedShort ? Unspecified : dwBuild;
+ m_dwRevision = dwRevision == UnspecifiedShort ? Unspecified : dwRevision;
}
void AssemblyVersion::SetVersion(AssemblyVersion *pAssemblyVersion)
@@ -74,83 +93,6 @@ void AssemblyVersion::SetVersion(AssemblyVersion *pAssemblyVersion)
m_dwRevision = pAssemblyVersion->GetRevision();
}
-BOOL AssemblyVersion::IsLargerFeatureVersion(AssemblyVersion *pAssemblyVersion)
-{
- BOOL result = FALSE;
-
- if (GetMajor() > pAssemblyVersion->GetMajor())
- {
- result = TRUE;
- }
- else if ((GetMajor() == pAssemblyVersion->GetMajor()) &&
- (GetMinor() > pAssemblyVersion->GetMinor()))
- {
- result = TRUE;
- }
-
- return result;
-}
-
-BOOL AssemblyVersion::IsEqualFeatureVersion(AssemblyVersion *pAssemblyVersion)
-{
- BOOL result = FALSE;
-
- if ((GetMajor() == pAssemblyVersion->GetMajor()) &&
- (GetMinor() == pAssemblyVersion->GetMinor()))
- {
- result = TRUE;
- }
-
- return result;
-}
-
-BOOL AssemblyVersion::IsSmallerFeatureVersion(AssemblyVersion *pAssemblyVersion)
-{
- BOOL result = FALSE;
-
- if (GetMajor() < pAssemblyVersion->GetMajor())
- {
- result = TRUE;
- }
- else if ((GetMajor() == pAssemblyVersion->GetMajor()) &&
- (GetMinor() < pAssemblyVersion->GetMinor()))
- {
- result = TRUE;
- }
-
- return result;
-}
-
-BOOL AssemblyVersion::IsEqualServiceVersion(AssemblyVersion *pAssemblyVersion)
-{
- BOOL result = FALSE;
-
- if ((GetBuild() == pAssemblyVersion->GetBuild()) &&
- (GetRevision() == pAssemblyVersion->GetRevision()))
- {
- result = TRUE;
- }
-
- return result;
-}
-
-BOOL AssemblyVersion::IsLargerServiceVersion(AssemblyVersion *pAssemblyVersion)
-{
- BOOL result = FALSE;
-
- if (GetBuild() > pAssemblyVersion->GetBuild())
- {
- result = TRUE;
- }
- else if ((GetBuild() == pAssemblyVersion->GetBuild()) &&
- (GetRevision() > pAssemblyVersion->GetRevision()))
- {
- result = TRUE;
- }
-
- return result;
-}
-
BOOL AssemblyVersion::Equals(AssemblyVersion *pAssemblyVersion)
{
BOOL result = FALSE;
@@ -164,20 +106,4 @@ BOOL AssemblyVersion::Equals(AssemblyVersion *pAssemblyVersion)
return result;
}
-BOOL AssemblyVersion::IsSmallerOrEqual(AssemblyVersion *pAssemblyVersion)
-{
- return (Equals(pAssemblyVersion) ||
- IsSmallerFeatureVersion(pAssemblyVersion) ||
- (IsEqualFeatureVersion(pAssemblyVersion) &&
- !IsLargerServiceVersion(pAssemblyVersion)));
-}
-
-BOOL AssemblyVersion::IsLargerOrEqual(AssemblyVersion *pAssemblyVersion)
-{
- return (Equals(pAssemblyVersion) ||
- IsLargerFeatureVersion(pAssemblyVersion) ||
- (IsEqualFeatureVersion(pAssemblyVersion) &&
- IsLargerServiceVersion(pAssemblyVersion)));
-}
-
#endif
diff --git a/src/binder/textualidentityparser.cpp b/src/binder/textualidentityparser.cpp
index eb6e371afe..2913847dd2 100644
--- a/src/binder/textualidentityparser.cpp
+++ b/src/binder/textualidentityparser.cpp
@@ -65,7 +65,7 @@ namespace BINDER_SPACE
const int iPublicKeyMaxLength = 2048;
const int iVersionMax = 65535;
- const int iRequiredVersionParts = 4;
+ const int iVersionParts = 4;
inline void UnicodeHexToBin(LPCWSTR pSrc, UINT cSrc, LPBYTE pDest)
{
@@ -288,10 +288,10 @@ namespace BINDER_SPACE
{
tmpString.Clear();
tmpString.Printf(W("%d.%d.%d.%d"),
- pAssemblyIdentity->m_version.GetMajor(),
- pAssemblyIdentity->m_version.GetMinor(),
- pAssemblyIdentity->m_version.GetBuild(),
- pAssemblyIdentity->m_version.GetRevision());
+ (DWORD)(USHORT)pAssemblyIdentity->m_version.GetMajor(),
+ (DWORD)(USHORT)pAssemblyIdentity->m_version.GetMinor(),
+ (DWORD)(USHORT)pAssemblyIdentity->m_version.GetBuild(),
+ (DWORD)(USHORT)pAssemblyIdentity->m_version.GetRevision());
textualIdentity.Append(W(", Version="));
textualIdentity.Append(tmpString);
@@ -378,8 +378,10 @@ namespace BINDER_SPACE
{
BOOL fIsValid = FALSE;
DWORD dwFoundNumbers = 0;
- DWORD dwCurrentNumber = 0;
- DWORD dwNumbers[iRequiredVersionParts];
+ bool foundUnspecifiedComponent = false;
+ const DWORD UnspecifiedNumber = (DWORD)-1;
+ DWORD dwCurrentNumber = UnspecifiedNumber;
+ DWORD dwNumbers[iVersionParts] = {UnspecifiedNumber, UnspecifiedNumber, UnspecifiedNumber, UnspecifiedNumber};
BINDER_LOG_ENTER(W("TextualIdentityParser::ParseVersion"));
@@ -391,17 +393,46 @@ namespace BINDER_SPACE
{
WCHAR wcCurrentChar = cursor[0];
- if (dwFoundNumbers >= static_cast<DWORD>(iRequiredVersionParts))
+ if (dwFoundNumbers >= static_cast<DWORD>(iVersionParts))
{
goto Exit;
}
- else if (wcCurrentChar == W('.') || wcCurrentChar == 0x00)
+ else if (wcCurrentChar == W('.') || wcCurrentChar == W('\0'))
{
- dwNumbers[dwFoundNumbers++] = dwCurrentNumber;
- dwCurrentNumber = 0;
+ if (dwCurrentNumber == UnspecifiedNumber)
+ {
+ // Difference from .NET Framework, compat with Version(string) constructor: A missing version component
+ // is considered invalid.
+ //
+ // Examples:
+ // "MyAssembly, Version=."
+ // "MyAssembly, Version=1."
+ // "MyAssembly, Version=.1"
+ // "MyAssembly, Version=1..1"
+ goto Exit;
+ }
+
+ // Compat with .NET Framework: A value of iVersionMax is considered unspecified. Once an unspecified
+ // component is found, validate the remaining components but consider them as unspecified as well.
+ if (dwCurrentNumber == iVersionMax)
+ {
+ foundUnspecifiedComponent = true;
+ dwCurrentNumber = UnspecifiedNumber;
+ }
+ else if (!foundUnspecifiedComponent)
+ {
+ dwNumbers[dwFoundNumbers] = dwCurrentNumber;
+ dwCurrentNumber = UnspecifiedNumber;
+ }
+
+ dwFoundNumbers++;
}
else if ((wcCurrentChar >= W('0')) && (wcCurrentChar <= W('9')))
{
+ if (dwCurrentNumber == UnspecifiedNumber)
+ {
+ dwCurrentNumber = 0;
+ }
dwCurrentNumber = (dwCurrentNumber * 10) + (wcCurrentChar - W('0'));
if (dwCurrentNumber > static_cast<DWORD>(iVersionMax))
@@ -417,12 +448,21 @@ namespace BINDER_SPACE
cursor++;
}
- if (dwFoundNumbers == static_cast<DWORD>(iRequiredVersionParts))
+ // Difference from .NET Framework: If the major or minor version are unspecified, the version is considered invalid.
+ //
+ // Examples:
+ // "MyAssembly, Version="
+ // "MyAssembly, Version=1"
+ // "MyAssembly, Version=65535.1"
+ // "MyAssembly, Version=1.65535"
+ if (dwFoundNumbers < 2 || dwNumbers[0] == UnspecifiedNumber || dwNumbers[1] == UnspecifiedNumber)
{
- pAssemblyVersion->SetFeatureVersion(dwNumbers[0], dwNumbers[1]);
- pAssemblyVersion->SetServiceVersion(dwNumbers[2], dwNumbers[3]);
- fIsValid = TRUE;
+ goto Exit;
}
+
+ pAssemblyVersion->SetFeatureVersion(dwNumbers[0], dwNumbers[1]);
+ pAssemblyVersion->SetServiceVersion(dwNumbers[2], dwNumbers[3]);
+ fIsValid = TRUE;
}
Exit:
diff --git a/src/build.proj b/src/build.proj
index 7962d27e99..b59b00c00f 100644
--- a/src/build.proj
+++ b/src/build.proj
@@ -38,4 +38,18 @@
DestinationFolder="$(BinDir)PDB" />
</Target>
+ <PropertyGroup>
+ <RunEnforcePGO Condition="$(__EnforcePgo) == '1'">true</RunEnforcePGO>
+ <RunEnforcePGO Condition="$(__BuildArch) == 'arm' OR $(__BuildArch) == 'arm64'">false</RunEnforcePGO>
+ </PropertyGroup>
+
+ <Target Name="EnforcePGO" Condition="$(RunEnforcePGO) == 'true'" AfterTargets="Build">
+ <ItemGroup>
+ <PGOEnforcedFiles Include="$(BinDir)coreclr.dll" />
+ <PGOEnforcedFiles Include="$(BinDir)clrjit.dll" />
+ </ItemGroup>
+
+ <Message Text="Checking if the following DLLs are properly compiled with PGO" Importance="High" />
+ <Exec Command="python $(MSBuildThisFileDirectory)scripts\pgocheck.py @(PGOEnforcedFiles)" />
+ </Target>
</Project>
diff --git a/src/classlibnative/bcltype/CMakeLists.txt b/src/classlibnative/bcltype/CMakeLists.txt
index 7999c0c90f..74ccda0a81 100644
--- a/src/classlibnative/bcltype/CMakeLists.txt
+++ b/src/classlibnative/bcltype/CMakeLists.txt
@@ -7,6 +7,7 @@ endif(PerfCountersSupportedBuild)
set(BCLTYPE_SOURCES
arraynative.cpp
arrayhelpers.cpp
+ bignum.cpp
currency.cpp
decimal.cpp
windowsruntimebufferhelper.cpp
diff --git a/src/classlibnative/bcltype/arraynative.cpp b/src/classlibnative/bcltype/arraynative.cpp
index 7933d3a469..b813b71638 100644
--- a/src/classlibnative/bcltype/arraynative.cpp
+++ b/src/classlibnative/bcltype/arraynative.cpp
@@ -14,7 +14,6 @@
#include "arraynative.h"
#include "excep.h"
#include "field.h"
-#include "security.h"
#include "invokeutil.h"
#include "arraynative.inl"
@@ -1152,7 +1151,6 @@ void ArrayNative::CheckElementType(TypeHandle elementType)
CorElementType etType = elementType.GetSignatureCorElementType();
if (etType == ELEMENT_TYPE_PTR || etType == ELEMENT_TYPE_FNPTR)
{
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
return;
}
diff --git a/src/classlibnative/bcltype/bignum.cpp b/src/classlibnative/bcltype/bignum.cpp
new file mode 100644
index 0000000000..c4aa534524
--- /dev/null
+++ b/src/classlibnative/bcltype/bignum.cpp
@@ -0,0 +1,599 @@
+// 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.
+//
+// File: bignum.cpp
+//
+
+//
+
+#include "bignum.h"
+#include <intrin.h>
+
+constexpr UINT32 BigNum::m_power10UInt32Table[UINT32POWER10NUM];
+BigNum BigNum::m_power10BigNumTable[BIGPOWER10NUM];
+BigNum::StaticInitializer BigNum::m_initializer;
+
+BigNum::BigNum()
+ :m_len(0) // Note: we do not zeroing m_blocks due to performance.
+{
+}
+
+BigNum::BigNum(UINT32 value)
+{
+ SetUInt32(value);
+}
+
+BigNum::BigNum(UINT64 value)
+{
+ SetUInt64(value);
+}
+
+BigNum::~BigNum()
+{
+}
+
+BigNum& BigNum::operator=(const BigNum &rhs)
+{
+ memcpy(m_blocks, rhs.m_blocks, ((UINT32)rhs.m_len) * sizeof(UINT32));
+ m_len = rhs.m_len;
+
+ return *this;
+}
+
+int BigNum::Compare(const BigNum& lhs, const BigNum& rhs)
+{
+ _ASSERTE(lhs.m_len <= BIGSIZE);
+ _ASSERTE(rhs.m_len <= BIGSIZE);
+
+ int lenDiff = (int)lhs.m_len - (int)rhs.m_len;
+ if (lenDiff != 0)
+ {
+ return lenDiff;
+ }
+
+ if (lhs.m_len == 0)
+ {
+ _ASSERTE(rhs.m_len == 0);
+
+ return 0;
+ }
+
+ for (INT32 currentIndex = lhs.m_len - 1; currentIndex >= 0; --currentIndex)
+ {
+ INT64 diff = (INT64)(lhs.m_blocks[currentIndex]) - (INT64)(rhs.m_blocks[currentIndex]);
+ if (diff != 0)
+ {
+ return diff > 0 ? 1 : -1;
+ }
+ }
+
+ return 0;
+}
+
+void BigNum::ShiftLeft(UINT64 input, UINT32 shift, BigNum& output)
+{
+ if (shift == 0)
+ {
+ return;
+ }
+
+ UINT32 shiftBlocks = shift / 32;
+ UINT32 remaningToShiftBits = shift % 32;
+
+ if (shiftBlocks > 0)
+ {
+ // If blocks shifted, we should fill the corresponding blocks with zero.
+ output.ExtendBlocks(0, shiftBlocks);
+ }
+
+ if (remaningToShiftBits == 0)
+ {
+ // We shift 32 * n (n >= 1) bits. No remaining bits.
+ output.ExtendBlock((UINT32)(input & 0xFFFFFFFF));
+
+ UINT32 highBits = (UINT32)(input >> 32);
+ if (highBits != 0)
+ {
+ output.ExtendBlock(highBits);
+ }
+ }
+ else
+ {
+ // Extract the high position bits which would be shifted out of range.
+ UINT32 highPositionBits = (UINT32)input >> (64 - remaningToShiftBits);
+
+ // Shift the input. The result should be stored to current block.
+ UINT64 shiftedInput = input << remaningToShiftBits;
+ output.ExtendBlock(shiftedInput & 0xFFFFFFFF);
+
+ UINT32 highBits = (UINT32)(input >> 32);
+ if (highBits != 0)
+ {
+ output.ExtendBlock(highBits);
+ }
+
+ if (highPositionBits != 0)
+ {
+ // If the high position bits is not 0, we should store them to next block.
+ output.ExtendBlock(highPositionBits);
+ }
+ }
+}
+
+void BigNum::ShiftLeft(UINT32 shift)
+{
+ if (m_len == 0 || shift == 0)
+ {
+ return;
+ }
+
+ UINT32 shiftBlocks = shift / 32;
+ UINT32 shiftBits = shift % 32;
+
+ // Process blocks high to low so that we can safely process in place
+ int inLength = m_len;
+
+ // Check if the shift is block aligned
+ if (shiftBits == 0)
+ {
+ // Copy blocks from high to low
+ UINT32* pInCurrent = m_blocks + inLength;
+ UINT32* pOutCurrent = pInCurrent + shiftBlocks;
+ while (pInCurrent >= m_blocks)
+ {
+ *pOutCurrent = *pInCurrent;
+
+ --pInCurrent;
+ --pOutCurrent;
+ }
+
+ m_len += shiftBlocks;
+
+ // Zero the remaining low blocks
+ memset(m_blocks, 0, shiftBlocks);
+ }
+ else
+ {
+ // We need to shift partial blocks
+ int inBlockIdx = inLength - 1;
+ UINT32 outBlockIdx = inLength + shiftBlocks;
+
+ _ASSERTE(outBlockIdx < BIGSIZE);
+
+ // Set the length to hold the shifted blocks
+ m_len = outBlockIdx + 1;
+
+ // Output the initial blocks
+ const UINT32 lowBitsShift = (32 - shiftBits);
+ UINT32 highBits = 0;
+ UINT32 block = m_blocks[inBlockIdx];
+ UINT32 lowBits = block >> lowBitsShift;
+ while (inBlockIdx > 0)
+ {
+ m_blocks[outBlockIdx] = highBits | lowBits;
+ highBits = block << shiftBits;
+
+ --inBlockIdx;
+ --outBlockIdx;
+
+ block = m_blocks[inBlockIdx];
+ lowBits = block >> lowBitsShift;
+ }
+
+ // Output the final blocks
+ m_blocks[outBlockIdx] = highBits | lowBits;
+ m_blocks[outBlockIdx - 1] = block << shiftBits;
+
+ // Zero the remaining low blocks
+ memset(m_blocks, 0, shiftBlocks * sizeof(UINT32));
+
+ // Check if the terminating block has no set bits
+ if (m_blocks[m_len - 1] == 0)
+ {
+ --m_len;
+ }
+ }
+}
+
+void BigNum::Pow10(int exp, BigNum& result)
+{
+ // We leverage two arrays - m_power10UInt32Table and m_power10BigNumTable to speed up the
+ // pow10 calculation.
+ //
+ // m_power10UInt32Table stores the results of 10^0 to 10^7.
+ // m_power10BigNumTable stores the results of 10^8, 10^16, 10^32, 10^64, 10^128 and 10^256.
+ //
+ // For example, let's say exp = (111111)2. We can split the exp to two parts, one is small exp,
+ // which 10^smallExp can be represented as UINT32, another part is 10^bigExp, which must be represented as BigNum.
+ // So the result should be 10^smallExp * 10^bigExp.
+ //
+ // Calculate 10^smallExp is simple, we just lookup the 10^smallExp from m_power10UInt32Table.
+ // But here's a bad news: although UINT32 can represent 10^9, exp 9's binary representation is 1001.
+ // That means 10^(1011), 10^(1101), 10^(1111) all cannot be stored as UINT32, we cannot easily say something like:
+ // "Any bits <= 3 is small exp, any bits > 3 is big exp". So instead of involving 10^8, 10^9 to m_power10UInt32Table,
+ // consider 10^8 and 10^9 as a bigNum, so they fall into m_power10BigNumTable. Now we can have a simple rule:
+ // "Any bits <= 3 is small exp, any bits > 3 is big exp".
+ //
+ // For (111111)2, we first calculate 10^(smallExp), which is 10^(7), now we can shift right 3 bits, prepare to calculate the bigExp part,
+ // the exp now becomes (000111)2.
+ //
+ // Apparently the lowest bit of bigExp should represent 10^8 because we have already shifted 3 bits for smallExp, so m_power10BigNumTable[0] = 10^8.
+ // Now let's shift exp right 1 bit, the lowest bit should represent 10^(8 * 2) = 10^16, and so on...
+ //
+ // That's why we just need the values of m_power10BigNumTable be power of 2.
+ //
+ // More details of this implementation can be found at: https://github.com/dotnet/coreclr/pull/12894#discussion_r128890596
+
+ BigNum temp1;
+ BigNum temp2;
+
+ BigNum* pCurrentTemp = &temp1;
+ BigNum* pNextTemp = &temp2;
+
+ // Extract small exp.
+ UINT32 smallExp = exp & 0x7;
+ pCurrentTemp->SetUInt32(m_power10UInt32Table[smallExp]);
+
+ exp >>= 3;
+ UINT32 idx = 0;
+
+ while (exp != 0)
+ {
+ // If the current bit is set, multiply it with the corresponding power of 10
+ if (exp & 1)
+ {
+ // Multiply into the next temporary
+ Multiply(*pCurrentTemp, m_power10BigNumTable[idx], *pNextTemp);
+
+ // Swap to the next temporary
+ BigNum* t = pNextTemp;
+ pNextTemp = pCurrentTemp;
+ pCurrentTemp = t;
+ }
+
+ // Advance to the next bit
+ ++idx;
+ exp >>= 1;
+ }
+
+ result = *pCurrentTemp;
+}
+
+void BigNum::PrepareHeuristicDivide(BigNum* pDividend, BigNum* pDivisor)
+{
+ UINT32 hiBlock = pDivisor->m_blocks[pDivisor->m_len - 1];
+ if (hiBlock < 8 || hiBlock > 429496729)
+ {
+ // Inspired by http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
+ // Perform a bit shift on all values to get the highest block of the divisor into
+ // the range [8,429496729]. We are more likely to make accurate quotient estimations
+ // in heuristicDivide() with higher divisor values so
+ // we shift the divisor to place the highest bit at index 27 of the highest block.
+ // This is safe because (2^28 - 1) = 268435455 which is less than 429496729. This means
+ // that all values with a highest bit at index 27 are within range.
+ UINT32 hiBlockLog2 = LogBase2(hiBlock);
+ UINT32 shift = (59 - hiBlockLog2) % 32;
+
+ pDivisor->ShiftLeft(shift);
+ pDividend->ShiftLeft(shift);
+ }
+}
+
+UINT32 BigNum::HeuristicDivide(BigNum* pDividend, const BigNum& divisor)
+{
+ UINT32 len = divisor.m_len;
+ if (pDividend->m_len < len)
+ {
+ return 0;
+ }
+
+ const UINT32* pFinalDivisorBlock = divisor.m_blocks + len - 1;
+ UINT32* pFinalDividendBlock = pDividend->m_blocks + len - 1;
+
+ // This is an estimated quotient. Its error should be less than 2.
+ // Reference inequality:
+ // a/b - floor(floor(a)/(floor(b) + 1)) < 2
+ UINT32 quotient = *pFinalDividendBlock / (*pFinalDivisorBlock + 1);
+
+ if (quotient != 0)
+ {
+ // Now we use our estimated quotient to update each block of dividend.
+ // dividend = dividend - divisor * quotient
+ const UINT32 *pDivisorCurrent = divisor.m_blocks;
+ UINT32 *pDividendCurrent = pDividend->m_blocks;
+
+ UINT64 borrow = 0;
+ UINT64 carry = 0;
+ do
+ {
+ UINT64 product = (UINT64)*pDivisorCurrent * (UINT64)quotient + carry;
+ carry = product >> 32;
+
+ UINT64 difference = (UINT64)*pDividendCurrent - (product & 0xFFFFFFFF) - borrow;
+ borrow = (difference >> 32) & 1;
+
+ *pDividendCurrent = difference & 0xFFFFFFFF;
+
+ ++pDivisorCurrent;
+ ++pDividendCurrent;
+ } while (pDivisorCurrent <= pFinalDivisorBlock);
+
+ // Remove all leading zero blocks from dividend
+ while (len > 0 && pDividend->m_blocks[len - 1] == 0)
+ {
+ --len;
+ }
+
+ pDividend->m_len = len;
+ }
+
+ // If the dividend is still larger than the divisor, we overshot our estimate quotient. To correct,
+ // we increment the quotient and subtract one more divisor from the dividend (Because we guaranteed the error range).
+ if (BigNum::Compare(*pDividend, divisor) >= 0)
+ {
+ ++quotient;
+
+ // dividend = dividend - divisor
+ const UINT32 *pDivisorCur = divisor.m_blocks;
+ UINT32 *pDividendCur = pDividend->m_blocks;
+
+ UINT64 borrow = 0;
+ do
+ {
+ UINT64 difference = (UINT64)*pDividendCur - (UINT64)*pDivisorCur - borrow;
+ borrow = (difference >> 32) & 1;
+
+ *pDividendCur = difference & 0xFFFFFFFF;
+
+ ++pDivisorCur;
+ ++pDividendCur;
+ } while (pDivisorCur <= pFinalDivisorBlock);
+
+ // Remove all leading zero blocks from dividend
+ while (len > 0 && pDividend->m_blocks[len - 1] == 0)
+ {
+ --len;
+ }
+
+ pDividend->m_len = len;
+ }
+
+ return quotient;
+}
+
+void BigNum::Multiply(UINT32 value)
+{
+ Multiply(*this, value, *this);
+}
+
+void BigNum::Multiply(const BigNum& value)
+{
+ BigNum temp;
+ BigNum::Multiply(*this, value, temp);
+
+ memcpy(m_blocks, temp.m_blocks, ((UINT32)temp.m_len) * sizeof(UINT32));
+ m_len = temp.m_len;
+}
+
+void BigNum::Multiply(const BigNum& lhs, UINT32 value, BigNum& result)
+{
+ if (lhs.IsZero() || value == 1)
+ {
+ result = lhs;
+
+ return;
+ }
+
+ if (value == 0)
+ {
+ result.SetZero();
+
+ return;
+ }
+
+ const UINT32* pCurrent = lhs.m_blocks;
+ const UINT32* pEnd = pCurrent + lhs.m_len;
+ UINT32* pResultCurrent = result.m_blocks;
+
+ UINT64 carry = 0;
+ while (pCurrent != pEnd)
+ {
+ UINT64 product = (UINT64)(*pCurrent) * (UINT64)value + carry;
+ carry = product >> 32;
+ *pResultCurrent = (UINT32)(product & 0xFFFFFFFF);
+
+ ++pResultCurrent;
+ ++pCurrent;
+ }
+
+ if (carry != 0)
+ {
+ _ASSERTE(lhs.m_len + 1 <= BIGSIZE);
+ *pResultCurrent = (UINT32)carry;
+ result.m_len += lhs.m_len + 1;
+ }
+}
+
+void BigNum::Multiply(const BigNum& lhs, const BigNum& rhs, BigNum& result)
+{
+ if (lhs.IsZero() || (rhs.m_len == 1 && rhs.m_blocks[0] == 1))
+ {
+ result = lhs;
+
+ return;
+ }
+
+ if (rhs.IsZero())
+ {
+ result.SetZero();
+
+ return;
+ }
+
+ const BigNum* pLarge = NULL;
+ const BigNum* pSmall = NULL;
+ if (lhs.m_len < rhs.m_len)
+ {
+ pSmall = &lhs;
+ pLarge = &rhs;
+ }
+ else
+ {
+ pSmall = &rhs;
+ pLarge = &lhs;
+ }
+
+ UINT32 maxResultLength = pSmall->m_len + pLarge->m_len;
+ _ASSERTE(maxResultLength <= BIGSIZE);
+
+ // Zero out result internal blocks.
+ memset(result.m_blocks, 0, sizeof(UINT32) * BIGSIZE);
+
+ const UINT32* pLargeBegin = pLarge->m_blocks;
+ const UINT32* pLargeEnd = pLarge->m_blocks + pLarge->m_len;
+
+ UINT32* pResultStart = result.m_blocks;
+ const UINT32* pSmallCurrent = pSmall->m_blocks;
+ const UINT32* pSmallEnd = pSmallCurrent + pSmall->m_len;
+
+ while (pSmallCurrent != pSmallEnd)
+ {
+ // Multiply each block of large BigNum.
+ if (*pSmallCurrent != 0)
+ {
+ const UINT32* pLargeCurrent = pLargeBegin;
+ UINT32* pResultCurrent = pResultStart;
+ UINT64 carry = 0;
+
+ do
+ {
+ UINT64 product = (UINT64)(*pResultCurrent) + (UINT64)(*pSmallCurrent) * (UINT64)(*pLargeCurrent) + carry;
+ carry = product >> 32;
+ *pResultCurrent = (UINT32)(product & 0xFFFFFFFF);
+
+ ++pResultCurrent;
+ ++pLargeCurrent;
+ } while (pLargeCurrent != pLargeEnd);
+
+ *pResultCurrent = (UINT32)(carry & 0xFFFFFFFF);
+ }
+
+ ++pSmallCurrent;
+ ++pResultStart;
+ }
+
+ if (maxResultLength > 0 && result.m_blocks[maxResultLength - 1] == 0)
+ {
+ result.m_len = maxResultLength - 1;
+ }
+ else
+ {
+ result.m_len = maxResultLength;
+ }
+}
+
+void BigNum::Multiply10()
+{
+ if (IsZero())
+ {
+ return;
+ }
+
+ const UINT32* pCurrent = m_blocks;
+ const UINT32* pEnd = pCurrent + m_len;
+ UINT32* pResultCurrent = m_blocks;
+
+ UINT64 carry = 0;
+ while (pCurrent != pEnd)
+ {
+ UINT64 product = ((UINT64)(*pCurrent) << 3) + ((UINT64)(*pCurrent) << 1) + carry;
+ carry = product >> 32;
+ *pResultCurrent = (UINT32)(product & 0xFFFFFFFF);
+
+ ++pResultCurrent;
+ ++pCurrent;
+ }
+
+ if (carry != 0)
+ {
+ _ASSERTE(m_len + 1 <= BIGSIZE);
+ *pResultCurrent = (UINT32)carry;
+ m_len += 1;
+ }
+}
+
+bool BigNum::IsZero() const
+{
+ return m_len == 0;
+}
+
+void BigNum::SetUInt32(UINT32 value)
+{
+ m_len = 1;
+ m_blocks[0] = value;
+}
+
+void BigNum::SetUInt64(UINT64 value)
+{
+ m_blocks[0] = (UINT32)(value & 0xFFFFFFFF);
+ m_blocks[1] = (UINT32)(value >> 32);
+ m_len = (m_blocks[1] == 0) ? 1 : 2;
+}
+
+void BigNum::SetZero()
+{
+ m_len = 0;
+}
+
+void BigNum::ExtendBlock(UINT32 blockValue)
+{
+ m_blocks[m_len] = blockValue;
+ ++m_len;
+}
+
+void BigNum::ExtendBlocks(UINT32 blockValue, UINT32 blockCount)
+{
+ _ASSERTE(blockCount > 0);
+
+ if (blockCount == 1)
+ {
+ ExtendBlock(blockValue);
+
+ return;
+ }
+
+ memset(m_blocks + m_len, 0, (blockCount - 1) * sizeof(UINT32));
+ m_len += blockCount;
+ m_blocks[m_len - 1] = blockValue;
+}
+
+UINT32 BigNum::LogBase2(UINT32 value)
+{
+ _ASSERTE(value != 0);
+
+ DWORD r;
+ BitScanReverse(&r, (DWORD)value);
+
+ return (UINT32)r;
+}
+
+UINT32 BigNum::LogBase2(UINT64 value)
+{
+ _ASSERTE(value != 0);
+
+#if (defined(_X86_) || defined(_ARM_)) && !defined(FEATURE_PAL)
+ UINT64 temp = value >> 32;
+ if (temp != 0)
+ {
+ return 32 + LogBase2((UINT32)temp);
+ }
+
+ return LogBase2((UINT32)value);
+#else
+ DWORD r;
+ BitScanReverse64(&r, (DWORD64)value);
+
+ return (UINT32)r;
+#endif
+}
diff --git a/src/classlibnative/bcltype/bignum.h b/src/classlibnative/bcltype/bignum.h
new file mode 100644
index 0000000000..0af7710d6e
--- /dev/null
+++ b/src/classlibnative/bcltype/bignum.h
@@ -0,0 +1,152 @@
+// 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.
+//
+// File: bignum.h
+//
+
+//
+
+#ifndef _BIGNUM_H_
+#define _BIGNUM_H_
+
+#include <clrtypes.h>
+
+class BigNum
+{
+public:
+ BigNum();
+ BigNum(UINT32 value);
+ BigNum(UINT64 value);
+ ~BigNum();
+
+ BigNum & operator=(const BigNum &rhs);
+
+ static UINT32 LogBase2(UINT32 val);
+ static UINT32 LogBase2(UINT64 val);
+
+ static int Compare(const BigNum& lhs, const BigNum& rhs);
+
+ static void ShiftLeft(UINT64 input, UINT32 shift, BigNum& output);
+ static void Pow10(int exp, BigNum& result);
+ static void PrepareHeuristicDivide(BigNum* pDividend, BigNum* divisor);
+ static UINT32 HeuristicDivide(BigNum* pDividend, const BigNum& divisor);
+ static void Multiply(const BigNum& lhs, UINT32 value, BigNum& result);
+ static void Multiply(const BigNum& lhs, const BigNum& rhs, BigNum& result);
+
+ bool IsZero() const;
+
+ void Multiply(UINT32 value);
+ void Multiply(const BigNum& value);
+ void Multiply10();
+ void ShiftLeft(UINT32 shift);
+ void SetUInt32(UINT32 value);
+ void SetUInt64(UINT64 value);
+ void SetZero();
+ void ExtendBlock(UINT32 blockValue);
+ void ExtendBlocks(UINT32 blockValue, UINT32 blockCount);
+
+private:
+
+ static const UINT32 BIGSIZE = 35;
+ static const UINT32 UINT32POWER10NUM = 8;
+ static const UINT32 BIGPOWER10NUM = 6;
+ static constexpr UINT32 m_power10UInt32Table[UINT32POWER10NUM] =
+ {
+ 1, // 10^0
+ 10, // 10^1
+ 100, // 10^2
+ 1000, // 10^3
+ 10000, // 10^4
+ 100000, // 10^5
+ 1000000, // 10^6
+ 10000000, // 10^7
+ };
+ static BigNum m_power10BigNumTable[BIGPOWER10NUM];
+
+ static class StaticInitializer
+ {
+ public:
+ StaticInitializer()
+ {
+ // 10^8
+ m_power10BigNumTable[0].m_len = (UINT32)1;
+ m_power10BigNumTable[0].m_blocks[0] = (UINT32)100000000;
+
+ // 10^16
+ m_power10BigNumTable[1].m_len = (UINT32)2;
+ m_power10BigNumTable[1].m_blocks[0] = (UINT32)0x6fc10000;
+ m_power10BigNumTable[1].m_blocks[1] = (UINT32)0x002386f2;
+
+ // 10^32
+ m_power10BigNumTable[2].m_len = (UINT32)4;
+ m_power10BigNumTable[2].m_blocks[0] = (UINT32)0x00000000;
+ m_power10BigNumTable[2].m_blocks[1] = (UINT32)0x85acef81;
+ m_power10BigNumTable[2].m_blocks[2] = (UINT32)0x2d6d415b;
+ m_power10BigNumTable[2].m_blocks[3] = (UINT32)0x000004ee;
+
+ // 10^64
+ m_power10BigNumTable[3].m_len = (UINT32)7;
+ m_power10BigNumTable[3].m_blocks[0] = (UINT32)0x00000000;
+ m_power10BigNumTable[3].m_blocks[1] = (UINT32)0x00000000;
+ m_power10BigNumTable[3].m_blocks[2] = (UINT32)0xbf6a1f01;
+ m_power10BigNumTable[3].m_blocks[3] = (UINT32)0x6e38ed64;
+ m_power10BigNumTable[3].m_blocks[4] = (UINT32)0xdaa797ed;
+ m_power10BigNumTable[3].m_blocks[5] = (UINT32)0xe93ff9f4;
+ m_power10BigNumTable[3].m_blocks[6] = (UINT32)0x00184f03;
+
+ // 10^128
+ m_power10BigNumTable[4].m_len = (UINT32)14;
+ m_power10BigNumTable[4].m_blocks[0] = (UINT32)0x00000000;
+ m_power10BigNumTable[4].m_blocks[1] = (UINT32)0x00000000;
+ m_power10BigNumTable[4].m_blocks[2] = (UINT32)0x00000000;
+ m_power10BigNumTable[4].m_blocks[3] = (UINT32)0x00000000;
+ m_power10BigNumTable[4].m_blocks[4] = (UINT32)0x2e953e01;
+ m_power10BigNumTable[4].m_blocks[5] = (UINT32)0x03df9909;
+ m_power10BigNumTable[4].m_blocks[6] = (UINT32)0x0f1538fd;
+ m_power10BigNumTable[4].m_blocks[7] = (UINT32)0x2374e42f;
+ m_power10BigNumTable[4].m_blocks[8] = (UINT32)0xd3cff5ec;
+ m_power10BigNumTable[4].m_blocks[9] = (UINT32)0xc404dc08;
+ m_power10BigNumTable[4].m_blocks[10] = (UINT32)0xbccdb0da;
+ m_power10BigNumTable[4].m_blocks[11] = (UINT32)0xa6337f19;
+ m_power10BigNumTable[4].m_blocks[12] = (UINT32)0xe91f2603;
+ m_power10BigNumTable[4].m_blocks[13] = (UINT32)0x0000024e;
+
+ // 10^256
+ m_power10BigNumTable[5].m_len = (UINT32)27;
+ m_power10BigNumTable[5].m_blocks[0] = (UINT32)0x00000000;
+ m_power10BigNumTable[5].m_blocks[1] = (UINT32)0x00000000;
+ m_power10BigNumTable[5].m_blocks[2] = (UINT32)0x00000000;
+ m_power10BigNumTable[5].m_blocks[3] = (UINT32)0x00000000;
+ m_power10BigNumTable[5].m_blocks[4] = (UINT32)0x00000000;
+ m_power10BigNumTable[5].m_blocks[5] = (UINT32)0x00000000;
+ m_power10BigNumTable[5].m_blocks[6] = (UINT32)0x00000000;
+ m_power10BigNumTable[5].m_blocks[7] = (UINT32)0x00000000;
+ m_power10BigNumTable[5].m_blocks[8] = (UINT32)0x982e7c01;
+ m_power10BigNumTable[5].m_blocks[9] = (UINT32)0xbed3875b;
+ m_power10BigNumTable[5].m_blocks[10] = (UINT32)0xd8d99f72;
+ m_power10BigNumTable[5].m_blocks[11] = (UINT32)0x12152f87;
+ m_power10BigNumTable[5].m_blocks[12] = (UINT32)0x6bde50c6;
+ m_power10BigNumTable[5].m_blocks[13] = (UINT32)0xcf4a6e70;
+ m_power10BigNumTable[5].m_blocks[14] = (UINT32)0xd595d80f;
+ m_power10BigNumTable[5].m_blocks[15] = (UINT32)0x26b2716e;
+ m_power10BigNumTable[5].m_blocks[16] = (UINT32)0xadc666b0;
+ m_power10BigNumTable[5].m_blocks[17] = (UINT32)0x1d153624;
+ m_power10BigNumTable[5].m_blocks[18] = (UINT32)0x3c42d35a;
+ m_power10BigNumTable[5].m_blocks[19] = (UINT32)0x63ff540e;
+ m_power10BigNumTable[5].m_blocks[20] = (UINT32)0xcc5573c0;
+ m_power10BigNumTable[5].m_blocks[21] = (UINT32)0x65f9ef17;
+ m_power10BigNumTable[5].m_blocks[22] = (UINT32)0x55bc28f2;
+ m_power10BigNumTable[5].m_blocks[23] = (UINT32)0x80dcc7f7;
+ m_power10BigNumTable[5].m_blocks[24] = (UINT32)0xf46eeddc;
+ m_power10BigNumTable[5].m_blocks[25] = (UINT32)0x5fdcefce;
+ m_power10BigNumTable[5].m_blocks[26] = (UINT32)0x000553f7;
+ }
+ } m_initializer;
+
+ UINT32 m_blocks[BIGSIZE];
+ UINT32 m_len;
+};
+
+
+#endif
diff --git a/src/classlibnative/bcltype/number.cpp b/src/classlibnative/bcltype/number.cpp
index 34169a77ed..a4497a50d3 100644
--- a/src/classlibnative/bcltype/number.cpp
+++ b/src/classlibnative/bcltype/number.cpp
@@ -12,6 +12,7 @@
#include "number.h"
#include "string.h"
#include "decimal.h"
+#include "bignum.h"
#include <stdlib.h>
typedef wchar_t wchar;
@@ -134,8 +135,288 @@ unsigned int Int64DivMod1E9(unsigned __int64* value)
#else // _TARGET_X86_ && !FEATURE_PAL
-#pragma warning(disable:4273)
-extern "C" char* __cdecl _ecvt(double, int, int*, int*);
+// Convert a double value to a NUMBER struct.
+//
+// 1. You should ensure the input value is not infinity or NaN.
+// 2. For 0.0, number->digits will be set as an empty string. i.e the value of the first bucket is 0.
+void DoubleToNumberWorker( double value, int count, int* dec, int* sign, wchar_t* digits )
+{
+ // ========================================================================================================================================
+ // This implementation is based on the paper: https://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf
+ // Besides the paper, some of the code and ideas are modified from http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
+ // You must read these two materials to fully understand the code.
+ //
+ // Note: we only support fixed format input.
+ // ========================================================================================================================================
+ //
+ // Overview:
+ //
+ // The input double number can be represented as:
+ // value = f * 2^e = r / s.
+ //
+ // f: the output mantissa. Note: f is not the 52 bits mantissa of the input double number.
+ // e: biased exponent.
+ // r: numerator.
+ // s: denominator.
+ // k: value = d0.d1d2 . . . dn * 10^k
+
+ _ASSERTE(dec != nullptr && sign != nullptr && digits != nullptr);
+
+ // The caller of DoubleToNumberWorker should already checked the Infinity and NAN values.
+ _ASSERTE(((FPDOUBLE*)&value)->exp != 0x7ff);
+
+ // Shortcut for zero.
+ if (value == 0.0)
+ {
+ *dec = 0;
+ *sign = 0;
+
+ // Instead of zeroing digits, we just make it as an empty string due to performance reason.
+ *digits = 0;
+
+ return;
+ }
+
+ // Step 1:
+ // Extract meta data from the input double value.
+ //
+ // Refer to IEEE double precision floating point format.
+ UINT64 f = 0;
+ int e = 0;
+ UINT32 mantissaHighBitIdx = 0;
+ if (((FPDOUBLE*)&value)->exp != 0)
+ {
+ // For normalized value, according to https://en.wikipedia.org/wiki/Double-precision_floating-point_format
+ // value = 1.fraction * 2^(exp - 1023)
+ // = (1 + mantissa / 2^52) * 2^(exp - 1023)
+ // = (2^52 + mantissa) * 2^(exp - 1023 - 52)
+ //
+ // So f = (2^52 + mantissa), e = exp - 1075;
+ f = ((UINT64)(((FPDOUBLE*)&value)->mantHi) << 32) | ((FPDOUBLE*)&value)->mantLo + ((UINT64)1 << 52);
+ e = ((FPDOUBLE*)&value)->exp - 1075;
+ mantissaHighBitIdx = 52;
+ }
+ else
+ {
+ // For denormalized value, according to https://en.wikipedia.org/wiki/Double-precision_floating-point_format
+ // value = 0.fraction * 2^(1 - 1023)
+ // = (mantissa / 2^52) * 2^(-1022)
+ // = mantissa * 2^(-1022 - 52)
+ // = mantissa * 2^(-1074)
+ // So f = mantissa, e = -1074
+ f = ((UINT64)(((FPDOUBLE*)&value)->mantHi) << 32) | ((FPDOUBLE*)&value)->mantLo;
+ e = -1074;
+ mantissaHighBitIdx = BigNum::LogBase2(f);
+ }
+
+ // Step 2:
+ // Estimate k. We'll verify it and fix any error later.
+ //
+ // This is an improvement of the estimation in the original paper.
+ // Inspired by http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
+ //
+ // LOG10V2 = 0.30102999566398119521373889472449
+ // DRIFT_FACTOR = 0.69 = 1 - log10V2 - epsilon (a small number account for drift of floating point multiplication)
+ int k = (int)(ceil(double((int)mantissaHighBitIdx + e) * LOG10V2 - DRIFT_FACTOR));
+
+ // Step 3:
+ // Store the input double value in BigNum format.
+ //
+ // To keep the precision, we represent the double value as r/s.
+ // We have several optimization based on following table in the paper.
+ //
+ // ----------------------------------------------------------------------------------------------------------
+ // | e >= 0 | e < 0 |
+ // ----------------------------------------------------------------------------------------------------------
+ // | f != b^(P - 1) | f = b^(P - 1) | e = min exp or f != b^(P - 1) | e > min exp and f = b^(P - 1) |
+ // --------------------------------------------------------------------------------------------------------------
+ // | r | f * b^e * 2 | f * b^(e + 1) * 2 | f * 2 | f * b * 2 |
+ // --------------------------------------------------------------------------------------------------------------
+ // | s | 2 | b * 2 | b^(-e) * 2 | b^(-e + 1) * 2 |
+ // --------------------------------------------------------------------------------------------------------------
+ //
+ // Note, we do not need m+ and m- because we only support fixed format input here.
+ // m+ and m- are used for free format input, which need to determine the exact range of values
+ // that would round to value when input so that we can generate the shortest correct digits.
+ //
+ // In our case, we just output digits until reaching the expected precision.
+ BigNum r(f);
+ BigNum s;
+ if (e >= 0)
+ {
+ // When f != b^(P - 1):
+ // r = f * b^e * 2
+ // s = 2
+ // value = r / s = f * b^e * 2 / 2 = f * b^e / 1
+ //
+ // When f = b^(P - 1):
+ // r = f * b^(e + 1) * 2
+ // s = b * 2
+ // value = r / s = f * b^(e + 1) * 2 / b * 2 = f * b^e / 1
+ //
+ // Therefore, we can simply say that when e >= 0:
+ // r = f * b^e = f * 2^e
+ // s = 1
+
+ r.ShiftLeft(e);
+ s.SetUInt64(1);
+ }
+ else
+ {
+ // When e = min exp or f != b^(P - 1):
+ // r = f * 2
+ // s = b^(-e) * 2
+ // value = r / s = f * 2 / b^(-e) * 2 = f / b^(-e)
+ //
+ // When e > min exp and f = b^(P - 1):
+ // r = f * b * 2
+ // s = b^(-e + 1) * 2
+ // value = r / s = f * b * 2 / b^(-e + 1) * 2 = f / b^(-e)
+ //
+ // Therefore, we can simply say that when e < 0:
+ // r = f
+ // s = b^(-e) = 2^(-e)
+
+ BigNum::ShiftLeft(1, -e, s);
+ }
+
+ // According to the paper, we should use k >= 0 instead of k > 0 here.
+ // However, if k = 0, both r and s won't be changed, we don't need to do any operation.
+ //
+ // Following are the Scheme code from the paper:
+ // --------------------------------------------------------------------------------
+ // (if (>= est 0)
+ // (fixup r (∗ s (exptt B est)) m+ m− est B low-ok? high-ok? )
+ // (let ([scale (exptt B (− est))])
+ // (fixup (∗ r scale) s (∗ m+ scale) (∗ m− scale) est B low-ok? high-ok? ))))
+ // --------------------------------------------------------------------------------
+ //
+ // If est is 0, (∗ s (exptt B est)) = s, (∗ r scale) = (* r (exptt B (− est)))) = r.
+ //
+ // So we just skip when k = 0.
+
+ if (k > 0)
+ {
+ BigNum poweredValue;
+ BigNum::Pow10(k, poweredValue);
+ s.Multiply(poweredValue);
+ }
+ else if (k < 0)
+ {
+ BigNum poweredValue;
+ BigNum::Pow10(-k, poweredValue);
+ r.Multiply(poweredValue);
+ }
+
+ if (BigNum::Compare(r, s) >= 0)
+ {
+ // The estimation was incorrect. Fix the error by increasing 1.
+ k += 1;
+ }
+ else
+ {
+ r.Multiply10();
+ }
+
+ *dec = k - 1;
+
+ // This the prerequisite of calling BigNum::HeuristicDivide().
+ BigNum::PrepareHeuristicDivide(&r, &s);
+
+ // Step 4:
+ // Calculate digits.
+ //
+ // Output digits until reaching the last but one precision or the numerator becomes zero.
+ int digitsNum = 0;
+ int currentDigit = 0;
+ while (true)
+ {
+ currentDigit = BigNum::HeuristicDivide(&r, s);
+ if (r.IsZero() || digitsNum + 1 == count)
+ {
+ break;
+ }
+
+ digits[digitsNum] = L'0' + currentDigit;
+ ++digitsNum;
+
+ r.Multiply10();
+ }
+
+ // Step 5:
+ // Set the last digit.
+ //
+ // We round to the closest digit by comparing value with 0.5:
+ // compare( value, 0.5 )
+ // = compare( r / s, 0.5 )
+ // = compare( r, 0.5 * s)
+ // = compare(2 * r, s)
+ // = compare(r << 1, s)
+ r.ShiftLeft(1);
+ int compareResult = BigNum::Compare(r, s);
+ bool isRoundDown = compareResult < 0;
+
+ // We are in the middle, round towards the even digit (i.e. IEEE rouding rules)
+ if (compareResult == 0)
+ {
+ isRoundDown = (currentDigit & 1) == 0;
+ }
+
+ if (isRoundDown)
+ {
+ digits[digitsNum] = L'0' + currentDigit;
+ ++digitsNum;
+ }
+ else
+ {
+ wchar_t* pCurDigit = digits + digitsNum;
+
+ // Rounding up for 9 is special.
+ if (currentDigit == 9)
+ {
+ // find the first non-nine prior digit
+ while (true)
+ {
+ // If we are at the first digit
+ if (pCurDigit == digits)
+ {
+ // Output 1 at the next highest exponent
+ *pCurDigit = L'1';
+ ++digitsNum;
+ *dec += 1;
+ break;
+ }
+
+ --pCurDigit;
+ --digitsNum;
+ if (*pCurDigit != L'9')
+ {
+ // increment the digit
+ *pCurDigit += 1;
+ ++digitsNum;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // It's simple if the digit is not 9.
+ *pCurDigit = L'0' + currentDigit + 1;
+ ++digitsNum;
+ }
+ }
+
+ while (digitsNum < count)
+ {
+ digits[digitsNum] = L'0';
+ ++digitsNum;
+ }
+
+ digits[count] = 0;
+
+ ++*dec;
+ *sign = ((FPDOUBLE*)&value)->sign;
+}
void DoubleToNumber(double value, int precision, NUMBER* number)
{
@@ -149,12 +430,7 @@ void DoubleToNumber(double value, int precision, NUMBER* number)
number->digits[0] = 0;
}
else {
- char* src = _ecvt(value, precision, &number->scale, &number->sign);
- wchar* dst = number->digits;
- if (*src != '0') {
- while (*src) *dst++ = *src++;
- }
- *dst = 0;
+ DoubleToNumberWorker(value, precision, &number->scale, &number->sign, number->digits);
}
}
diff --git a/src/classlibnative/bcltype/number.h b/src/classlibnative/bcltype/number.h
index db66435117..7b5efc4485 100644
--- a/src/classlibnative/bcltype/number.h
+++ b/src/classlibnative/bcltype/number.h
@@ -14,6 +14,11 @@
#define NUMBER_MAXDIGITS 50
+static const double LOG10V2 = 0.30102999566398119521373889472449;
+
+// DRIFT_FACTOR = 1 - LOG10V2 - epsilon (a small number account for drift of floating point multiplication)
+static const double DRIFT_FACTOR = 0.69;
+
struct NUMBER {
int precision;
int scale;
diff --git a/src/classlibnative/bcltype/stringnative.cpp b/src/classlibnative/bcltype/stringnative.cpp
index af6593a6be..34fbf1eb34 100644
--- a/src/classlibnative/bcltype/stringnative.cpp
+++ b/src/classlibnative/bcltype/stringnative.cpp
@@ -155,15 +155,13 @@ FCIMPL4(Object *, COMString::StringInitCharPtrPartial, StringObject *stringThis,
}
FCIMPLEND
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
-
inline COMNlsHashProvider * GetCurrentNlsHashProvider()
{
LIMITED_METHOD_CONTRACT;
return &COMNlsHashProvider::s_NlsHashProvider;
}
-FCIMPL3(INT32, COMString::Marvin32HashString, StringObject* thisRefUNSAFE, INT32 strLen, INT64 additionalEntropy) {
+FCIMPL1(INT32, COMString::Marvin32HashString, StringObject* thisRefUNSAFE) {
FCALL_CONTRACT;
int iReturnHash = 0;
@@ -173,7 +171,7 @@ FCIMPL3(INT32, COMString::Marvin32HashString, StringObject* thisRefUNSAFE, INT32
}
BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException));
- iReturnHash = GetCurrentNlsHashProvider()->HashString(thisRefUNSAFE->GetBuffer(), thisRefUNSAFE->GetStringLength(), TRUE, additionalEntropy);
+ iReturnHash = GetCurrentNlsHashProvider()->HashString(thisRefUNSAFE->GetBuffer(), thisRefUNSAFE->GetStringLength());
END_SO_INTOLERANT_CODE;
FC_GC_POLL_RET();
@@ -182,21 +180,6 @@ FCIMPL3(INT32, COMString::Marvin32HashString, StringObject* thisRefUNSAFE, INT32
}
FCIMPLEND
-BOOL QCALLTYPE COMString::UseRandomizedHashing() {
- QCALL_CONTRACT;
-
- BOOL bUseRandomizedHashing = FALSE;
-
- BEGIN_QCALL;
-
- bUseRandomizedHashing = GetCurrentNlsHashProvider()->GetUseRandomHashing();
-
- END_QCALL;
-
- return bUseRandomizedHashing;
-}
-#endif
-
/*===============================IsFastSort===============================
**Action: Call the helper to walk the string and see if we have any high chars.
**Returns: void. The appropriate bits are set on the String.
@@ -327,8 +310,6 @@ FCIMPL4(INT32, COMString::IndexOfCharArray, StringObject* thisRef, CHARArray* va
if (thisRef == NULL)
FCThrow(kNullReferenceException);
- if (valueRef == NULL)
- FCThrowArgumentNull(W("anyOf"));
WCHAR *thisChars;
WCHAR *valueChars;
@@ -337,14 +318,6 @@ FCIMPL4(INT32, COMString::IndexOfCharArray, StringObject* thisRef, CHARArray* va
thisRef->RefInterpretGetStringValuesDangerousForGC(&thisChars, &thisLength);
- if (startIndex < 0 || startIndex > thisLength) {
- FCThrowArgumentOutOfRange(W("startIndex"), W("ArgumentOutOfRange_Index"));
- }
-
- if (count < 0 || count > thisLength - startIndex) {
- FCThrowArgumentOutOfRange(W("count"), W("ArgumentOutOfRange_Count"));
- }
-
int endIndex = startIndex + count;
valueLength = valueRef->GetNumComponents();
@@ -494,19 +467,31 @@ void InitializeProbabilisticMap(int* charMap, __in_ecount(length) const WCHAR* c
_ASSERTE(charArray != NULL);
_ASSERTE(length >= 0);
+ bool hasAscii = false;
+
for(int i = 0; i < length; ++i) {
int hi,lo;
- WCHAR c = charArray[i];
+ int c = charArray[i];
- hi = (c >> 8) & 0xFF;
lo = c & 0xFF;
+ hi = (c >> 8) & 0xFF;
int* value = &charMap[lo & PROBABILISTICMAP_BLOCK_INDEX_MASK];
SetBit(value, lo >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT);
- value = &charMap[hi & PROBABILISTICMAP_BLOCK_INDEX_MASK];
- SetBit(value, hi >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT);
+ if (hi > 0) {
+ value = &charMap[hi & PROBABILISTICMAP_BLOCK_INDEX_MASK];
+ SetBit(value, hi >> PROBABILISTICMAP_BLOCK_INDEX_SHIFT);
+ }
+ else {
+ hasAscii = true;
+ }
+ }
+
+ if (hasAscii) {
+ // Common to search for ASCII symbols. Just the high value once.
+ charMap[0] |= 1;
}
}
diff --git a/src/classlibnative/bcltype/stringnative.h b/src/classlibnative/bcltype/stringnative.h
index a4d962df6d..a8409826c6 100644
--- a/src/classlibnative/bcltype/stringnative.h
+++ b/src/classlibnative/bcltype/stringnative.h
@@ -85,10 +85,7 @@ public:
static FCDECL2(VOID, FCSetTrailByte, StringObject* thisRefUNSAFE, UINT8 bData);
#endif // FEATURE_COMINTEROP
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
- static FCDECL3(INT32, Marvin32HashString, StringObject* thisRefUNSAFE, INT32 strLen, INT64 additionalEntropy);
- static BOOL QCALLTYPE UseRandomizedHashing();
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
+ static FCDECL1(INT32, Marvin32HashString, StringObject* thisRefUNSAFE);
};
diff --git a/src/classlibnative/bcltype/system.cpp b/src/classlibnative/bcltype/system.cpp
index f6a19f97d1..4e13bd1876 100644
--- a/src/classlibnative/bcltype/system.cpp
+++ b/src/classlibnative/bcltype/system.cpp
@@ -369,6 +369,13 @@ INT32 QCALLTYPE SystemNative::GetProcessorCount()
processorCount = systemInfo.dwNumberOfProcessors;
}
+#ifdef FEATURE_PAL
+ uint32_t cpuLimit;
+
+ if (PAL_GetCpuLimit(&cpuLimit) && cpuLimit < processorCount)
+ processorCount = cpuLimit;
+#endif
+
END_QCALL;
return processorCount;
diff --git a/src/classlibnative/inc/nlsinfo.h b/src/classlibnative/inc/nlsinfo.h
index 505827bc90..0593a4f034 100644
--- a/src/classlibnative/inc/nlsinfo.h
+++ b/src/classlibnative/inc/nlsinfo.h
@@ -54,7 +54,7 @@ public:
// Native helper functions for CultureData
//
- static INT32 QCALLTYPE InternalGetGlobalizedHashCode(INT_PTR handle, LPCWSTR localeName, LPCWSTR pString, INT32 length, INT32 dwFlagsIn, INT64 additionalEntropy);
+ static INT32 QCALLTYPE InternalGetGlobalizedHashCode(INT_PTR handle, LPCWSTR localeName, LPCWSTR pString, INT32 length, INT32 dwFlagsIn);
//
// Native helper function for methods in EncodingTable
diff --git a/src/classlibnative/nls/nlsinfo.cpp b/src/classlibnative/nls/nlsinfo.cpp
index fa288c0e0a..053bb14a65 100644
--- a/src/classlibnative/nls/nlsinfo.cpp
+++ b/src/classlibnative/nls/nlsinfo.cpp
@@ -91,7 +91,7 @@ INT32 COMNlsInfo::CallGetUserDefaultUILanguage()
// InternalGetGlobalizedHashCode
//
////////////////////////////////////////////////////////////////////////////
-INT32 QCALLTYPE COMNlsInfo::InternalGetGlobalizedHashCode(INT_PTR handle, LPCWSTR localeName, LPCWSTR string, INT32 length, INT32 dwFlagsIn, INT64 additionalEntropy)
+INT32 QCALLTYPE COMNlsInfo::InternalGetGlobalizedHashCode(INT_PTR handle, LPCWSTR localeName, LPCWSTR string, INT32 length, INT32 dwFlagsIn)
{
CONTRACTL
{
@@ -140,7 +140,7 @@ INT32 QCALLTYPE COMNlsInfo::InternalGetGlobalizedHashCode(INT_PTR handle, LPCWST
NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, (LPWSTR)pByte, byteCount, NULL,NULL, (LPARAM) handle);
}
- iReturnHash = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pByte, byteCount, true, additionalEntropy);
+ iReturnHash = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pByte, byteCount);
}
END_QCALL;
return(iReturnHash);
diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp
index a9e3b66f3b..814aac9fc4 100644
--- a/src/coreclr/hosts/corerun/corerun.cpp
+++ b/src/coreclr/hosts/corerun/corerun.cpp
@@ -402,7 +402,7 @@ bool TryRun(const int argc, const wchar_t* argv[], Logger &log, const bool verbo
appPathPtr = appPath.OpenUnicodeBuffer(size - 1);
length = WszGetFullPathName(exeName, size, appPathPtr, &filePart);
}
- if (length == 0 || length >= size) {
+ if (length == 0 || length >= size || filePart == NULL) {
log << W("Failed to get full path: ") << exeName << Logger::endl;
log << W("Error code: ") << GetLastError() << Logger::endl;
return false;
diff --git a/src/coreclr/hosts/corerun/logger.cpp b/src/coreclr/hosts/corerun/logger.cpp
index 95eb7c9561..70716258ab 100644
--- a/src/coreclr/hosts/corerun/logger.cpp
+++ b/src/coreclr/hosts/corerun/logger.cpp
@@ -88,7 +88,7 @@ void PrintAsHResult(int val) {
case 0x80131416: str = W("CORSEC_E_POLICY_EXCEPTION"); break;
case 0x80131417: str = W("CORSEC_E_MIN_GRANT_FAIL"); break;
case 0x80131418: str = W("CORSEC_E_NO_EXEC_PERM"); break;
- //case 0x80131418: str = W("CORSEC_E_XMLSYNTAX"); break;
+ //case 0x80131419: str = W("CORSEC_E_XMLSYNTAX"); break;
case 0x80131430: str = W("CORSEC_E_CRYPTO"); break;
case 0x80131431: str = W("CORSEC_E_CRYPTO_UNEX_OPER"); break;
case 0x80131500: str = W("COR_E_EXCEPTION"); break;
diff --git a/src/corefx/System.Globalization.Native/collation.cpp b/src/corefx/System.Globalization.Native/collation.cpp
index a8c24e16ad..3aefae2a6a 100644
--- a/src/corefx/System.Globalization.Native/collation.cpp
+++ b/src/corefx/System.Globalization.Native/collation.cpp
@@ -327,12 +327,6 @@ bool CanIgnoreAllCollationElements(const UCollator* pColl, const UChar* lpStr, i
}
-extern "C" int32_t GlobalizationNative_GetSortVersion()
-{
- // we didn't use UCOL_TAILORINGS_VERSION because it is deprecated in ICU v5
- return UCOL_RUNTIME_VERSION << 16 | UCOL_BUILDER_VERSION;
-}
-
extern "C" ResultCode GlobalizationNative_GetSortHandle(const char* lpLocaleName, SortHandle** ppSortHandle)
{
assert(ppSortHandle != nullptr);
@@ -407,6 +401,26 @@ const UCollator* GetCollatorFromSortHandle(SortHandle* pSortHandle, int32_t opti
return pCollator;
}
+extern "C" int32_t GlobalizationNative_GetSortVersion(SortHandle* pSortHandle)
+{
+ UErrorCode err = U_ZERO_ERROR;
+ const UCollator* pColl = GetCollatorFromSortHandle(pSortHandle, 0, &err);
+ int32_t result = 0;
+
+ if (U_SUCCESS(err))
+ {
+ ucol_getVersion(pColl, (uint8_t *) &result);
+ }
+ else
+ {
+ assert(false && "Unexpected ucol_getVersion to fail.");
+
+ // we didn't use UCOL_TAILORINGS_VERSION because it is deprecated in ICU v5
+ result = UCOL_RUNTIME_VERSION << 16 | UCOL_BUILDER_VERSION;
+ }
+ return result;
+}
+
/*
Function:
CompareString
diff --git a/src/corefx/System.Globalization.Native/icushim.cpp b/src/corefx/System.Globalization.Native/icushim.cpp
index 6e7817e85f..e97896a205 100644
--- a/src/corefx/System.Globalization.Native/icushim.cpp
+++ b/src/corefx/System.Globalization.Native/icushim.cpp
@@ -270,6 +270,15 @@ extern "C" int32_t GlobalizationNative_LoadICU()
return 1;
}
+// GlobalizationNative_GetICUVersion
+// return the current loaded ICU version
+extern "C" int32_t GlobalizationNative_GetICUVersion()
+{
+ int32_t version;
+ u_getVersion((uint8_t *) &version);
+ return version;
+}
+
__attribute__((destructor))
void ShutdownICUShim()
{
diff --git a/src/corefx/System.Globalization.Native/icushim.h b/src/corefx/System.Globalization.Native/icushim.h
index 313002ebb6..d7ec2fb05c 100644
--- a/src/corefx/System.Globalization.Native/icushim.h
+++ b/src/corefx/System.Globalization.Native/icushim.h
@@ -38,6 +38,7 @@
// List of all functions from the ICU libraries that are used in the System.Globalization.Native.so
#define FOR_ALL_UNCONDITIONAL_ICU_FUNCTIONS \
PER_FUNCTION_BLOCK(u_charsToUChars, libicuuc) \
+ PER_FUNCTION_BLOCK(u_getVersion, libicuuc) \
PER_FUNCTION_BLOCK(u_strlen, libicuuc) \
PER_FUNCTION_BLOCK(u_strncpy, libicuuc) \
PER_FUNCTION_BLOCK(u_tolower, libicuuc) \
@@ -56,6 +57,7 @@
PER_FUNCTION_BLOCK(ucol_getRules, libicui18n) \
PER_FUNCTION_BLOCK(ucol_getSortKey, libicui18n) \
PER_FUNCTION_BLOCK(ucol_getStrength, libicui18n) \
+ PER_FUNCTION_BLOCK(ucol_getVersion, libicui18n) \
PER_FUNCTION_BLOCK(ucol_next, libicui18n) \
PER_FUNCTION_BLOCK(ucol_open, libicui18n) \
PER_FUNCTION_BLOCK(ucol_openElements, libicui18n) \
@@ -143,6 +145,7 @@ FOR_ALL_ICU_FUNCTIONS
// Redefine all calls to ICU functions as calls through pointers that are set
// to the functions of the selected version of ICU in the initialization.
#define u_charsToUChars(...) u_charsToUChars_ptr(__VA_ARGS__)
+#define u_getVersion(...) u_getVersion_ptr(__VA_ARGS__)
#define u_strlen(...) u_strlen_ptr(__VA_ARGS__)
#define u_strncpy(...) u_strncpy_ptr(__VA_ARGS__)
#define u_tolower(...) u_tolower_ptr(__VA_ARGS__)
@@ -161,6 +164,7 @@ FOR_ALL_ICU_FUNCTIONS
#define ucol_getRules(...) ucol_getRules_ptr(__VA_ARGS__)
#define ucol_getSortKey(...) ucol_getSortKey_ptr(__VA_ARGS__)
#define ucol_getStrength(...) ucol_getStrength_ptr(__VA_ARGS__)
+#define ucol_getVersion(...) ucol_getVersion_ptr(__VA_ARGS__)
#define ucol_next(...) ucol_next_ptr(__VA_ARGS__)
#define ucol_open(...) ucol_open_ptr(__VA_ARGS__)
#define ucol_openElements(...) ucol_openElements_ptr(__VA_ARGS__)
diff --git a/src/debug/SetDebugTargetCoreSysARM.props b/src/debug/SetDebugTargetCoreSysARM.props
deleted file mode 100644
index cedb5aaf32..0000000000
--- a/src/debug/SetDebugTargetCoreSysARM.props
+++ /dev/null
@@ -1,25 +0,0 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <!--*****************************************************-->
- <!--This MSBuild project file was automatically generated-->
- <!--from the original SOURCES/DIRS file by the KBC tool.-->
- <!--*****************************************************-->
- <!--Import the settings-->
- <!--Leaf project Properties-->
- <PropertyGroup>
- <FeatureDbgipcTransportDI>true</FeatureDbgipcTransportDI>
- <FeatureDbgipcTransportVM>true</FeatureDbgipcTransportVM>
- <FeatureIpcman>false</FeatureIpcman>
- <FeatureInteropDebugging>false</FeatureInteropDebugging>
-
- <DbgTargetARM>true</DbgTargetARM>
- <CDefines>$(CDefines);DBG_TARGET_ARM=1</CDefines>
-
- <DbgTargetCoreSystem>true</DbgTargetCoreSystem>
- <CDefines>$(CDefines);DBG_TARGET_CORESYSTEM=1</CDefines>
-
- <DbgTargetSameEndiannes Condition="'$(BuildArchitecture)' == 'i386' or '$(BuildProjectName)' == 'CoreCLR' or '$(BuildProjectName)' == 'CoreSys'">true</DbgTargetSameEndiannes>
- <CDefines Condition="'$(BuildArchitecture)' == 'i386' or '$(BuildProjectName)' == 'CoreCLR' or '$(BuildProjectName)' == 'CoreSys'">$(CDefines);DBG_TARGET_SAME_ENDIANNESS=1</CDefines>
- </PropertyGroup>
- <!--Leaf Project Items-->
- <!--Import the targets-->
-</Project>
diff --git a/src/debug/SetDebugTargetCoreSysX86.props b/src/debug/SetDebugTargetCoreSysX86.props
deleted file mode 100644
index 594016a0d7..0000000000
--- a/src/debug/SetDebugTargetCoreSysX86.props
+++ /dev/null
@@ -1,23 +0,0 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <!--*****************************************************-->
- <!--This MSBuild project file was automatically generated-->
- <!--from the original SOURCES/DIRS file by the KBC tool.-->
- <!--*****************************************************-->
- <!--Import the settings-->
- <!--Leaf project Properties-->
- <PropertyGroup>
- <FeatureDbgipcTransportDI>true</FeatureDbgipcTransportDI>
- <FeatureDbgipcTransportVM>true</FeatureDbgipcTransportVM>
- <FeatureIpcman>false</FeatureIpcman>
- <FeatureInteropDebugging>false</FeatureInteropDebugging>
-
- <DbgTargetX86>true</DbgTargetX86>
- <CDefines>$(CDefines);DBG_TARGET_X86=1</CDefines>
-
- <DbgTargetCoreSystem>true</DbgTargetCoreSystem>
- <CDefines>$(CDefines);DBG_TARGET_CORESYSTEM=1</CDefines>
-
- </PropertyGroup>
- <!--Leaf Project Items-->
- <!--Import the targets-->
-</Project>
diff --git a/src/debug/SetDebugTargetLocal.props b/src/debug/SetDebugTargetLocal.props
deleted file mode 100644
index 28665150b8..0000000000
--- a/src/debug/SetDebugTargetLocal.props
+++ /dev/null
@@ -1,30 +0,0 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <!--*****************************************************-->
- <!--This MSBuild project file was automatically generated-->
- <!--from the original SOURCES/DIRS file by the KBC tool.-->
- <!--*****************************************************-->
- <!--Import the settings-->
- <!--Leaf project Properties-->
- <PropertyGroup>
- <DbgTargetX86 Condition="('$(BuildArchitecture)' == 'i386' or '$(_BuildArch)'=='rotor_x86') and ('$(CrossTargetArchitecture)'=='' or '$(CrossTargetArchitecture)' == 'i386')">1</DbgTargetX86>
- <CDefines Condition="('$(BuildArchitecture)' == 'i386' or '$(_BuildArch)'=='rotor_x86') and ('$(CrossTargetArchitecture)'=='' or '$(CrossTargetArchitecture)' == 'i386')">$(CDefines);DBG_TARGET_X86=1</CDefines>
-
- <DbgTargetAmd64 Condition="'$(BuildArchitecture)' == 'amd64' and ('$(CrossTargetArchitecture)'=='' or '$(CrossTargetArchitecture)' == 'amd64')">1</DbgTargetAmd64>
- <CDefines Condition="'$(BuildArchitecture)' == 'amd64' and ('$(CrossTargetArchitecture)'=='' or '$(CrossTargetArchitecture)' == 'amd64')">$(CDefines);DBG_TARGET_AMD64=1</CDefines>
-
- <DbgTargetArm Condition="'$(BuildArchitecture)' == 'arm' Or '$(CrossTargetArchitecture)' == 'arm'">1</DbgTargetArm>
- <CDefines Condition="'$(BuildArchitecture)' == 'arm' Or '$(CrossTargetArchitecture)' == 'arm'">$(CDefines);DBG_TARGET_ARM=1;</CDefines>
-
- <DbgTargetArm64 Condition="'$(BuildArchitecture)' == 'arm64' Or '$(CrossTargetArchitecture)' == 'arm64'">1</DbgTargetArm64>
- <CDefines Condition="'$(BuildArchitecture)' == 'arm64' Or '$(CrossTargetArchitecture)' == 'arm64'">$(CDefines);DBG_TARGET_ARM64=1;</CDefines>
-
- <DbgTargetWin64 Condition="'$(DbgTargetAmd64)' == '1' Or'$(DbgTargetArm64)' == '1'">1</DbgTargetWin64>
- <CDefines Condition="'$(DbgTargetAmd64)' == '1' Or'$(DbgTargetArm64)' == '1' ">$(CDefines);DBG_TARGET_WIN64=1</CDefines>
-
- <DbgTarget64bit Condition="'$(DbgTargetWin64)' == '1'">1</DbgTarget64bit>
- <CDefines Condition="'$(DbgTargetWin64)' == '1'">$(CDefines);DBG_TARGET_64BIT=1</CDefines>
-
- </PropertyGroup>
- <!--Leaf Project Items-->
- <!--Import the targets-->
-</Project>
diff --git a/src/debug/SetDebugTargetWinARM.props b/src/debug/SetDebugTargetWinARM.props
deleted file mode 100644
index 02240f12ca..0000000000
--- a/src/debug/SetDebugTargetWinARM.props
+++ /dev/null
@@ -1,19 +0,0 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <!--*****************************************************-->
- <!--This MSBuild project file was automatically generated-->
- <!--from the original SOURCES/DIRS file by the KBC tool.-->
- <!--*****************************************************-->
- <!--Import the settings-->
- <!--Leaf project Properties-->
- <PropertyGroup>
- <FeatureDbgipcTransportDI>false</FeatureDbgipcTransportDI>
- <FeatureDbgipcTransportVM>false</FeatureDbgipcTransportVM>
- <FeatureIpcman>false</FeatureIpcman>
- <FeatureInteropDebugging>false</FeatureInteropDebugging>
-
- <DbgTargetArm>true</DbgTargetArm>
- <CDefines>$(CDefines);DBG_TARGET_ARM=1</CDefines>
- </PropertyGroup>
- <!--Leaf Project Items-->
- <!--Import the targets-->
-</Project>
diff --git a/src/debug/SetDebugTargetWinx86.props b/src/debug/SetDebugTargetWinx86.props
deleted file mode 100644
index bd90a410ab..0000000000
--- a/src/debug/SetDebugTargetWinx86.props
+++ /dev/null
@@ -1,8 +0,0 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <PropertyGroup>
-
- <DbgTargetX86>1</DbgTargetX86>
- <CDefines>$(CDefines);DBG_TARGET_X86=1</CDefines>
-
- </PropertyGroup>
-</Project>
diff --git a/src/debug/XPlatCommon.props b/src/debug/XPlatCommon.props
deleted file mode 100644
index e7913f8380..0000000000
--- a/src/debug/XPlatCommon.props
+++ /dev/null
@@ -1,41 +0,0 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-
- <!--Usage: optionally import a clr\xplat\SetHostXXX.props or SetTargetXXX.props first, then this props file -->
-
-
- <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\utility.props"/>
-
- <PropertyGroup>
-
- <!-- Is DBI based OOP debugging supported -->
- <FeatureDbiOopDebugging Condition="'$(XPlatHostBuildDir)'=='HostWinx86'">$(FeatureDbiOopDebugging_HostWindowsx86)</FeatureDbiOopDebugging>
- <FeatureDbiOopDebugging Condition="'$(XPlatHostBuildDir)'=='HostOneCorex86'">$(FeatureDbiOopDebugging_HostOneCorex86)</FeatureDbiOopDebugging>
- <FeatureDbiOopDebugging Condition="'$(XPlatHostBuildDir)'=='HostWinAmd64'">$(FeatureDbiOopDebugging_HostWindowsamd64)</FeatureDbiOopDebugging>
- <FeatureDbiOopDebugging Condition="'$(XPlatHostBuildDir)'=='HostOneCoreAmd64'">$(FeatureDbiOopDebugging_HostOneCoreamd64)</FeatureDbiOopDebugging>
- <FeatureDbiOopDebugging Condition="'$(XPlatHostBuildDir)'=='HostLocal'">$(FeatureDbiOopDebugging_HostLocal)</FeatureDbiOopDebugging>
-
- </PropertyGroup>
-
- <!-- Additional properties used when DBI based OOP debugging is used -->
- <PropertyGroup Condition="'$(FeatureDbiOopDebugging)'=='true'">
-
- <!-- These parameters set up the naming convention for CLRDEBUGINFO resources that are added to clr.dll, these resources have PE information for specific builds
- of DAC and DBI. The naming scheme started with CLRDEBUGINFO only, but with the advent of x-platform debugging had to be expanded. It is now the general form
- CLRDEBUGINFO$(DebugInfoResourceHostOS)$(DebugInfoResourceHostArch). To keep back-compat if host==target and target is windows that maps to an empty suffix. These values must be
- in all capital letters because of weird restrictions on win32 resource names -->
- <DebugInfoResourceHostOS>UNKNOWN</DebugInfoResourceHostOS>
- <DebugInfoResourceHostOS Condition="'$(HostMachineOS)'=='windows'">WINDOWS</DebugInfoResourceHostOS>
- <DebugInfoResourceHostOS Condition="'$(HostMachineOS)'=='OneCore'">CORESYS</DebugInfoResourceHostOS>
-
- <DebugInfoResourceHostArch>UNKNOWN</DebugInfoResourceHostArch>
- <DebugInfoResourceHostArch Condition="'$(HostMachineArch)'=='x86'">X86</DebugInfoResourceHostArch>
- <DebugInfoResourceHostArch Condition="'$(HostMachineArch)'=='arm'">ARM</DebugInfoResourceHostArch>
- <DebugInfoResourceHostArch Condition="'$(HostMachineArch)'=='amd64'">AMD64</DebugInfoResourceHostArch>
-
- <DebugInfoResourceSuffixValid Condition="'$(DebugInfoResourceHostArch)'!='UNKNOWN' and '$(DebugInfoResourceHostOS)'!='UNKNOWN'">true</DebugInfoResourceSuffixValid>
- <DebugInfoResourceSuffix Condition=" '$(TargetMachineOS)'=='windows' and '$(XPlatHostMatchesEnvironment)'=='true' "></DebugInfoResourceSuffix>
- <DebugInfoResourceSuffix Condition="!('$(TargetMachineOS)'=='windows' and '$(XPlatHostMatchesEnvironment)'=='true')">$(DebugInfoResourceHostOS)$(DebugInfoResourceHostArch)</DebugInfoResourceSuffix>
-
- </PropertyGroup>
-
-</Project>
diff --git a/src/debug/createdump/crashinfo.cpp b/src/debug/createdump/crashinfo.cpp
index d9642b309a..ae3e70f461 100644
--- a/src/debug/createdump/crashinfo.cpp
+++ b/src/debug/createdump/crashinfo.cpp
@@ -4,6 +4,9 @@
#include "createdump.h"
+// This is for the PAL_VirtualUnwindOutOfProc read memory adapter.
+CrashInfo* g_crashInfo;
+
CrashInfo::CrashInfo(pid_t pid, ICLRDataTarget* dataTarget, bool sos) :
m_ref(1),
m_pid(pid),
@@ -12,6 +15,7 @@ CrashInfo::CrashInfo(pid_t pid, ICLRDataTarget* dataTarget, bool sos) :
m_sos(sos),
m_dataTarget(dataTarget)
{
+ g_crashInfo = this;
dataTarget->AddRef();
m_auxvValues.fill(0);
}
@@ -57,7 +61,7 @@ CrashInfo::QueryInterface(
}
else
{
- *Interface = NULL;
+ *Interface = nullptr;
return E_NOINTERFACE;
}
}
@@ -172,6 +176,12 @@ CrashInfo::GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType)
{
return false;
}
+
+ for (const MemoryRegion& region : m_moduleAddresses)
+ {
+ region.Trace();
+ }
+
// If full memory dump, include everything regardless of permissions
if (minidumpType & MiniDumpWithFullMemory)
{
@@ -205,15 +215,8 @@ CrashInfo::GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType)
// Add the thread's stack and some code memory to core
for (ThreadInfo* thread : m_threads)
{
- uint64_t start;
- size_t size;
-
- // Add the thread's stack and some of the code
- thread->GetThreadStack(*this, &start, &size);
- InsertMemoryRegion(start, size);
-
- thread->GetThreadCode(&start, &size);
- InsertMemoryRegion(start, size);
+ // Add the thread's stack
+ thread->GetThreadStack(*this);
}
// All the regions added so far has been backed by memory. Now add the rest of
// mappings so the debuggers like lldb see that an address is code (PF_X) even
@@ -302,7 +305,7 @@ CrashInfo::EnumerateModuleMappings()
// 35b1dac000-35b1fac000 ---p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so
// 35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so
// 35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870 /usr/lib64/libc-2.15.so
- char* line = NULL;
+ char* line = nullptr;
size_t lineLen = 0;
int count = 0;
ssize_t read;
@@ -313,7 +316,7 @@ CrashInfo::EnumerateModuleMappings()
assert(chars > 0 && chars <= sizeof(mapPath));
FILE* mapsFile = fopen(mapPath, "r");
- if (mapsFile == NULL)
+ if (mapsFile == nullptr)
{
fprintf(stderr, "fopen(%s) FAILED %s\n", mapPath, strerror(errno));
return false;
@@ -408,7 +411,8 @@ CrashInfo::GetDSOInfo()
if (phnum <= 0 || phdrAddr == nullptr) {
return false;
}
- TRACE("DSO: phdr %p phnum %d\n", phdrAddr, phnum);
+ uint64_t baseAddress = (uint64_t)phdrAddr - sizeof(Ehdr);
+ TRACE("DSO: base %" PRIA PRIx64 " phdr %p phnum %d\n", baseAddress, phdrAddr, phnum);
// Search for the program PT_DYNAMIC header
ElfW(Dyn)* dynamicAddr = nullptr;
@@ -422,16 +426,23 @@ CrashInfo::GetDSOInfo()
TRACE("DSO: phdr %p type %d (%x) vaddr %" PRIxA " memsz %" PRIxA " offset %" PRIxA "\n",
phdrAddr, ph.p_type, ph.p_type, ph.p_vaddr, ph.p_memsz, ph.p_offset);
- if (ph.p_type == PT_DYNAMIC)
+ switch (ph.p_type)
{
+ case PT_DYNAMIC:
dynamicAddr = reinterpret_cast<ElfW(Dyn)*>(ph.p_vaddr);
- }
- else if (ph.p_type == PT_NOTE || ph.p_type == PT_GNU_EH_FRAME)
- {
- if (ph.p_vaddr != 0 && ph.p_memsz != 0)
- {
+ break;
+
+ case PT_NOTE:
+ case PT_GNU_EH_FRAME:
+ if (ph.p_vaddr != 0 && ph.p_memsz != 0) {
InsertMemoryRegion(ph.p_vaddr, ph.p_memsz);
}
+ break;
+
+ case PT_LOAD:
+ MemoryRegion region(0, ph.p_vaddr, ph.p_vaddr + ph.p_memsz, baseAddress);
+ m_moduleAddresses.insert(region);
+ break;
}
}
@@ -500,58 +511,23 @@ CrashInfo::GetDSOInfo()
return true;
}
-inline bool
-NameCompare(const char* name, const char* sectionName)
-{
- return strncmp(name, sectionName, strlen(sectionName) + 1) == 0;
-}
-
-bool
-ValidShdr(const std::set<MemoryRegion>& mappings, uint64_t elfBaseAddr, Shdr *shdrAddr)
-{
- bool isValid = false;
- const char *moduleName = nullptr;
- for (const MemoryRegion& region : mappings)
- {
- if (elfBaseAddr == region.StartAddress())
- moduleName = region.FileName();
-
- if (!moduleName)
- continue;
-
- if ((uint64_t)shdrAddr < region.StartAddress() || (uint64_t)shdrAddr >= region.EndAddress())
- continue;
-
- if (!strcmp(region.FileName(), moduleName)) {
- isValid = true;
- break;
- }
- }
-
- return isValid;
-}
-
//
// Add all the necessary ELF headers to the core dump
//
bool
CrashInfo::GetELFInfo(uint64_t baseAddress)
{
- if (baseAddress == 0) {
+ if (baseAddress == 0 || baseAddress == m_auxvValues[AT_SYSINFO_EHDR] || baseAddress == m_auxvValues[AT_BASE]) {
return true;
}
Ehdr ehdr;
if (!ReadMemory((void*)baseAddress, &ehdr, sizeof(ehdr))) {
- fprintf(stderr, "ReadMemory(%p, %" PRIx ") ehdr FAILED\n", (void*)baseAddress, sizeof(ehdr));
- return false;
+ TRACE("ReadMemory(%p, %" PRIx ") ehdr FAILED\n", (void*)baseAddress, sizeof(ehdr));
+ return true;
}
int phnum = ehdr.e_phnum;
- int shnum = ehdr.e_shnum;
assert(phnum != PN_XNUM);
- assert(shnum != SHN_XINDEX);
- assert(ehdr.e_shstrndx != SHN_XINDEX);
assert(ehdr.e_phentsize == sizeof(Phdr));
- assert(ehdr.e_shentsize == sizeof(Shdr));
#ifdef BIT64
assert(ehdr.e_ident[EI_CLASS] == ELFCLASS64);
#else
@@ -560,7 +536,7 @@ CrashInfo::GetELFInfo(uint64_t baseAddress)
assert(ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
TRACE("ELF: type %d mach 0x%x ver %d flags 0x%x phnum %d phoff %" PRIxA " phentsize 0x%02x shnum %d shoff %" PRIxA " shentsize 0x%02x shstrndx %d\n",
- ehdr.e_type, ehdr.e_machine, ehdr.e_version, ehdr.e_flags, phnum, ehdr.e_phoff, ehdr.e_phentsize, shnum, ehdr.e_shoff, ehdr.e_shentsize, ehdr.e_shstrndx);
+ ehdr.e_type, ehdr.e_machine, ehdr.e_version, ehdr.e_flags, phnum, ehdr.e_phoff, ehdr.e_phentsize, ehdr.e_shnum, ehdr.e_shoff, ehdr.e_shentsize, ehdr.e_shstrndx);
if (ehdr.e_phoff != 0 && phnum > 0)
{
@@ -577,67 +553,20 @@ CrashInfo::GetELFInfo(uint64_t baseAddress)
TRACE("ELF: phdr %p type %d (%x) vaddr %" PRIxA " memsz %" PRIxA " paddr %" PRIxA " filesz %" PRIxA " offset %" PRIxA " align %" PRIxA "\n",
phdrAddr, ph.p_type, ph.p_type, ph.p_vaddr, ph.p_memsz, ph.p_paddr, ph.p_filesz, ph.p_offset, ph.p_align);
- if (ph.p_type == PT_DYNAMIC || ph.p_type == PT_NOTE || ph.p_type == PT_GNU_EH_FRAME)
+ switch (ph.p_type)
{
- if (ph.p_vaddr != 0 && ph.p_memsz != 0)
- {
+ case PT_DYNAMIC:
+ case PT_NOTE:
+ case PT_GNU_EH_FRAME:
+ if (ph.p_vaddr != 0 && ph.p_memsz != 0) {
InsertMemoryRegion(baseAddress + ph.p_vaddr, ph.p_memsz);
}
- }
- }
- }
+ break;
- // Skip the "interpreter" module i.e. /lib64/ld-linux-x86-64.so.2 or ld-2.19.so. The in-memory section headers are
- // not valid. Ignore all failures too because on debug builds of coreclr, the section headers are not in valid memory.
- if (baseAddress != m_auxvValues[AT_BASE] && ehdr.e_shoff != 0 && shnum > 0 && ehdr.e_shstrndx != SHN_UNDEF)
- {
- Shdr* shdrAddr = reinterpret_cast<Shdr*>(baseAddress + ehdr.e_shoff);
- Shdr* stringTableShdrAddr = shdrAddr + ehdr.e_shstrndx;
-
- // Check the section headers address. In some cases there isn't section header table in process memory.
- if ((!ValidShdr(m_moduleMappings, baseAddress, shdrAddr) && !ValidShdr(m_otherMappings, baseAddress, shdrAddr)) ||
- (!ValidShdr(m_moduleMappings, baseAddress, stringTableShdrAddr) && !ValidShdr(m_otherMappings, baseAddress, stringTableShdrAddr))) {
- TRACE("ELF: %2d shdr %p Invalid section headers table address\n", ehdr.e_shstrndx, shdrAddr);
- return true;
- }
-
- // Get the string table section header
- Shdr stringTableSectionHeader;
- if (!ReadMemory(stringTableShdrAddr, &stringTableSectionHeader, sizeof(stringTableSectionHeader))) {
- TRACE("ELF: %2d shdr %p ReadMemory string table section header FAILED\n", ehdr.e_shstrndx, stringTableShdrAddr);
- return true;
- }
-
- // Get the string table
- ArrayHolder<char> stringTable = new char[stringTableSectionHeader.sh_size];
- if (!ReadMemory((void*)(baseAddress + stringTableSectionHeader.sh_offset), stringTable.GetPtr(), stringTableSectionHeader.sh_size)) {
- TRACE("ELF: %2d shdr %p ReadMemory string table FAILED\n", ehdr.e_shstrndx, (void*)(baseAddress + stringTableSectionHeader.sh_offset));
- return true;
- }
- // Add the section headers to the core dump
- for (int sectionIndex = 0; sectionIndex < shnum; sectionIndex++, shdrAddr++)
- {
- Shdr sh;
- if (!ReadMemory(shdrAddr, &sh, sizeof(sh))) {
- TRACE("ELF: %2d shdr %p ReadMemory FAILED\n", sectionIndex, shdrAddr);
- return true;
- }
- TRACE("ELF: %2d shdr %p type %2d (%x) addr %" PRIxA " offset %" PRIxA " size %" PRIxA " link %08x info %08x name %4d %s\n",
- sectionIndex, shdrAddr, sh.sh_type, sh.sh_type, sh.sh_addr, sh.sh_offset, sh.sh_size, sh.sh_link, sh.sh_info, sh.sh_name, &stringTable[sh.sh_name]);
-
- if (sh.sh_name != SHN_UNDEF && sh.sh_offset > 0 && sh.sh_size > 0) {
- char* name = &stringTable[sh.sh_name];
-
- // Add the .eh_frame/.eh_frame_hdr unwind info to the core dump
- if (NameCompare(name, ".eh_frame") ||
- NameCompare(name, ".eh_frame_hdr") ||
- NameCompare(name, ".note.gnu.build-id") ||
- NameCompare(name, ".note.gnu.ABI-tag") ||
- NameCompare(name, ".gnu_debuglink"))
- {
- TRACE("ELF: %s %p size %" PRIxA "\n", name, (void*)(baseAddress + sh.sh_offset), sh.sh_size);
- InsertMemoryRegion(baseAddress + sh.sh_offset, sh.sh_size);
- }
+ case PT_LOAD:
+ MemoryRegion region(0, baseAddress + ph.p_vaddr, baseAddress + ph.p_vaddr + ph.p_memsz, baseAddress);
+ m_moduleAddresses.insert(region);
+ break;
}
}
}
@@ -652,8 +581,8 @@ bool
CrashInfo::EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE minidumpType)
{
PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr;
- ICLRDataEnumMemoryRegions* clrDataEnumRegions = nullptr;
- IXCLRDataProcess* clrDataProcess = nullptr;
+ ICLRDataEnumMemoryRegions* pClrDataEnumRegions = nullptr;
+ IXCLRDataProcess* pClrDataProcess = nullptr;
HMODULE hdac = nullptr;
HRESULT hr = S_OK;
bool result = false;
@@ -679,39 +608,43 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE
}
if ((minidumpType & MiniDumpWithFullMemory) == 0)
{
- hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), m_dataTarget, (void**)&clrDataEnumRegions);
+ hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), m_dataTarget, (void**)&pClrDataEnumRegions);
if (FAILED(hr))
{
fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr);
goto exit;
}
// Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC
- hr = clrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT);
+ hr = pClrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT);
if (FAILED(hr))
{
fprintf(stderr, "EnumMemoryRegions FAILED %08x\n", hr);
goto exit;
}
}
- hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), m_dataTarget, (void**)&clrDataProcess);
+ hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), m_dataTarget, (void**)&pClrDataProcess);
if (FAILED(hr))
{
fprintf(stderr, "CLRDataCreateInstance(IXCLRDataProcess) FAILED %08x\n", hr);
goto exit;
}
- if (!EnumerateManagedModules(clrDataProcess))
+ if (!EnumerateManagedModules(pClrDataProcess))
+ {
+ goto exit;
+ }
+ if (!UnwindAllThreads(pClrDataProcess))
{
goto exit;
}
result = true;
exit:
- if (clrDataEnumRegions != nullptr)
+ if (pClrDataEnumRegions != nullptr)
{
- clrDataEnumRegions->Release();
+ pClrDataEnumRegions->Release();
}
- if (clrDataProcess != nullptr)
+ if (pClrDataProcess != nullptr)
{
- clrDataProcess->Release();
+ pClrDataProcess->Release();
}
if (hdac != nullptr)
{
@@ -721,24 +654,29 @@ exit:
}
//
-// Enumerate all the managed modules and replace the module
-// mapping with the module name found.
+// Enumerate all the managed modules and replace the module mapping with the module name found.
//
bool
-CrashInfo::EnumerateManagedModules(IXCLRDataProcess* clrDataProcess)
+CrashInfo::EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess)
{
- IXCLRDataModule* clrDataModule = nullptr;
CLRDATA_ENUM enumModules = 0;
+ bool result = true;
HRESULT hr = S_OK;
- if (FAILED(hr = clrDataProcess->StartEnumModules(&enumModules))) {
+ if (FAILED(hr = pClrDataProcess->StartEnumModules(&enumModules))) {
fprintf(stderr, "StartEnumModules FAILED %08x\n", hr);
return false;
}
- while ((hr = clrDataProcess->EnumModule(&enumModules, &clrDataModule)) == S_OK)
+
+ while (true)
{
+ ReleaseHolder<IXCLRDataModule> pClrDataModule;
+ if ((hr = pClrDataProcess->EnumModule(&enumModules, &pClrDataModule)) != S_OK) {
+ break;
+ }
+
DacpGetModuleData moduleData;
- if (SUCCEEDED(hr = moduleData.Request(clrDataModule)))
+ if (SUCCEEDED(hr = moduleData.Request(pClrDataModule.GetPtr())))
{
TRACE("MODULE: %" PRIA PRIx64 " dyn %d inmem %d file %d pe %" PRIA PRIx64 " pdb %" PRIA PRIx64, moduleData.LoadedPEAddress, moduleData.IsDynamic,
moduleData.IsInMemory, moduleData.IsFileLayout, moduleData.PEFile, moduleData.InMemoryPdbAddress);
@@ -746,12 +684,13 @@ CrashInfo::EnumerateManagedModules(IXCLRDataProcess* clrDataProcess)
if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0)
{
ArrayHolder<WCHAR> wszUnicodeName = new WCHAR[MAX_LONGPATH + 1];
- if (SUCCEEDED(hr = clrDataModule->GetFileName(MAX_LONGPATH, NULL, wszUnicodeName)))
+ if (SUCCEEDED(hr = pClrDataModule->GetFileName(MAX_LONGPATH, nullptr, wszUnicodeName)))
{
char* pszName = (char*)malloc(MAX_LONGPATH + 1);
if (pszName == nullptr) {
fprintf(stderr, "Allocating module name FAILED\n");
- return false;
+ result = false;
+ break;
}
sprintf_s(pszName, MAX_LONGPATH, "%S", (WCHAR*)wszUnicodeName);
TRACE(" %s\n", pszName);
@@ -770,12 +709,27 @@ CrashInfo::EnumerateManagedModules(IXCLRDataProcess* clrDataProcess)
else {
TRACE("moduleData.Request FAILED %08x\n", hr);
}
- if (clrDataModule != nullptr) {
- clrDataModule->Release();
- }
}
+
if (enumModules != 0) {
- clrDataProcess->EndEnumModules(enumModules);
+ pClrDataProcess->EndEnumModules(enumModules);
+ }
+
+ return result;
+}
+
+//
+// Unwind all the native threads to ensure that the dwarf unwind info is added to the core dump.
+//
+bool
+CrashInfo::UnwindAllThreads(IXCLRDataProcess* pClrDataProcess)
+{
+ // For each native and managed thread
+ for (ThreadInfo* thread : m_threads)
+ {
+ if (!thread->UnwindThread(*this, pClrDataProcess)) {
+ return false;
+ }
}
return true;
}
@@ -819,6 +773,20 @@ CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, const char* pszName
}
//
+// Returns the module base address for the IP or 0.
+//
+uint64_t CrashInfo::GetBaseAddress(uint64_t ip)
+{
+ MemoryRegion search(0, ip, ip, 0);
+ const MemoryRegion* found = SearchMemoryRegions(m_moduleAddresses, search);
+ if (found == nullptr) {
+ return 0;
+ }
+ // The memory region Offset() is the base address of the module
+ return found->Offset();
+}
+
+//
// ReadMemory from target and add to memory regions list
//
bool
@@ -918,11 +886,12 @@ CrashInfo::InsertMemoryRegion(const MemoryRegion& region)
uint32_t
CrashInfo::GetMemoryRegionFlags(uint64_t start)
{
- const MemoryRegion* region = SearchMemoryRegions(m_moduleMappings, start);
+ MemoryRegion search(0, start, start + PAGE_SIZE);
+ const MemoryRegion* region = SearchMemoryRegions(m_moduleMappings, search);
if (region != nullptr) {
return region->Flags();
}
- region = SearchMemoryRegions(m_otherMappings, start);
+ region = SearchMemoryRegions(m_otherMappings, search);
if (region != nullptr) {
return region->Flags();
}
@@ -1012,12 +981,12 @@ CrashInfo::CombineMemoryRegions()
// Searches for a memory region given an address.
//
const MemoryRegion*
-CrashInfo::SearchMemoryRegions(const std::set<MemoryRegion>& regions, uint64_t start)
+CrashInfo::SearchMemoryRegions(const std::set<MemoryRegion>& regions, const MemoryRegion& search)
{
- std::set<MemoryRegion>::iterator found = regions.find(MemoryRegion(0, start, start + PAGE_SIZE));
+ std::set<MemoryRegion>::iterator found = regions.find(search);
for (; found != regions.end(); found++)
{
- if (start >= found->StartAddress() && start < found->EndAddress())
+ if (search.StartAddress() >= found->StartAddress() && search.StartAddress() < found->EndAddress())
{
return &*found;
}
diff --git a/src/debug/createdump/crashinfo.h b/src/debug/createdump/crashinfo.h
index 65f1d18730..d7c3f037c2 100644
--- a/src/debug/createdump/crashinfo.h
+++ b/src/debug/createdump/crashinfo.h
@@ -10,7 +10,7 @@ typedef Elf32_auxv_t elf_aux_entry;
#define PRId PRId32
#define PRIA "08"
#define PRIxA PRIA PRIx
-#elif defined(__x86_64) || defined(__aarch64__)
+#elif defined(__x86_64__) || defined(__aarch64__)
typedef Elf64_auxv_t elf_aux_entry;
#define PRIx PRIx64
#define PRIu PRIu64
@@ -40,6 +40,7 @@ private:
std::set<MemoryRegion> m_moduleMappings; // module memory mappings
std::set<MemoryRegion> m_otherMappings; // other memory mappings
std::set<MemoryRegion> m_memoryRegions; // memory regions from DAC, etc.
+ std::set<MemoryRegion> m_moduleAddresses; // memory region to module base address
public:
CrashInfo(pid_t pid, ICLRDataTarget* dataTarget, bool sos);
@@ -47,21 +48,24 @@ public:
bool EnumerateAndSuspendThreads();
bool GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType);
void ResumeThreads();
+ bool ReadMemory(void* address, void* buffer, size_t size);
+ uint64_t GetBaseAddress(uint64_t ip);
+ void InsertMemoryRegion(uint64_t address, size_t size);
static bool GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, char** name);
- static const MemoryRegion* SearchMemoryRegions(const std::set<MemoryRegion>& regions, uint64_t start);
+ static const MemoryRegion* SearchMemoryRegions(const std::set<MemoryRegion>& regions, const MemoryRegion& search);
- const pid_t Pid() const { return m_pid; }
- const pid_t Ppid() const { return m_ppid; }
- const pid_t Tgid() const { return m_tgid; }
- const char* Name() const { return m_name; }
- ICLRDataTarget* DataTarget() const { return m_dataTarget; }
+ inline const pid_t Pid() const { return m_pid; }
+ inline const pid_t Ppid() const { return m_ppid; }
+ inline const pid_t Tgid() const { return m_tgid; }
+ inline const char* Name() const { return m_name; }
+ inline ICLRDataTarget* DataTarget() const { return m_dataTarget; }
- const std::vector<ThreadInfo*> Threads() const { return m_threads; }
- const std::set<MemoryRegion> ModuleMappings() const { return m_moduleMappings; }
- const std::set<MemoryRegion> OtherMappings() const { return m_otherMappings; }
- const std::set<MemoryRegion> MemoryRegions() const { return m_memoryRegions; }
- const std::vector<elf_aux_entry> AuxvEntries() const { return m_auxvEntries; }
- const size_t GetAuxvSize() const { return m_auxvEntries.size() * sizeof(elf_aux_entry); }
+ inline const std::vector<ThreadInfo*> Threads() const { return m_threads; }
+ inline const std::set<MemoryRegion> ModuleMappings() const { return m_moduleMappings; }
+ inline const std::set<MemoryRegion> OtherMappings() const { return m_otherMappings; }
+ inline const std::set<MemoryRegion> MemoryRegions() const { return m_memoryRegions; }
+ inline const std::vector<elf_aux_entry> AuxvEntries() const { return m_auxvEntries; }
+ inline const size_t GetAuxvSize() const { return m_auxvEntries.size() * sizeof(elf_aux_entry); }
// IUnknown
STDMETHOD(QueryInterface)(___in REFIID InterfaceId, ___out PVOID* Interface);
@@ -77,11 +81,10 @@ private:
bool GetDSOInfo();
bool GetELFInfo(uint64_t baseAddress);
bool EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE minidumpType);
- bool EnumerateManagedModules(IXCLRDataProcess* clrDataProcess);
+ bool EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess);
+ bool UnwindAllThreads(IXCLRDataProcess* pClrDataProcess);
void ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, const char* pszName);
- bool ReadMemory(void* address, void* buffer, size_t size);
void InsertMemoryBackedRegion(const MemoryRegion& region);
- void InsertMemoryRegion(uint64_t address, size_t size);
void InsertMemoryRegion(const MemoryRegion& region);
uint32_t GetMemoryRegionFlags(uint64_t start);
bool ValidRegion(const MemoryRegion& region);
diff --git a/src/debug/createdump/datatarget.cpp b/src/debug/createdump/datatarget.cpp
index dec52c707f..9bbfcc7ec9 100644
--- a/src/debug/createdump/datatarget.cpp
+++ b/src/debug/createdump/datatarget.cpp
@@ -251,12 +251,3 @@ DumpDataTarget::Request(
assert(false);
return E_NOTIMPL;
}
-
-HRESULT STDMETHODCALLTYPE
-DumpDataTarget::VirtualUnwind(
- /* [in] */ DWORD threadId,
- /* [in] */ ULONG32 contextSize,
- /* [in, out, size_is(contextSize)] */ PBYTE context)
-{
- return E_NOTIMPL;
-}
diff --git a/src/debug/createdump/datatarget.h b/src/debug/createdump/datatarget.h
index 8ff6773fa3..f1698186ea 100644
--- a/src/debug/createdump/datatarget.h
+++ b/src/debug/createdump/datatarget.h
@@ -4,7 +4,7 @@
class CrashInfo;
-class DumpDataTarget : public ICLRDataTarget, ICorDebugDataTarget4
+class DumpDataTarget : public ICLRDataTarget
{
private:
LONG m_ref; // reference count
@@ -79,12 +79,4 @@ public:
/* [size_is][in] */ BYTE *inBuffer,
/* [in] */ ULONG32 outBufferSize,
/* [size_is][out] */ BYTE *outBuffer);
-
- //
- // ICorDebugDataTarget4
- //
- virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(
- /* [in] */ DWORD threadId,
- /* [in] */ ULONG32 contextSize,
- /* [in, out, size_is(contextSize)] */ PBYTE context);
};
diff --git a/src/debug/createdump/memoryregion.h b/src/debug/createdump/memoryregion.h
index e8e49aa149..fbdd9d0442 100644
--- a/src/debug/createdump/memoryregion.h
+++ b/src/debug/createdump/memoryregion.h
@@ -58,6 +58,18 @@ public:
assert((end & ~PAGE_MASK) == 0);
}
+ // This is a special constructor for the module base address
+ // set where the start/end are not page aligned and "offset"
+ // is reused as the module base address.
+ MemoryRegion(uint32_t flags, uint64_t start, uint64_t end, uint64_t baseAddress) :
+ m_flags(flags),
+ m_startAddress(start),
+ m_endAddress(end),
+ m_offset(baseAddress),
+ m_fileName(nullptr)
+ {
+ }
+
// copy with new file name constructor
MemoryRegion(const MemoryRegion& region, const char* fileName) :
m_flags(region.m_flags),
diff --git a/src/debug/createdump/threadinfo.cpp b/src/debug/createdump/threadinfo.cpp
index 0bb439e50c..3e5a5ccc17 100644
--- a/src/debug/createdump/threadinfo.cpp
+++ b/src/debug/createdump/threadinfo.cpp
@@ -5,11 +5,21 @@
#include "createdump.h"
#include <asm/ptrace.h>
+#ifndef THUMB_CODE
+#define THUMB_CODE 1
+#endif
+
+#ifndef __GLIBC__
+typedef int __ptrace_request;
+#endif
+
#define FPREG_ErrorOffset(fpregs) *(DWORD*)&((fpregs).rip)
#define FPREG_ErrorSelector(fpregs) *(((WORD*)&((fpregs).rip)) + 2)
#define FPREG_DataOffset(fpregs) *(DWORD*)&((fpregs).rdp)
#define FPREG_DataSelector(fpregs) *(((WORD*)&((fpregs).rdp)) + 2)
+extern CrashInfo* g_crashInfo;
+
ThreadInfo::ThreadInfo(pid_t tid) :
m_tid(tid)
{
@@ -20,15 +30,15 @@ ThreadInfo::~ThreadInfo()
}
bool
-ThreadInfo::Initialize(ICLRDataTarget* dataTarget)
+ThreadInfo::Initialize(ICLRDataTarget* pDataTarget)
{
if (!CrashInfo::GetStatus(m_tid, &m_ppid, &m_tgid, nullptr))
{
return false;
}
- if (dataTarget != nullptr)
+ if (pDataTarget != nullptr)
{
- if (!GetRegistersWithDataTarget(dataTarget))
+ if (!GetRegistersWithDataTarget(pDataTarget))
{
return false;
}
@@ -58,6 +68,108 @@ ThreadInfo::ResumeThread()
}
}
+// Helper for UnwindNativeFrames
+static void
+GetFrameLocation(CONTEXT* pContext, uint64_t* ip, uint64_t* sp)
+{
+#if defined(__x86_64__)
+ *ip = pContext->Rip;
+ *sp = pContext->Rsp;
+#elif defined(__i386__)
+ *ip = pContext->Eip;
+ *sp = pContext->Esp;
+#elif defined(__arm__)
+ *ip = pContext->Pc & ~THUMB_CODE;
+ *sp = pContext->Sp;
+#endif
+}
+
+// Helper for UnwindNativeFrames
+static BOOL
+ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size)
+{
+ return g_crashInfo->ReadMemory(address, buffer, size);
+}
+
+void
+ThreadInfo::UnwindNativeFrames(CrashInfo& crashInfo, CONTEXT* pContext)
+{
+ // For each native frame
+ while (true)
+ {
+ uint64_t ip = 0, sp = 0;
+ GetFrameLocation(pContext, &ip, &sp);
+
+ TRACE("Unwind: sp %" PRIA PRIx64 " ip %" PRIA PRIx64 "\n", sp, ip);
+ if (ip == 0) {
+ break;
+ }
+ // Add two pages around the instruction pointer to the core dump
+ crashInfo.InsertMemoryRegion(ip - PAGE_SIZE, PAGE_SIZE * 2);
+
+ // Look up the ip address to get the module base address
+ uint64_t baseAddress = crashInfo.GetBaseAddress(ip);
+ if (baseAddress == 0) {
+ TRACE("Unwind: module base not found ip %" PRIA PRIx64 "\n", ip);
+ break;
+ }
+
+ // Unwind the native frame adding all the memory accessed to the
+ // core dump via the read memory adapter.
+ if (!PAL_VirtualUnwindOutOfProc(pContext, nullptr, baseAddress, ReadMemoryAdapter)) {
+ TRACE("Unwind: PAL_VirtualUnwindOutOfProc returned false\n");
+ break;
+ }
+ }
+}
+
+bool
+ThreadInfo::UnwindThread(CrashInfo& crashInfo, IXCLRDataProcess* pClrDataProcess)
+{
+ ReleaseHolder<IXCLRDataTask> pTask;
+ ReleaseHolder<IXCLRDataStackWalk> pStackwalk;
+
+ TRACE("Unwind: thread %04x\n", Tid());
+
+ // Get starting native context for the thread
+ CONTEXT context;
+ GetThreadContext(CONTEXT_ALL, &context);
+
+ // Unwind the native frames at the top of the stack
+ UnwindNativeFrames(crashInfo, &context);
+
+ // Get the managed stack walker for this thread
+ if (SUCCEEDED(pClrDataProcess->GetTaskByOSThreadID(Tid(), &pTask)))
+ {
+ pTask->CreateStackWalk(
+ CLRDATA_SIMPFRAME_UNRECOGNIZED |
+ CLRDATA_SIMPFRAME_MANAGED_METHOD |
+ CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
+ CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
+ &pStackwalk);
+ }
+
+ // For each managed frame (if any)
+ if (pStackwalk != nullptr)
+ {
+ TRACE("Unwind: managed frames\n");
+ do
+ {
+ // Get the managed stack frame context
+ if (pStackwalk->GetContext(CONTEXT_ALL, sizeof(context), nullptr, (BYTE *)&context) != S_OK) {
+ TRACE("Unwind: stack walker GetContext FAILED\n");
+ break;
+ }
+
+ // Unwind all the native frames after the managed frame
+ UnwindNativeFrames(crashInfo, &context);
+
+ } while (pStackwalk->Next() == S_OK);
+ }
+
+ return true;
+}
+
bool
ThreadInfo::GetRegistersWithPTrace()
{
@@ -93,11 +205,11 @@ ThreadInfo::GetRegistersWithPTrace()
}
bool
-ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* dataTarget)
+ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* pDataTarget)
{
CONTEXT context;
context.ContextFlags = CONTEXT_ALL;
- if (dataTarget->GetThreadContext(m_tid, context.ContextFlags, sizeof(context), reinterpret_cast<PBYTE>(&context)) != S_OK)
+ if (pDataTarget->GetThreadContext(m_tid, context.ContextFlags, sizeof(context), reinterpret_cast<PBYTE>(&context)) != S_OK)
{
return false;
}
@@ -184,39 +296,32 @@ ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* dataTarget)
}
void
-ThreadInfo::GetThreadStack(const CrashInfo& crashInfo, uint64_t* startAddress, size_t* size) const
+ThreadInfo::GetThreadStack(CrashInfo& crashInfo)
{
+ uint64_t startAddress;
+ size_t size;
+
#if defined(__arm__)
- *startAddress = m_gpRegisters.ARM_sp & PAGE_MASK;
+ startAddress = m_gpRegisters.ARM_sp & PAGE_MASK;
#else
- *startAddress = m_gpRegisters.rsp & PAGE_MASK;
+ startAddress = m_gpRegisters.rsp & PAGE_MASK;
#endif
- *size = 4 * PAGE_SIZE;
+ size = 4 * PAGE_SIZE;
- const MemoryRegion* region = CrashInfo::SearchMemoryRegions(crashInfo.OtherMappings(), *startAddress);
+ MemoryRegion search(0, startAddress, startAddress + PAGE_SIZE);
+ const MemoryRegion* region = CrashInfo::SearchMemoryRegions(crashInfo.OtherMappings(), search);
if (region != nullptr) {
// Use the mapping found for the size of the thread's stack
- *size = region->EndAddress() - *startAddress;
+ size = region->EndAddress() - startAddress;
if (g_diagnostics)
{
- TRACE("Thread %04x stack found in other mapping (size %08zx): ", m_tid, *size);
+ TRACE("Thread %04x stack found in other mapping (size %08zx): ", m_tid, size);
region->Trace();
}
}
-
-}
-
-void
-ThreadInfo::GetThreadCode(uint64_t* startAddress, size_t* size) const
-{
-#if defined(__arm__)
- *startAddress = m_gpRegisters.ARM_pc & PAGE_MASK;
-#elif defined(__x86_64__)
- *startAddress = m_gpRegisters.rip & PAGE_MASK;
-#endif
- *size = PAGE_SIZE;
+ crashInfo.InsertMemoryRegion(startAddress, size);
}
void
diff --git a/src/debug/createdump/threadinfo.h b/src/debug/createdump/threadinfo.h
index 3756669ac3..8851877cd0 100644
--- a/src/debug/createdump/threadinfo.h
+++ b/src/debug/createdump/threadinfo.h
@@ -35,25 +35,26 @@ private:
public:
ThreadInfo(pid_t tid);
~ThreadInfo();
- bool Initialize(ICLRDataTarget* dataTarget);
+ bool Initialize(ICLRDataTarget* pDataTarget);
void ResumeThread();
- void GetThreadStack(const CrashInfo& crashInfo, uint64_t* startAddress, size_t* size) const;
- void GetThreadCode(uint64_t* startAddress, size_t* size) const;
+ bool UnwindThread(CrashInfo& crashInfo, IXCLRDataProcess* pClrDataProcess);
+ void GetThreadStack(CrashInfo& crashInfo);
void GetThreadContext(uint32_t flags, CONTEXT* context) const;
- const pid_t Tid() const { return m_tid; }
- const pid_t Ppid() const { return m_ppid; }
- const pid_t Tgid() const { return m_tgid; }
+ inline const pid_t Tid() const { return m_tid; }
+ inline const pid_t Ppid() const { return m_ppid; }
+ inline const pid_t Tgid() const { return m_tgid; }
- const user_regs_struct* GPRegisters() const { return &m_gpRegisters; }
- const user_fpregs_struct* FPRegisters() const { return &m_fpRegisters; }
+ inline const user_regs_struct* GPRegisters() const { return &m_gpRegisters; }
+ inline const user_fpregs_struct* FPRegisters() const { return &m_fpRegisters; }
#if defined(__i386__)
- const user_fpxregs_struct* FPXRegisters() const { return &m_fpxRegisters; }
+ inline const user_fpxregs_struct* FPXRegisters() const { return &m_fpxRegisters; }
#elif defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__)
- const user_vfpregs_struct* VFPRegisters() const { return &m_vfpRegisters; }
+ inline const user_vfpregs_struct* VFPRegisters() const { return &m_vfpRegisters; }
#endif
private:
+ void UnwindNativeFrames(CrashInfo& crashInfo, CONTEXT* pContext);
bool GetRegistersWithPTrace();
bool GetRegistersWithDataTarget(ICLRDataTarget* dataTarget);
};
diff --git a/src/debug/daccess/daccess.cpp b/src/debug/daccess/daccess.cpp
index 14ce251bfc..12825e040f 100644
--- a/src/debug/daccess/daccess.cpp
+++ b/src/debug/daccess/daccess.cpp
@@ -29,6 +29,7 @@
#endif
#include "dwbucketmanager.hpp"
+#include "gcinterface.dac.h"
// To include definiton of IsThrowableThreadAbortException
// #include <exstatecommon.h>
@@ -3274,6 +3275,10 @@ ClrDataAccess::QueryInterface(THIS_
{
ifaceRet = static_cast<ISOSDacInterface4*>(this);
}
+ else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface5)))
+ {
+ ifaceRet = static_cast<ISOSDacInterface5*>(this);
+ }
else
{
*iface = NULL;
@@ -7932,8 +7937,6 @@ STDAPI OutOfProcessExceptionEventDebuggerLaunchCallback(__in PDWORD pContext,
// DacHandleEnum
-// TODO(Local GC) - The DAC should not include GC headers
-#include "../../gc/handletablepriv.h"
#include "comcallablewrapper.h"
DacHandleWalker::DacHandleWalker()
@@ -7987,7 +7990,7 @@ HRESULT DacHandleWalker::Init(UINT32 typemask)
{
SUPPORTS_DAC;
- mMap = &g_HandleTableMap;
+ mMap = g_gcDacGlobals->handle_table_map;
mTypeMask = typemask;
return S_OK;
@@ -8065,7 +8068,7 @@ bool DacHandleWalker::FetchMoreHandles(HANDLESCANPROC callback)
{
for (int i = 0; i < max_slots; ++i)
{
- HHANDLETABLE hTable = mMap->pBuckets[mIndex]->pTable[i];
+ DPTR(dac_handle_table) hTable = mMap->pBuckets[mIndex]->pTable[i];
if (hTable)
{
// Yikes! The handle table callbacks don't produce the handle type or
@@ -8079,8 +8082,8 @@ bool DacHandleWalker::FetchMoreHandles(HANDLESCANPROC callback)
{
if (mask & 1)
{
- HandleTable *pTable = (HandleTable *)hTable;
- PTR_AppDomain pDomain = SystemDomain::GetAppDomainAtIndex(pTable->uADIndex);
+ dac_handle_table *pTable = hTable;
+ PTR_AppDomain pDomain = SystemDomain::GetAppDomainAtIndex(ADIndex(pTable->uADIndex));
param.AppDomain = TO_CDADDR(pDomain.GetAddr());
param.Type = handleType;
diff --git a/src/debug/daccess/dacdbiimpl.cpp b/src/debug/daccess/dacdbiimpl.cpp
index f48ecc0bd0..38f52adc1b 100644
--- a/src/debug/daccess/dacdbiimpl.cpp
+++ b/src/debug/daccess/dacdbiimpl.cpp
@@ -903,14 +903,15 @@ void DacDbiInterfaceImpl::GetNativeVarData(MethodDesc * pMethodDesc,
// pEntryCount is the number of valid entries in nativeMap, and it may be adjusted downwards
// as part of the composition.
//-----------------------------------------------------------------------------
-void DacDbiInterfaceImpl::ComposeMapping(InstrumentedILOffsetMapping profilerILMap, ICorDebugInfo::OffsetMapping nativeMap[], ULONG32* pEntryCount)
+void DacDbiInterfaceImpl::ComposeMapping(const InstrumentedILOffsetMapping * pProfilerILMap, ICorDebugInfo::OffsetMapping nativeMap[], ULONG32* pEntryCount)
{
// Translate the IL offset if the profiler has provided us with a mapping.
// The ICD public API should always expose the original IL offsets, but GetBoundaries()
// directly accesses the debug info, which stores the instrumented IL offsets.
ULONG32 entryCount = *pEntryCount;
- if (!profilerILMap.IsNull())
+ // The map pointer could be NULL or there could be no entries in the map, in either case no work to do
+ if (pProfilerILMap && !pProfilerILMap->IsNull())
{
// If we did instrument, then we can't have any sequence points that
// are "in-between" the old-->new map that the profiler gave us.
@@ -925,7 +926,7 @@ void DacDbiInterfaceImpl::ComposeMapping(InstrumentedILOffsetMapping profilerILM
ULONG32 prevILOffset = (ULONG32)(ICorDebugInfo::MAX_ILNUM);
for (ULONG32 i = 0; i < entryCount; i++)
{
- ULONG32 origILOffset = TranslateInstrumentedILOffsetToOriginal(nativeMap[i].ilOffset, &profilerILMap);
+ ULONG32 origILOffset = TranslateInstrumentedILOffsetToOriginal(nativeMap[i].ilOffset, pProfilerILMap);
if (origILOffset == prevILOffset)
{
@@ -1003,12 +1004,12 @@ void DacDbiInterfaceImpl::GetSequencePoints(MethodDesc * pMethodDesc,
// if there is a rejit IL map for this function, apply that in preference to load-time mapping
#ifdef FEATURE_REJIT
- ReJitManager * pReJitMgr = pMethodDesc->GetReJitManager();
- ReJitInfo* pReJitInfo = pReJitMgr->FindReJitInfo(dac_cast<PTR_MethodDesc>(pMethodDesc), (PCODE)startAddr, 0);
- if (pReJitInfo != NULL)
+ CodeVersionManager * pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
+ NativeCodeVersion nativeCodeVersion = pCodeVersionManager->GetNativeCodeVersion(dac_cast<PTR_MethodDesc>(pMethodDesc), (PCODE)startAddr);
+ if (!nativeCodeVersion.IsNull())
{
- InstrumentedILOffsetMapping rejitMapping = pReJitInfo->m_pShared->m_instrumentedILMap;
- ComposeMapping(rejitMapping, mapCopy, &entryCount);
+ const InstrumentedILOffsetMapping * pRejitMapping = nativeCodeVersion.GetILCodeVersion().GetInstrumentedILMap();
+ ComposeMapping(pRejitMapping, mapCopy, &entryCount);
}
else
{
@@ -1016,7 +1017,7 @@ void DacDbiInterfaceImpl::GetSequencePoints(MethodDesc * pMethodDesc,
// if there is a profiler load-time mapping and not a rejit mapping, apply that instead
InstrumentedILOffsetMapping loadTimeMapping =
pMethodDesc->GetModule()->GetInstrumentedILOffsetMapping(pMethodDesc->GetMemberDef());
- ComposeMapping(loadTimeMapping, mapCopy, &entryCount);
+ ComposeMapping(&loadTimeMapping, mapCopy, &entryCount);
#ifdef FEATURE_REJIT
}
#endif
@@ -5716,7 +5717,7 @@ BOOL DacDbiInterfaceImpl::IsVmObjectHandleValid(VMPTR_OBJECTHANDLE vmHandle)
// SEH exceptions will be caught
EX_TRY
{
- OBJECTREF objRef = HndFetchHandle((OBJECTHANDLE)vmHandle.GetDacPtr());
+ OBJECTREF objRef = ObjectFromHandle((OBJECTHANDLE)vmHandle.GetDacPtr());
// NULL is certainly valid...
if (objRef != NULL)
@@ -7149,26 +7150,36 @@ HRESULT DacDbiInterfaceImpl::GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TA
HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pvmReJitInfo)
{
DD_ENTER_MAY_THROW;
- if (pvmReJitInfo == NULL)
+ _ASSERTE(!"You shouldn't be calling this - use GetActiveRejitILCodeVersionNode instead");
+ return S_OK;
+}
+
+HRESULT DacDbiInterfaceImpl::GetActiveRejitILCodeVersionNode(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ILCodeVersionNode* pVmILCodeVersionNode)
+{
+ DD_ENTER_MAY_THROW;
+ if (pVmILCodeVersionNode == NULL)
return E_INVALIDARG;
#ifdef FEATURE_REJIT
PTR_Module pModule = vmModule.GetDacPtr();
- ReJitManager * pReJitMgr = pModule->GetReJitManager();
- PTR_ReJitInfo pReJitInfoCurrent = pReJitMgr->FindNonRevertedReJitInfo(pModule, methodTk);
- // if the token lookup failed, we need to search again by method desc
- // The rejit manager will index by token if the method isn't loaded when RequestReJIT runs
- // and by methoddesc if it was loaded
- if (pReJitInfoCurrent == NULL)
- {
- MethodDesc* pMD = pModule->LookupMethodDef(methodTk);
- if (pMD != NULL)
- {
- pReJitInfoCurrent = pReJitMgr->FindNonRevertedReJitInfo(dac_cast<PTR_MethodDesc>(pMD));
- }
+ CodeVersionManager * pCodeVersionManager = pModule->GetCodeVersionManager();
+ // Be careful, there are two different definitions of 'active' being used here
+ // For the CodeVersionManager, the active IL version is whatever one should be used in the next invocation of the method
+ // 'rejit active' narrows that to only include rejit IL bodies where the profiler has already provided the definition
+ // for the new IL (ilCodeVersion.GetRejitState()==ILCodeVersion::kStateActive). It is possible that the code version
+ // manager's active IL version hasn't yet asked the profiler for the IL body to use, in which case we want to filter it
+ // out from the return in this method.
+ ILCodeVersion activeILVersion = pCodeVersionManager->GetActiveILCodeVersion(pModule, methodTk);
+ if (activeILVersion.IsNull() || activeILVersion.GetRejitState() != ILCodeVersion::kStateActive)
+ {
+ pVmILCodeVersionNode->SetDacTargetPtr(0);
+ }
+ else
+ {
+ pVmILCodeVersionNode->SetDacTargetPtr(PTR_TO_TADDR(activeILVersion.AsNode()));
}
- pvmReJitInfo->SetDacTargetPtr(PTR_TO_TADDR(pReJitInfoCurrent));
#else
- pvmReJitInfo->SetDacTargetPtr(0);
+ _ASSERTE(!"You shouldn't be calling this - rejit is not supported in this build");
+ pVmILCodeVersionNode->SetDacTargetPtr(0);
#endif
return S_OK;
}
@@ -7176,15 +7187,22 @@ HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_Module vmModule, mdMethodDef met
HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pvmReJitInfo)
{
DD_ENTER_MAY_THROW;
- if (pvmReJitInfo == NULL)
+ _ASSERTE(!"You shouldn't be calling this - use GetNativeCodeVersionNode instead");
+ return S_OK;
+}
+
+HRESULT DacDbiInterfaceImpl::GetNativeCodeVersionNode(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_NativeCodeVersionNode* pVmNativeCodeVersionNode)
+{
+ DD_ENTER_MAY_THROW;
+ if (pVmNativeCodeVersionNode == NULL)
return E_INVALIDARG;
#ifdef FEATURE_REJIT
PTR_MethodDesc pMD = vmMethod.GetDacPtr();
- ReJitManager * pReJitMgr = pMD->GetReJitManager();
- PTR_ReJitInfo pReJitInfoCurrent = pReJitMgr->FindReJitInfo(pMD, (PCODE)codeStartAddress, 0);
- pvmReJitInfo->SetDacTargetPtr(PTR_TO_TADDR(pReJitInfoCurrent));
+ CodeVersionManager * pCodeVersionManager = pMD->GetCodeVersionManager();
+ NativeCodeVersion codeVersion = pCodeVersionManager->GetNativeCodeVersion(pMD, (PCODE)codeStartAddress);
+ pVmNativeCodeVersionNode->SetDacTargetPtr(PTR_TO_TADDR(codeVersion.AsNode()));
#else
- pvmReJitInfo->SetDacTargetPtr(0);
+ pVmNativeCodeVersionNode->SetDacTargetPtr(0);
#endif
return S_OK;
}
@@ -7192,14 +7210,21 @@ HRESULT DacDbiInterfaceImpl::GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRE
HRESULT DacDbiInterfaceImpl::GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, OUT VMPTR_SharedReJitInfo* pvmSharedReJitInfo)
{
DD_ENTER_MAY_THROW;
- if (pvmSharedReJitInfo == NULL)
+ _ASSERTE(!"You shouldn't be calling this - use GetLCodeVersionNode instead");
+ return S_OK;
+}
+
+HRESULT DacDbiInterfaceImpl::GetILCodeVersionNode(VMPTR_NativeCodeVersionNode vmNativeCodeVersionNode, VMPTR_ILCodeVersionNode* pVmILCodeVersionNode)
+{
+ DD_ENTER_MAY_THROW;
+ if (pVmILCodeVersionNode == NULL)
return E_INVALIDARG;
#ifdef FEATURE_REJIT
- ReJitInfo* pReJitInfo = vmReJitInfo.GetDacPtr();
- pvmSharedReJitInfo->SetDacTargetPtr(PTR_TO_TADDR(pReJitInfo->m_pShared));
+ NativeCodeVersionNode* pNativeCodeVersionNode = vmNativeCodeVersionNode.GetDacPtr();
+ pVmILCodeVersionNode->SetDacTargetPtr(PTR_TO_TADDR(pNativeCodeVersionNode->GetILCodeVersion().AsNode()));
#else
- _ASSERTE(!"You shouldn't be calling this - how did you get a ReJitInfo?");
- pvmSharedReJitInfo->SetDacTargetPtr(0);
+ _ASSERTE(!"You shouldn't be calling this - rejit is not supported in this build");
+ pVmILCodeVersionNode->SetDacTargetPtr(0);
#endif
return S_OK;
}
@@ -7207,15 +7232,31 @@ HRESULT DacDbiInterfaceImpl::GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, OUT
HRESULT DacDbiInterfaceImpl::GetSharedReJitInfoData(VMPTR_SharedReJitInfo vmSharedReJitInfo, DacSharedReJitInfo* pData)
{
DD_ENTER_MAY_THROW;
+ _ASSERTE(!"You shouldn't be calling this - use GetILCodeVersionNodeData instead");
+ return S_OK;
+}
+
+HRESULT DacDbiInterfaceImpl::GetILCodeVersionNodeData(VMPTR_ILCodeVersionNode vmILCodeVersionNode, DacSharedReJitInfo* pData)
+{
+ DD_ENTER_MAY_THROW;
#ifdef FEATURE_REJIT
- SharedReJitInfo* pSharedReJitInfo = vmSharedReJitInfo.GetDacPtr();
- pData->m_state = pSharedReJitInfo->GetState();
- pData->m_pbIL = PTR_TO_CORDB_ADDRESS(pSharedReJitInfo->m_pbIL);
- pData->m_dwCodegenFlags = pSharedReJitInfo->m_dwCodegenFlags;
- pData->m_cInstrumentedMapEntries = (ULONG)pSharedReJitInfo->m_instrumentedILMap.GetCount();
- pData->m_rgInstrumentedMapEntries = PTR_TO_CORDB_ADDRESS(dac_cast<ULONG_PTR>(pSharedReJitInfo->m_instrumentedILMap.GetOffsets()));
+ ILCodeVersionNode* pILCodeVersionNode = vmILCodeVersionNode.GetDacPtr();
+ pData->m_state = pILCodeVersionNode->GetRejitState();
+ pData->m_pbIL = PTR_TO_CORDB_ADDRESS(dac_cast<ULONG_PTR>(pILCodeVersionNode->GetIL()));
+ pData->m_dwCodegenFlags = pILCodeVersionNode->GetJitFlags();
+ const InstrumentedILOffsetMapping* pMapping = pILCodeVersionNode->GetInstrumentedILMap();
+ if (pMapping)
+ {
+ pData->m_cInstrumentedMapEntries = (ULONG)pMapping->GetCount();
+ pData->m_rgInstrumentedMapEntries = PTR_TO_CORDB_ADDRESS(dac_cast<ULONG_PTR>(pMapping->GetOffsets()));
+ }
+ else
+ {
+ pData->m_cInstrumentedMapEntries = 0;
+ pData->m_rgInstrumentedMapEntries = 0;
+ }
#else
- _ASSERTE(!"You shouldn't be calling this - how did you get a SharedReJitInfo?");
+ _ASSERTE(!"You shouldn't be calling this - rejit isn't supported in this build");
#endif
return S_OK;
}
diff --git a/src/debug/daccess/dacdbiimpl.h b/src/debug/daccess/dacdbiimpl.h
index a86072325c..b13c75a561 100644
--- a/src/debug/daccess/dacdbiimpl.h
+++ b/src/debug/daccess/dacdbiimpl.h
@@ -147,9 +147,13 @@ public:
void GetGCHeapInformation(COR_HEAPINFO * pHeapInfo);
HRESULT GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW);
HRESULT GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pReJitInfo);
+ HRESULT GetActiveRejitILCodeVersionNode(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ILCodeVersionNode* pVmILCodeVersionNode);
HRESULT GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pReJitInfo);
+ HRESULT GetNativeCodeVersionNode(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_NativeCodeVersionNode* pVmNativeCodeVersionNode);
HRESULT GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, VMPTR_SharedReJitInfo* pSharedReJitInfo);
+ HRESULT GetILCodeVersionNode(VMPTR_NativeCodeVersionNode vmNativeCodeVersionNode, VMPTR_ILCodeVersionNode* pVmILCodeVersionNode);
HRESULT GetSharedReJitInfoData(VMPTR_SharedReJitInfo sharedReJitInfo, DacSharedReJitInfo* pData);
+ HRESULT GetILCodeVersionNodeData(VMPTR_ILCodeVersionNode vmILCodeVersionNode, DacSharedReJitInfo* pData);
HRESULT GetDefinesBitField(ULONG32 *pDefines);
HRESULT GetMDStructuresVersion(ULONG32* pMDStructuresVersion);
@@ -174,7 +178,7 @@ private:
SequencePoints * pNativeMap);
// Helper to compose a IL->IL and IL->Native mapping
- void ComposeMapping(InstrumentedILOffsetMapping profilerILMap, ICorDebugInfo::OffsetMapping nativeMap[], ULONG32* pEntryCount);
+ void ComposeMapping(const InstrumentedILOffsetMapping * pProfilerILMap, ICorDebugInfo::OffsetMapping nativeMap[], ULONG32* pEntryCount);
// Helper function to convert an instrumented IL offset to the corresponding original IL offset.
ULONG TranslateInstrumentedILOffsetToOriginal(ULONG ilOffset,
diff --git a/src/debug/daccess/dacfn.cpp b/src/debug/daccess/dacfn.cpp
index 2f7a98de1a..d6e0a89267 100644
--- a/src/debug/daccess/dacfn.cpp
+++ b/src/debug/daccess/dacfn.cpp
@@ -203,8 +203,7 @@ DacWriteAll(TADDR addr, PVOID buffer, ULONG32 size, bool throwEx)
HRESULT status;
- status = g_dacImpl->m_pMutableTarget->
- WriteVirtual(addr, (PBYTE)buffer, size);
+ status = g_dacImpl->m_pMutableTarget->WriteVirtual(addr, (PBYTE)buffer, size);
if (status != S_OK)
{
if (throwEx)
@@ -218,6 +217,34 @@ DacWriteAll(TADDR addr, PVOID buffer, ULONG32 size, bool throwEx)
}
#ifdef FEATURE_PAL
+
+static BOOL DacReadAllAdapter(PVOID address, PVOID buffer, SIZE_T size)
+{
+ DAC_INSTANCE* inst = g_dacImpl->m_instances.Find((TADDR)address);
+ if (inst == nullptr || inst->size < size)
+ {
+ inst = g_dacImpl->m_instances.Alloc((TADDR)address, size, DAC_PAL);
+ if (inst == nullptr)
+ {
+ return FALSE;
+ }
+ inst->noReport = 0;
+ HRESULT hr = DacReadAll((TADDR)address, inst + 1, size, false);
+ if (FAILED(hr))
+ {
+ g_dacImpl->m_instances.ReturnAlloc(inst);
+ return FALSE;
+ }
+ if (!g_dacImpl->m_instances.Add(inst))
+ {
+ g_dacImpl->m_instances.ReturnAlloc(inst);
+ return FALSE;
+ }
+ }
+ memcpy(buffer, inst + 1, size);
+ return TRUE;
+}
+
HRESULT
DacVirtualUnwind(DWORD threadId, PT_CONTEXT context, PT_KNONVOLATILE_CONTEXT_POINTERS contextPointers)
{
@@ -233,15 +260,28 @@ DacVirtualUnwind(DWORD threadId, PT_CONTEXT context, PT_KNONVOLATILE_CONTEXT_POI
memset(contextPointers, 0, sizeof(T_KNONVOLATILE_CONTEXT_POINTERS));
}
+ HRESULT hr = S_OK;
+
+#ifdef FEATURE_DATATARGET4
ReleaseHolder<ICorDebugDataTarget4> dt;
- HRESULT hr = g_dacImpl->m_pTarget->QueryInterface(IID_ICorDebugDataTarget4, (void **)&dt);
+ hr = g_dacImpl->m_pTarget->QueryInterface(IID_ICorDebugDataTarget4, (void **)&dt);
if (SUCCEEDED(hr))
{
hr = dt->VirtualUnwind(threadId, sizeof(CONTEXT), (BYTE*)context);
}
+ else
+#endif
+ {
+ SIZE_T baseAddress = DacGlobalBase();
+ if (baseAddress == 0 || !PAL_VirtualUnwindOutOfProc(context, contextPointers, baseAddress, DacReadAllAdapter))
+ {
+ hr = E_FAIL;
+ }
+ }
return hr;
}
+
#endif // FEATURE_PAL
// DacAllocVirtual - Allocate memory from the target process
diff --git a/src/debug/daccess/dacimpl.h b/src/debug/daccess/dacimpl.h
index c3a1c46f68..2647c8d300 100644
--- a/src/debug/daccess/dacimpl.h
+++ b/src/debug/daccess/dacimpl.h
@@ -14,10 +14,7 @@
#ifndef __DACIMPL_H__
#define __DACIMPL_H__
-// This header include will need to be removed as part of GitHub#12170.
-// The only reason it's here now is that this header references the GC-private
-// structure "HandleTableMap".
-#include "../../gc/objecthandle.h"
+#include "gcinterface.dac.h"
#if defined(_TARGET_ARM_) || defined(FEATURE_CORESYSTEM) // @ARMTODO: STL breaks the build with current VC headers
//---------------------------------------------------------------------------------------
@@ -128,6 +125,7 @@ enum DAC_USAGE_TYPE
DAC_VPTR,
DAC_STRA,
DAC_STRW,
+ DAC_PAL,
};
// mscordacwks's module handle
@@ -863,7 +861,8 @@ class ClrDataAccess
public ISOSDacInterface,
public ISOSDacInterface2,
public ISOSDacInterface3,
- public ISOSDacInterface4
+ public ISOSDacInterface4,
+ public ISOSDacInterface5
{
public:
ClrDataAccess(ICorDebugDataTarget * pTarget, ICLRDataTarget * pLegacyTarget=0);
@@ -1206,6 +1205,9 @@ public:
// ISOSDacInterface4
virtual HRESULT STDMETHODCALLTYPE GetClrNotification(CLRDATA_ADDRESS arguments[], int count, int *pNeeded);
+ // ISOSDacInterface5
+ virtual HRESULT STDMETHODCALLTYPE GetTieredVersions(CLRDATA_ADDRESS methodDesc, int rejitId, struct DacpTieredVersionData *nativeCodeAddrs, int cNativeCodeAddrs, int *pcNativeCodeAddrs);
+
//
// ClrDataAccess.
//
@@ -2242,7 +2244,7 @@ private:
ULONG32 m_instanceAge;
// Handle table walking variables.
- HandleTableMap *mMap;
+ dac_handle_table_map *mMap;
int mIndex;
UINT32 mTypeMask;
int mGenerationFilter;
diff --git a/src/debug/daccess/enummem.cpp b/src/debug/daccess/enummem.cpp
index 6cd210f3dc..c1155d9e36 100644
--- a/src/debug/daccess/enummem.cpp
+++ b/src/debug/daccess/enummem.cpp
@@ -298,7 +298,6 @@ HRESULT ClrDataAccess::EnumMemCLRStatic(IN CLRDataEnumMemoryFlags flags)
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pEnumClass.EnumMem(); )
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pThreadClass.EnumMem(); )
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pFreeObjectMethodTable.EnumMem(); )
- CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pObjectCtorMD.EnumMem(); )
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_fHostConfig.EnumMem(); )
// These two static pointers are pointed to static data of byte[]
diff --git a/src/debug/daccess/gcinterface.dac.h b/src/debug/daccess/gcinterface.dac.h
index 031ec36f30..c7765ef364 100644
--- a/src/debug/daccess/gcinterface.dac.h
+++ b/src/debug/daccess/gcinterface.dac.h
@@ -1,5 +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 __DACCESS_GCINTERFACE_DAC_H__
+#define __DACCESS_GCINTERFACE_DAC_H__
#include "../../gc/gcinterface.dac.h"
+
+// The following six function prototypes are for functions that are used by the DAC
+// to inspect the handle table in detail. The semantics of these functions MUST be
+// versioned along with the rest of this interface - any changes in semantics
+// must be accompanied with a major versino bump.
+//
+// Please do not add any additional functions to this list; we'd like to keep it
+// to an absolute minimum.
+#ifdef DACCESS_COMPILE
+// for DAC builds, OBJECTHANDLE is a uintptr_t.
+GC_DAC_VISIBLE
+OBJECTREF GetDependentHandleSecondary(OBJECTHANDLE handle);
+
+GC_DAC_VISIBLE_NO_MANGLE
+void HndScanHandlesForGC(
+ DPTR(dac_handle_table) hTable,
+ HANDLESCANPROC scanProc,
+ uintptr_t param1,
+ uintptr_t param2,
+ const uint32_t *types,
+ uint32_t typeCount,
+ uint32_t condemned,
+ uint32_t maxGen,
+ uint32_t flags);
+
+GC_DAC_VISIBLE_NO_MANGLE
+void HndEnumHandles(
+ DPTR(dac_handle_table) hTable,
+ const uint32_t *puType,
+ uint32_t uTypeCount,
+ HANDLESCANPROC pfnEnum,
+ uintptr_t lParam1,
+ uintptr_t lParam2,
+ bool fAsync);
+
+GC_DAC_VISIBLE
+OBJECTREF HndFetchHandle(OBJECTHANDLE handle);
+
+GC_DAC_VISIBLE
+struct ADIndex HndGetHandleADIndex(OBJECTHANDLE handle);
+
+GC_DAC_VISIBLE
+uintptr_t HndGetHandleExtraInfo(OBJECTHANDLE handle);
+
+#endif // DACCESS_COMPILE
+
+#endif // __DACCESS_GCINTERFACE_DAC_H__
diff --git a/src/debug/daccess/nidump.cpp b/src/debug/daccess/nidump.cpp
index cd5ba83b23..f1ee6f151e 100644
--- a/src/debug/daccess/nidump.cpp
+++ b/src/debug/daccess/nidump.cpp
@@ -3947,7 +3947,7 @@ void NativeImageDumper::DumpModule( PTR_Module module )
DisplayWriteFieldInt( numElementsHot, ctorInfo->numElementsHot,
ModuleCtorInfo, SLIM_MODULE_TBLS );
DisplayWriteFieldAddress( ppMT, DPtrToPreferredAddr(ctorInfo->ppMT),
- ctorInfo->numElements * sizeof(MethodTable*),
+ ctorInfo->numElements * sizeof(RelativePointer<MethodTable*>),
ModuleCtorInfo, SLIM_MODULE_TBLS );
/* REVISIT_TODO Tue 03/21/2006
* is cctorInfoHot and cctorInfoCold actually have anything interesting
@@ -5084,7 +5084,10 @@ void NativeImageDumper::MethodTableToString( PTR_MethodTable mt, SString& buf )
{
numDicts = (DWORD)CountDictionariesInClass(token, dependency->pImport);
}
- PTR_Dictionary dictionary( mt->GetPerInstInfo()[numDicts-1] );
+
+ TADDR base = dac_cast<TADDR>(&(mt->GetPerInstInfo()[numDicts-1]));
+
+ PTR_Dictionary dictionary( MethodTable::PerInstInfoElem_t::GetValueAtPtr(base) );
unsigned numArgs = mt->GetNumGenericArgs();
DictionaryToArgString( dictionary, numArgs, buf );
@@ -5739,17 +5742,6 @@ static NativeImageDumper::EnumMnemonics s_CorTypeAttr[] =
};
static NativeImageDumper::EnumMnemonics s_VMFlags[] =
{
-#define VMF_ENTRY_TRANSPARENCY(x) NativeImageDumper::EnumMnemonics( EEClass::VMFLAG_ ## x, EEClass::VMFLAG_TRANSPARENCY_MASK, W(#x) )
- VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_UNKNOWN),
- VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_TRANSPARENT),
- VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_ALL_TRANSPARENT),
- VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_CRITICAL),
- VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_CRITICAL_TAS),
- VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_ALLCRITICAL),
- VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_ALLCRITICAL_TAS),
- VMF_ENTRY_TRANSPARENCY(TRANSPARENCY_TAS_NOTCRITICAL),
-#undef VMF_ENTRY_TRANSPARENCY
-
#define VMF_ENTRY(x) NativeImageDumper::EnumMnemonics( EEClass::VMFLAG_ ## x, W(#x) )
#ifdef FEATURE_READYTORUN
@@ -5769,12 +5761,9 @@ static NativeImageDumper::EnumMnemonics s_VMFlags[] =
VMF_ENTRY(BESTFITMAPPING),
VMF_ENTRY(THROWONUNMAPPABLECHAR),
- VMF_ENTRY(NOSUPPRESSUNMGDCODEACCESS),
VMF_ENTRY(NO_GUID),
VMF_ENTRY(HASNONPUBLICFIELDS),
- VMF_ENTRY(REMOTING_PROXY_ATTRIBUTE),
VMF_ENTRY(PREFER_ALIGN8),
- VMF_ENTRY(METHODS_REQUIRE_INHERITANCE_CHECKS),
#ifdef FEATURE_COMINTEROP
VMF_ENTRY(SPARSE_FOR_COMINTEROP),
@@ -5831,11 +5820,6 @@ NativeImageDumper::EnumMnemonics NativeImageDumper::s_MDFlag2[] =
MDF2_ENTRY(HasPrecode),
MDF2_ENTRY(IsUnboxingStub),
MDF2_ENTRY(HasNativeCodeSlot),
- MDF2_ENTRY(Transparency_TreatAsSafe),
- MDF2_ENTRY(Transparency_Transparent),
- MDF2_ENTRY(Transparency_Critical),
- MDF2_ENTRY(HostProtectionLinkCheckOnly),
- MDF2_ENTRY(CASDemandsOnly),
#undef MDF2_ENTRY
};
@@ -5861,13 +5845,6 @@ NativeImageDumper::EnumMnemonics NativeImageDumper::s_MDC[] =
// Method is static
MDC_ENTRY(mdcStatic),
- MDC_ENTRY(mdcIntercepted),
-
- MDC_ENTRY(mdcRequiresLinktimeCheck),
-
- MDC_ENTRY(mdcRequiresInheritanceCheck),
-
- MDC_ENTRY(mdcParentRequiresInheritanceCheck),
MDC_ENTRY(mdcDuplicate),
MDC_ENTRY(mdcVerifiedState),
@@ -6009,7 +5986,7 @@ PTR_MethodTable NativeImageDumper::GetParent( PTR_MethodTable mt )
/* REVISIT_TODO Thu 12/01/2005
* Handle fixups
*/
- PTR_MethodTable parent( mt->m_pParentMethodTable );
+ PTR_MethodTable parent( ReadPointerMaybeNull((MethodTable *) mt, &MethodTable::m_pParentMethodTable) );
_ASSERTE(!CORCOMPILE_IS_POINTER_TAGGED(PTR_TO_TADDR(parent)));
return parent;
}
@@ -6983,7 +6960,7 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
- PTR_MethodTable parent = mt->m_pParentMethodTable;
+ PTR_MethodTable parent = ReadPointerMaybeNull((MethodTable *) mt, &MethodTable::m_pParentMethodTable);
if( parent == NULL )
{
DisplayWriteFieldPointer( m_pParentMethodTable, NULL, MethodTable,
@@ -7003,7 +6980,7 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
DPtrToPreferredAddr(mt->GetLoaderModule()),
MethodTable, METHODTABLES );
- PTR_MethodTableWriteableData wd = mt->m_pWriteableData;
+ PTR_MethodTableWriteableData wd = ReadPointer((MethodTable *)mt, &MethodTable::m_pWriteableData);
_ASSERTE(wd != NULL);
DisplayStartStructureWithOffset( m_pWriteableData, DPtrToPreferredAddr(wd),
sizeof(*wd), MethodTable, METHODTABLES );
@@ -7049,8 +7026,7 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
GenericsDictInfo, METHODTABLES);
DisplayEndStructure( METHODTABLES ); //GenericsDictInfo
-
- DPTR(PTR_Dictionary) perInstInfo = mt->GetPerInstInfo();
+ DPTR(MethodTable::PerInstInfoElem_t) perInstInfo = mt->GetPerInstInfo();
DisplayStartStructure( "PerInstInfo",
DPtrToPreferredAddr(perInstInfo),
@@ -7186,9 +7162,9 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
{
m_display->StartStructureWithOffset("Vtable",
mt->GetVtableOffset(),
- mt->GetNumVtableIndirections() * sizeof(PTR_PCODE),
+ mt->GetNumVtableIndirections() * sizeof(MethodTable::VTableIndir_t),
DataPtrToDisplay(PTR_TO_TADDR(mt) + mt->GetVtableOffset()),
- mt->GetNumVtableIndirections() * sizeof(PTR_PCODE));
+ mt->GetNumVtableIndirections() * sizeof(MethodTable::VTableIndir_t));
MethodTable::VtableIndirectionSlotIterator itIndirect = mt->IterateVtableIndirectionSlots();
@@ -7207,7 +7183,8 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
{
DisplayStartElement( "Slot", ALWAYS );
DisplayWriteElementInt( "Index", i, ALWAYS );
- PTR_PCODE tgt = mt->GetVtableIndirections()[i];
+ TADDR base = dac_cast<TADDR>(&(mt->GetVtableIndirections()[i]));
+ PTR_PCODE tgt = MethodTable::VTableIndir_t::GetValueMaybeNullAtPtr(base);
DisplayWriteElementPointer( "Pointer",
DataPtrToDisplay(dac_cast<TADDR>(tgt)),
ALWAYS );
@@ -7243,7 +7220,7 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
else
{
CoverageRead( PTR_TO_TADDR(mt) + mt->GetVtableOffset(),
- mt->GetNumVtableIndirections() * sizeof(PTR_PCODE) );
+ mt->GetNumVtableIndirections() * sizeof(MethodTable::VTableIndir_t) );
if (mt->HasNonVirtualSlotsArray())
{
@@ -7259,7 +7236,7 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
{
PTR_InterfaceInfo ifMap = mt->GetInterfaceMap();
m_display->StartArrayWithOffset( "InterfaceMap",
- offsetof(MethodTable, m_pMultipurposeSlot2),
+ offsetof(MethodTable, m_pInterfaceMap),
sizeof(void*),
NULL );
for( unsigned i = 0; i < mt->GetNumInterfaces(); ++i )
@@ -7293,7 +7270,7 @@ NativeImageDumper::DumpMethodTable( PTR_MethodTable mt, const char * name,
DPtrToPreferredAddr(genStatics),
sizeof(*genStatics) );
- PTR_FieldDesc fieldDescs = genStatics->m_pFieldDescs;
+ PTR_FieldDesc fieldDescs = ReadPointerMaybeNull((GenericsStaticsInfo *) genStatics, &GenericsStaticsInfo::m_pFieldDescs);
if( fieldDescs == NULL )
{
DisplayWriteFieldPointer( m_pFieldDescs, NULL, GenericsStaticsInfo,
diff --git a/src/debug/daccess/request.cpp b/src/debug/daccess/request.cpp
index ebaa1f833f..08136f39e1 100644
--- a/src/debug/daccess/request.cpp
+++ b/src/debug/daccess/request.cpp
@@ -13,8 +13,6 @@
#include "stdafx.h"
#include <win32threadpool.h>
-// TODO(Local GC) - The DAC should not include GC headers
-#include <../../gc/handletablepriv.h>
#include "typestring.h"
#include <gccover.h>
#include <virtualcallstub.h>
@@ -817,35 +815,36 @@ ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *
}
#ifdef FEATURE_REJIT
-void CopyReJitInfoToReJitData(ReJitInfo * pReJitInfo, DacpReJitData * pReJitData)
+void CopyNativeCodeVersionToReJitData(NativeCodeVersion nativeCodeVersion, NativeCodeVersion activeCodeVersion, DacpReJitData * pReJitData)
{
- pReJitData->rejitID = pReJitInfo->m_pShared->GetId();
- pReJitData->NativeCodeAddr = pReJitInfo->m_pCode;
+ pReJitData->rejitID = nativeCodeVersion.GetILCodeVersion().GetVersionId();
+ pReJitData->NativeCodeAddr = nativeCodeVersion.GetNativeCode();
- switch (pReJitInfo->m_pShared->GetState())
+ if (nativeCodeVersion != activeCodeVersion)
{
- default:
- _ASSERTE(!"Unknown SharedRejitInfo state. DAC should be updated to understand this new state.");
- pReJitData->flags = DacpReJitData::kUnknown;
- break;
-
- case SharedReJitInfo::kStateRequested:
- pReJitData->flags = DacpReJitData::kRequested;
- break;
+ pReJitData->flags = DacpReJitData::kReverted;
+ }
+ else
+ {
+ switch (nativeCodeVersion.GetILCodeVersion().GetRejitState())
+ {
+ default:
+ _ASSERTE(!"Unknown SharedRejitInfo state. DAC should be updated to understand this new state.");
+ pReJitData->flags = DacpReJitData::kUnknown;
+ break;
- case SharedReJitInfo::kStateActive:
- pReJitData->flags = DacpReJitData::kActive;
- break;
+ case ILCodeVersion::kStateRequested:
+ pReJitData->flags = DacpReJitData::kRequested;
+ break;
- case SharedReJitInfo::kStateReverted:
- pReJitData->flags = DacpReJitData::kReverted;
- break;
+ case ILCodeVersion::kStateActive:
+ pReJitData->flags = DacpReJitData::kActive;
+ break;
+ }
}
}
#endif // FEATURE_REJIT
-
-
//---------------------------------------------------------------------------------------
//
// Given a method desc addr, this loads up DacpMethodDescData and multiple DacpReJitDatas
@@ -871,10 +870,10 @@ void CopyReJitInfoToReJitData(ReJitInfo * pReJitInfo, DacpReJitData * pReJitData
//
HRESULT ClrDataAccess::GetMethodDescData(
- CLRDATA_ADDRESS methodDesc,
- CLRDATA_ADDRESS ip,
- struct DacpMethodDescData *methodDescData,
- ULONG cRevertedRejitVersions,
+ CLRDATA_ADDRESS methodDesc,
+ CLRDATA_ADDRESS ip,
+ struct DacpMethodDescData *methodDescData,
+ ULONG cRevertedRejitVersions,
DacpReJitData * rgRevertedRejitData,
ULONG * pcNeededRevertedRejitData)
{
@@ -903,12 +902,12 @@ HRESULT ClrDataAccess::GetMethodDescData(
}
else
{
- ZeroMemory(methodDescData,sizeof(DacpMethodDescData));
+ ZeroMemory(methodDescData, sizeof(DacpMethodDescData));
if (rgRevertedRejitData != NULL)
- ZeroMemory(rgRevertedRejitData, sizeof(*rgRevertedRejitData)*cRevertedRejitVersions);
+ ZeroMemory(rgRevertedRejitData, sizeof(*rgRevertedRejitData) * cRevertedRejitVersions);
if (pcNeededRevertedRejitData != NULL)
*pcNeededRevertedRejitData = 0;
-
+
methodDescData->requestedIP = ip;
methodDescData->bHasNativeCode = pMD->HasNativeCode();
methodDescData->bIsDynamic = (pMD->IsLCGMethod()) ? TRUE : FALSE;
@@ -924,8 +923,7 @@ HRESULT ClrDataAccess::GetMethodDescData(
{
methodDescData->NativeCodeAddr = (CLRDATA_ADDRESS)-1;
}
- methodDescData->AddressOfNativeCodeSlot = pMD->HasNativeCodeSlot() ?
- TO_CDADDR(pMD->GetAddrOfNativeCodeSlot()) : NULL;
+ methodDescData->AddressOfNativeCodeSlot = pMD->HasNativeCodeSlot() ? TO_CDADDR(pMD->GetAddrOfNativeCodeSlot()) : NULL;
methodDescData->MDToken = pMD->GetMemberDef();
methodDescData->MethodDescPtr = methodDesc;
methodDescData->MethodTablePtr = HOST_CDADDR(pMD->GetMethodTable());
@@ -938,39 +936,43 @@ HRESULT ClrDataAccess::GetMethodDescData(
// * ReJitInfo for the requested IP (for !ip2md and !u)
// * ReJitInfos for all reverted versions of the method (up to
// cRevertedRejitVersions)
- //
+ //
// Minidumps will not have all this rejit info, and failure to get rejit info
// should not be fatal. So enclose all rejit stuff in a try.
EX_TRY
{
- ReJitManager * pReJitMgr = pMD->GetReJitManager();
+ CodeVersionManager *pCodeVersionManager = pMD->GetCodeVersionManager();
// Current ReJitInfo
- ReJitInfo * pReJitInfoCurrent = pReJitMgr->FindNonRevertedReJitInfo(pMD);
- if (pReJitInfoCurrent != NULL)
+ ILCodeVersion activeILCodeVersion = pCodeVersionManager->GetActiveILCodeVersion(pMD);
+ NativeCodeVersion activeChild = activeILCodeVersion.GetActiveNativeCodeVersion(pMD);
+ CopyNativeCodeVersionToReJitData(activeChild, activeChild, &methodDescData->rejitDataCurrent);
+
+ if (!activeChild.IsNull())
{
- CopyReJitInfoToReJitData(pReJitInfoCurrent, &methodDescData->rejitDataCurrent);
+ // This was already set previously, but MethodDesc::GetNativeCode is potentially not aware of
+ // a new native code version, so this is more accurate.
+ methodDescData->NativeCodeAddr = activeChild.GetNativeCode();
}
// Requested ReJitInfo
_ASSERTE(methodDescData->rejitDataRequested.rejitID == 0);
if (methodDescData->requestedIP != NULL)
{
- ReJitInfo * pReJitInfoRequested = pReJitMgr->FindReJitInfo(
- pMD,
- CLRDATA_ADDRESS_TO_TADDR(methodDescData->requestedIP),
- NULL /* reJitId */);
+ NativeCodeVersion nativeCodeVersionRequested = pCodeVersionManager->GetNativeCodeVersion(
+ pMD,
+ CLRDATA_ADDRESS_TO_TADDR(methodDescData->requestedIP));
- if (pReJitInfoRequested != NULL)
+ if (!nativeCodeVersionRequested.IsNull())
{
- CopyReJitInfoToReJitData(pReJitInfoRequested, &methodDescData->rejitDataRequested);
+ CopyNativeCodeVersionToReJitData(nativeCodeVersionRequested, activeChild, &methodDescData->rejitDataRequested);
}
}
// Total number of jitted rejit versions
ULONG cJittedRejitVersions;
- if (SUCCEEDED(pReJitMgr->GetReJITIDs(pMD, 0 /* cReJitIds */, &cJittedRejitVersions, NULL /* reJitIds */)))
+ if (SUCCEEDED(ReJitManager::GetReJITIDs(pMD, 0 /* cReJitIds */, &cJittedRejitVersions, NULL /* reJitIds */)))
{
methodDescData->cJittedRejitVersions = cJittedRejitVersions;
}
@@ -994,31 +996,30 @@ HRESULT ClrDataAccess::GetMethodDescData(
// Prepare array to populate with rejitids. "+ 1" because GetReJITIDs
// returns all available rejitids, including the rejitid for the one non-reverted
// current version.
- ReJITID * rgReJitIds = reJitIds.OpenRawBuffer(cRevertedRejitVersions + 1);
+ ReJITID *rgReJitIds = reJitIds.OpenRawBuffer(cRevertedRejitVersions + 1);
if (rgReJitIds != NULL)
{
- hr = pReJitMgr->GetReJITIDs(pMD, cRevertedRejitVersions + 1, &cReJitIds, rgReJitIds);
+ hr = ReJitManager::GetReJITIDs(pMD, cRevertedRejitVersions + 1, &cReJitIds, rgReJitIds);
if (SUCCEEDED(hr))
{
// Go through rejitids. For each reverted one, populate a entry in rgRevertedRejitData
reJitIds.CloseRawBuffer(cReJitIds);
ULONG iRejitDataReverted = 0;
- for (COUNT_T i=0;
- (i < cReJitIds) && (iRejitDataReverted < cRevertedRejitVersions);
- i++)
+ ILCodeVersion activeVersion = pCodeVersionManager->GetActiveILCodeVersion(pMD);
+ for (COUNT_T i = 0;
+ (i < cReJitIds) && (iRejitDataReverted < cRevertedRejitVersions);
+ i++)
{
- ReJitInfo * pRejitInfo = pReJitMgr->FindReJitInfo(
- pMD,
- NULL /* pCodeStart */,
- reJitIds[i]);
+ ILCodeVersion ilCodeVersion = pCodeVersionManager->GetILCodeVersion(pMD, reJitIds[i]);
- if ((pRejitInfo == NULL) ||
- (pRejitInfo->m_pShared->GetState() != SharedReJitInfo::kStateReverted))
+ if ((ilCodeVersion.IsNull()) ||
+ (ilCodeVersion == activeVersion))
{
continue;
}
- CopyReJitInfoToReJitData(pRejitInfo, &rgRevertedRejitData[iRejitDataReverted]);
+ NativeCodeVersion activeRejitChild = ilCodeVersion.GetActiveNativeCodeVersion(pMD);
+ CopyNativeCodeVersionToReJitData(activeRejitChild, activeChild, &rgRevertedRejitData[iRejitDataReverted]);
iRejitDataReverted++;
}
// pcNeededRevertedRejitData != NULL as per condition at top of function (cuz rgRevertedRejitData !=
@@ -1034,7 +1035,7 @@ HRESULT ClrDataAccess::GetMethodDescData(
*pcNeededRevertedRejitData = 0;
}
EX_END_CATCH(SwallowAllExceptions)
- hr = S_OK; // Failure to get rejitids is not fatal
+ hr = S_OK; // Failure to get rejitids is not fatal
#endif // FEATURE_REJIT
#if defined(HAVE_GCCOVER)
@@ -1076,7 +1077,7 @@ HRESULT ClrDataAccess::GetMethodDescData(
}
}
}
- }
+ }
}
}
@@ -1084,6 +1085,126 @@ HRESULT ClrDataAccess::GetMethodDescData(
return hr;
}
+HRESULT ClrDataAccess::GetTieredVersions(
+ CLRDATA_ADDRESS methodDesc,
+ int rejitId,
+ struct DacpTieredVersionData *nativeCodeAddrs,
+ int cNativeCodeAddrs,
+ int *pcNativeCodeAddrs)
+{
+ if (methodDesc == 0 || cNativeCodeAddrs == 0 || pcNativeCodeAddrs == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pcNativeCodeAddrs = 0;
+
+ SOSDacEnter();
+
+#ifdef FEATURE_REJIT
+ PTR_MethodDesc pMD = PTR_MethodDesc(TO_TADDR(methodDesc));
+
+ // If rejit info is appropriate, get the following:
+ // * ReJitInfo for the current, active version of the method
+ // * ReJitInfo for the requested IP (for !ip2md and !u)
+ // * ReJitInfos for all reverted versions of the method (up to
+ // cRevertedRejitVersions)
+ //
+ // Minidumps will not have all this rejit info, and failure to get rejit info
+ // should not be fatal. So enclose all rejit stuff in a try.
+
+ EX_TRY
+ {
+ CodeVersionManager *pCodeVersionManager = pMD->GetCodeVersionManager();
+
+ // Total number of jitted rejit versions
+ ULONG cJittedRejitVersions;
+ if (!SUCCEEDED(ReJitManager::GetReJITIDs(pMD, 0 /* cReJitIds */, &cJittedRejitVersions, NULL /* reJitIds */)))
+ {
+ goto cleanup;
+ }
+
+ if ((ULONG)rejitId >= cJittedRejitVersions)
+ {
+ hr = E_INVALIDARG;
+ goto cleanup;
+ }
+
+ ULONG cReJitIds;
+ StackSArray<ReJITID> reJitIds;
+
+ // Prepare array to populate with rejitids.
+ ReJITID *rgReJitIds = reJitIds.OpenRawBuffer(cJittedRejitVersions);
+ if (rgReJitIds != NULL)
+ {
+ hr = ReJitManager::GetReJITIDs(pMD, cJittedRejitVersions, &cReJitIds, rgReJitIds);
+ if (SUCCEEDED(hr))
+ {
+ reJitIds.CloseRawBuffer(cReJitIds);
+
+ ILCodeVersion ilCodeVersion = pCodeVersionManager->GetILCodeVersion(pMD, reJitIds[rejitId]);
+
+ if (ilCodeVersion.IsNull())
+ {
+ hr = S_FALSE;
+ goto cleanup;
+ }
+
+ NativeCodeVersionCollection nativeCodeVersions = ilCodeVersion.GetNativeCodeVersions(pMD);
+ int count = 0;
+ for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++)
+ {
+ nativeCodeAddrs[count].NativeCodeAddr = (*iter).GetNativeCode();
+ PTR_NativeCodeVersionNode pNode = (*iter).AsNode();
+ nativeCodeAddrs[count].NativeCodeVersionNodePtr = TO_CDADDR(PTR_TO_TADDR(pNode));
+
+ if (pMD->IsEligibleForTieredCompilation())
+ {
+ switch ((*iter).GetOptimizationTier())
+ {
+ default:
+ nativeCodeAddrs[count].TieredInfo = DacpTieredVersionData::TIERED_UNKNOWN;
+ break;
+ case NativeCodeVersion::OptimizationTier0:
+ nativeCodeAddrs[count].TieredInfo = DacpTieredVersionData::TIERED_0;
+ break;
+ case NativeCodeVersion::OptimizationTier1:
+ nativeCodeAddrs[count].TieredInfo = DacpTieredVersionData::TIERED_1;
+ break;
+ }
+ }
+ else
+ {
+ nativeCodeAddrs[count].TieredInfo = DacpTieredVersionData::NON_TIERED;
+ }
+
+ ++count;
+
+ if (count >= cNativeCodeAddrs)
+ {
+ hr = S_FALSE;
+ break;
+ }
+ }
+
+ *pcNativeCodeAddrs = count;
+ }
+ }
+ }
+ EX_CATCH
+ {
+ hr = E_FAIL;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+
+cleanup:
+ ;
+#endif // FEATURE_REJIT
+
+ SOSDacLeave();
+ return hr;
+}
+
HRESULT
ClrDataAccess::GetMethodDescTransparencyData(CLRDATA_ADDRESS methodDesc, struct DacpMethodDescTransparencyData *data)
{
@@ -1100,13 +1221,6 @@ ClrDataAccess::GetMethodDescTransparencyData(CLRDATA_ADDRESS methodDesc, struct
else
{
ZeroMemory(data, sizeof(DacpMethodDescTransparencyData));
-
- if (pMD->HasCriticalTransparentInfo())
- {
- data->bHasCriticalTransparentInfo = pMD->HasCriticalTransparentInfo();
- data->bIsCritical = pMD->IsCritical();
- data->bIsTreatAsSafe = pMD->IsTreatAsSafe();
- }
}
SOSDacLeave();
@@ -1857,14 +1971,6 @@ ClrDataAccess::GetMethodTableTransparencyData(CLRDATA_ADDRESS mt, struct DacpMet
else
{
ZeroMemory(pTransparencyData, sizeof(DacpMethodTableTransparencyData));
-
- EEClass * pClass = pMT->GetClass();
- if (pClass->HasCriticalTransparentInfo())
- {
- pTransparencyData->bHasCriticalTransparentInfo = pClass->HasCriticalTransparentInfo();
- pTransparencyData->bIsCritical = pClass->IsCritical() || pClass->IsAllCritical();
- pTransparencyData->bIsTreatAsSafe = pClass->IsTreatAsSafe();
- }
}
SOSDacLeave();
@@ -3871,7 +3977,7 @@ HRESULT ClrDataAccess::GetClrWatsonBucketsWorker(Thread * pThread, GenericModeBl
if (ohThrowable != NULL)
{
// Get the object from handle and check if the throwable is preallocated or not
- OBJECTREF oThrowable = ::HndFetchHandle(ohThrowable);
+ OBJECTREF oThrowable = ObjectFromHandle(ohThrowable);
if (oThrowable != NULL)
{
// Does the throwable have buckets?
@@ -4165,7 +4271,7 @@ HRESULT ClrDataAccess::GetCCWData(CLRDATA_ADDRESS ccw, struct DacpCCWData *ccwDa
ccwData->isAggregated = pCCW->GetSimpleWrapper()->IsAggregated();
if (pCCW->GetObjectHandle() != NULL)
- ccwData->managedObject = PTR_CDADDR(::HndFetchHandle(pCCW->GetObjectHandle()));
+ ccwData->managedObject = PTR_CDADDR(ObjectFromHandle(pCCW->GetObjectHandle()));
// count the number of COM vtables
ccwData->interfaceCount = 0;
diff --git a/src/debug/di/hash.cpp b/src/debug/di/hash.cpp
index 554edaa308..54d4216156 100644
--- a/src/debug/di/hash.cpp
+++ b/src/debug/di/hash.cpp
@@ -609,7 +609,7 @@ HRESULT CordbHashTableEnum::QueryInterface(REFIID id, void **pInterface)
void CordbHashTableEnum::AssertValid()
{
// @todo - Our behavior is undefined when enumerating a collection that changes underneath us.
- // We'd love to just call this situatation illegal, but clients could very reasonably taken a depedency
+ // We'd love to just call this situation illegal, but clients could have very reasonably taken a dependency
// on it. Various APIs (eg, ICDStepper::Deactivate) may remove items from the hash.
// So we need to figure out what the behavior is here, spec it, and then enforce that and enable the
// strongest asserts possible there.
diff --git a/src/debug/di/module.cpp b/src/debug/di/module.cpp
index 36cc6f5f9e..5d1d3da427 100644
--- a/src/debug/di/module.cpp
+++ b/src/debug/di/module.cpp
@@ -3391,15 +3391,15 @@ mdSignature CordbILCode::GetLocalVarSigToken()
return m_localVarSigToken;
}
-CordbReJitILCode::CordbReJitILCode(CordbFunction *pFunction, SIZE_T encVersion, VMPTR_SharedReJitInfo vmSharedReJitInfo) :
-CordbILCode(pFunction, TargetBuffer(), encVersion, mdSignatureNil, VmPtrToCookie(vmSharedReJitInfo)),
+CordbReJitILCode::CordbReJitILCode(CordbFunction *pFunction, SIZE_T encVersion, VMPTR_ILCodeVersionNode vmILCodeVersionNode) :
+CordbILCode(pFunction, TargetBuffer(), encVersion, mdSignatureNil, VmPtrToCookie(vmILCodeVersionNode)),
m_cClauses(0),
m_cbLocalIL(0),
m_cILMap(0)
{
- _ASSERTE(!vmSharedReJitInfo.IsNull());
+ _ASSERTE(!vmILCodeVersionNode.IsNull());
DacSharedReJitInfo data = { 0 };
- IfFailThrow(GetProcess()->GetDAC()->GetSharedReJitInfoData(vmSharedReJitInfo, &data));
+ IfFailThrow(GetProcess()->GetDAC()->GetILCodeVersionNodeData(vmILCodeVersionNode, &data));
IfFailThrow(Init(&data));
}
diff --git a/src/debug/di/rsfunction.cpp b/src/debug/di/rsfunction.cpp
index 8621edcedc..bf3c49bb98 100644
--- a/src/debug/di/rsfunction.cpp
+++ b/src/debug/di/rsfunction.cpp
@@ -557,14 +557,12 @@ HRESULT CordbFunction::GetActiveReJitRequestILCode(ICorDebugILCode **ppReJitedIL
{
*ppReJitedILCode = NULL;
- VMPTR_ReJitInfo vmReJitInfo = VMPTR_ReJitInfo::NullPtr();
- GetProcess()->GetDAC()->GetReJitInfo(GetModule()->m_vmModule, m_MDToken, &vmReJitInfo);
- if (!vmReJitInfo.IsNull())
+ VMPTR_ILCodeVersionNode vmILCodeVersionNode = VMPTR_ILCodeVersionNode::NullPtr();
+ GetProcess()->GetDAC()->GetActiveRejitILCodeVersionNode(GetModule()->m_vmModule, m_MDToken, &vmILCodeVersionNode);
+ if (!vmILCodeVersionNode.IsNull())
{
- VMPTR_SharedReJitInfo vmSharedReJitInfo = VMPTR_SharedReJitInfo::NullPtr();
- GetProcess()->GetDAC()->GetSharedReJitInfo(vmReJitInfo, &vmSharedReJitInfo);
RSSmartPtr<CordbReJitILCode> pILCode;
- IfFailThrow(LookupOrCreateReJitILCode(vmSharedReJitInfo, &pILCode));
+ IfFailThrow(LookupOrCreateReJitILCode(vmILCodeVersionNode, &pILCode));
IfFailThrow(pILCode->QueryInterface(IID_ICorDebugILCode, (void**)ppReJitedILCode));
}
}
@@ -1165,21 +1163,21 @@ VOID CordbFunction::NotifyCodeCreated(CordbNativeCode* nativeCode)
// If the CordbReJitILCode doesn't exist, it creates it.
//
//
-HRESULT CordbFunction::LookupOrCreateReJitILCode(VMPTR_SharedReJitInfo vmSharedReJitInfo, CordbReJitILCode** ppILCode)
+HRESULT CordbFunction::LookupOrCreateReJitILCode(VMPTR_ILCodeVersionNode vmILCodeVersionNode, CordbReJitILCode** ppILCode)
{
INTERNAL_API_ENTRY(this);
HRESULT hr = S_OK;
_ASSERTE(GetProcess()->ThreadHoldsProcessLock());
- CordbReJitILCode * pILCode = m_reJitILCodes.GetBase(VmPtrToCookie(vmSharedReJitInfo));
+ CordbReJitILCode * pILCode = m_reJitILCodes.GetBase(VmPtrToCookie(vmILCodeVersionNode));
// special case non-existance as need to add to the hash table too
if (pILCode == NULL)
{
// we don't yet support ENC and ReJIT together, so the version should be 1
_ASSERTE(m_dwEnCVersionNumber == 1);
- RSInitHolder<CordbReJitILCode> pILCodeHolder(new CordbReJitILCode(this, 1, vmSharedReJitInfo));
+ RSInitHolder<CordbReJitILCode> pILCodeHolder(new CordbReJitILCode(this, 1, vmILCodeVersionNode));
IfFailRet(m_reJitILCodes.AddBase(pILCodeHolder));
pILCode = pILCodeHolder;
pILCodeHolder.ClearAndMarkDontNeuter();
diff --git a/src/debug/di/rspriv.h b/src/debug/di/rspriv.h
index 1abe087693..e0489c53ad 100644
--- a/src/debug/di/rspriv.h
+++ b/src/debug/di/rspriv.h
@@ -5431,7 +5431,7 @@ public:
HRESULT GetILCode(CordbILCode ** ppCode);
// Finds or creates an ILCode for a given rejit request
- HRESULT LookupOrCreateReJitILCode(VMPTR_SharedReJitInfo vmSharedRejitInfo,
+ HRESULT LookupOrCreateReJitILCode(VMPTR_ILCodeVersionNode vmILCodeVersionNode,
CordbReJitILCode** ppILCode);
@@ -5775,7 +5775,7 @@ class CordbReJitILCode : public CordbILCode, public ICorDebugILCode, public ICor
{
public:
// Initialize a new CordbILCode instance
- CordbReJitILCode(CordbFunction *pFunction, SIZE_T encVersion, VMPTR_SharedReJitInfo vmSharedReJitInfo);
+ CordbReJitILCode(CordbFunction *pFunction, SIZE_T encVersion, VMPTR_ILCodeVersionNode vmILCodeVersionNode);
//-----------------------------------------------------------
// IUnknown
diff --git a/src/debug/di/rsstackwalk.cpp b/src/debug/di/rsstackwalk.cpp
index 8ade4c9a74..466e113d8f 100644
--- a/src/debug/di/rsstackwalk.cpp
+++ b/src/debug/di/rsstackwalk.cpp
@@ -749,13 +749,16 @@ HRESULT CordbStackWalk::GetFrameWorker(ICorDebugFrame ** ppFrame)
RSSmartPtr<CordbReJitILCode> pReJitCode;
EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY
{
- VMPTR_ReJitInfo reJitInfo = VMPTR_ReJitInfo::NullPtr();
- IfFailThrow(GetProcess()->GetDAC()->GetReJitInfo(pJITFuncData->vmNativeCodeMethodDescToken, pJITFuncData->nativeStartAddressPtr, &reJitInfo));
- if (!reJitInfo.IsNull())
+ VMPTR_NativeCodeVersionNode vmNativeCodeVersionNode = VMPTR_NativeCodeVersionNode::NullPtr();
+ IfFailThrow(GetProcess()->GetDAC()->GetNativeCodeVersionNode(pJITFuncData->vmNativeCodeMethodDescToken, pJITFuncData->nativeStartAddressPtr, &vmNativeCodeVersionNode));
+ if (!vmNativeCodeVersionNode.IsNull())
{
- VMPTR_SharedReJitInfo sharedReJitInfo = VMPTR_SharedReJitInfo::NullPtr();
- IfFailThrow(GetProcess()->GetDAC()->GetSharedReJitInfo(reJitInfo, &sharedReJitInfo));
- IfFailThrow(pFunction->LookupOrCreateReJitILCode(sharedReJitInfo, &pReJitCode));
+ VMPTR_ILCodeVersionNode vmILCodeVersionNode = VMPTR_ILCodeVersionNode::NullPtr();
+ IfFailThrow(GetProcess()->GetDAC()->GetILCodeVersionNode(vmNativeCodeVersionNode, &vmILCodeVersionNode));
+ if (!vmILCodeVersionNode.IsNull())
+ {
+ IfFailThrow(pFunction->LookupOrCreateReJitILCode(vmILCodeVersionNode, &pReJitCode));
+ }
}
}
EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY
diff --git a/src/debug/ee/debugger.cpp b/src/debug/ee/debugger.cpp
index 609a1f6b2a..01833b3730 100644
--- a/src/debug/ee/debugger.cpp
+++ b/src/debug/ee/debugger.cpp
@@ -26,7 +26,6 @@
#include "typeparse.h"
#include "debuginfostore.h"
#include "generics.h"
-#include "../../vm/security.h"
#include "../../vm/methoditer.h"
#include "../../vm/encee.h"
#include "../../vm/dwreport.h"
diff --git a/src/debug/ee/funceval.cpp b/src/debug/ee/funceval.cpp
index dcfe5a5dcf..33f16f433e 100644
--- a/src/debug/ee/funceval.cpp
+++ b/src/debug/ee/funceval.cpp
@@ -1559,22 +1559,7 @@ void ResolveFuncEvalGenericArgInfo(DebuggerEval *pDE)
// We better have a MethodDesc at this point.
_ASSERTE(pDE->m_md != NULL);
-
- IMDInternalImport *pInternalImport = pDE->m_md->GetMDImport();
- DWORD dwAttr;
- if (FAILED(pInternalImport->GetMethodDefProps(pDE->m_methodToken, &dwAttr)))
- {
- COMPlusThrow(kArgumentException, W("Argument_InvalidGenericArg"));
- }
-
- if (dwAttr & mdRequireSecObject)
- {
- // command window cannot evaluate a function with mdRequireSecObject is turned on because
- // this is expecting to put a security object into caller's frame which we don't have.
- //
- COMPlusThrow(kArgumentException,W("Argument_CantCallSecObjFunc"));
- }
-
+
ValidateFuncEvalReturnType(pDE->m_evalType , pDE->m_md->GetMethodTable());
// If this is a new object operation, then we should have a .ctor.
diff --git a/src/debug/inc/arm/primitives.h b/src/debug/inc/arm/primitives.h
index 0bac542667..1cceeffeab 100644
--- a/src/debug/inc/arm/primitives.h
+++ b/src/debug/inc/arm/primitives.h
@@ -30,7 +30,11 @@ typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE;
#define STACKWALK_CONTROLPC_ADJUST_OFFSET 2
#define CORDbg_BREAK_INSTRUCTION_SIZE 2
+#ifdef __linux__
+#define CORDbg_BREAK_INSTRUCTION (USHORT)0xde01
+#else
#define CORDbg_BREAK_INSTRUCTION (USHORT)0xdefe
+#endif
inline CORDB_ADDRESS GetPatchEndAddr(CORDB_ADDRESS patchAddr)
{
diff --git a/src/debug/inc/dacdbiinterface.h b/src/debug/inc/dacdbiinterface.h
index 4077ad426a..5e765c94f3 100644
--- a/src/debug/inc/dacdbiinterface.h
+++ b/src/debug/inc/dacdbiinterface.h
@@ -2532,6 +2532,7 @@ public:
virtual
HRESULT GetPEFileMDInternalRW(VMPTR_PEFile vmPEFile, OUT TADDR* pAddrMDInternalRW) = 0;
+ // DEPRECATED - use GetActiveRejitILCodeVersionNode
// Retrieves the active ReJitInfo for a given module/methodDef, if it exists.
// Active is defined as after GetReJitParameters returns from the profiler dll and
// no call to Revert has completed yet.
@@ -2550,17 +2551,16 @@ public:
virtual
HRESULT GetReJitInfo(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ReJitInfo* pReJitInfo) = 0;
- // Retrieves the active ReJitInfo for a given MethodDesc/code address, if it exists.
- // Active is defined as after GetReJitParameters returns from the profiler dll and
- // no call to Revert has completed yet.
+ // DEPRECATED - use GetNativeCodeVersionNode
+ // Retrieves the ReJitInfo for a given MethodDesc/code address, if it exists.
//
//
// Arguments:
// vmMethod - The method to look for
// codeStartAddress - The code start address disambiguates between multiple rejitted instances
// of the method.
- // pReJitInfo - [out] The RejitInfo request, if any, that is active on this method. If no request
- // is active this will be pReJitInfo->IsNull() == TRUE.
+ // pReJitInfo - [out] The RejitInfo request that corresponds to this MethodDesc/code address, if it exists.
+ // NULL otherwise.
//
// Returns:
// S_OK regardless of whether a rejit request is active or not, as long as the answer is certain
@@ -2569,7 +2569,7 @@ public:
virtual
HRESULT GetReJitInfo(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_ReJitInfo* pReJitInfo) = 0;
-
+ // DEPRECATED - use GetILCodeVersion
// Retrieves the SharedReJitInfo for a given ReJitInfo.
//
//
@@ -2584,6 +2584,7 @@ public:
virtual
HRESULT GetSharedReJitInfo(VMPTR_ReJitInfo vmReJitInfo, VMPTR_SharedReJitInfo* pSharedReJitInfo) = 0;
+ // DEPRECATED - use GetILCodeVersionData
// Retrieves useful data from a SharedReJitInfo such as IL code and IL mapping.
//
//
@@ -2630,6 +2631,71 @@ public:
virtual
HRESULT GetMDStructuresVersion(ULONG32* pMDStructuresVersion) = 0;
+ // Retrieves the active rejit ILCodeVersionNode for a given module/methodDef, if it exists.
+ // Active is defined as after GetReJitParameters returns from the profiler dll and
+ // no call to Revert has completed yet.
+ //
+ //
+ // Arguments:
+ // vmModule - The module to search in
+ // methodTk - The methodDef token indicates the method within the module to check
+ // pILCodeVersionNode - [out] The Rejit request, if any, that is active on this method. If no request
+ // is active this will be pILCodeVersionNode->IsNull() == TRUE.
+ //
+ // Returns:
+ // S_OK regardless of whether a rejit request is active or not, as long as the answer is certain
+ // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible
+ //
+ virtual
+ HRESULT GetActiveRejitILCodeVersionNode(VMPTR_Module vmModule, mdMethodDef methodTk, OUT VMPTR_ILCodeVersionNode* pVmILCodeVersionNode) = 0;
+
+ // Retrieves the NativeCodeVersionNode for a given MethodDesc/code address, if it exists.
+ // NOTE: The initial (default) code generated for a MethodDesc is a valid MethodDesc/code address pair but it won't have a corresponding
+ // NativeCodeVersionNode.
+ //
+ //
+ // Arguments:
+ // vmMethod - The method to look for
+ // codeStartAddress - The code start address disambiguates between multiple jitted instances of the method.
+ // pVmNativeCodeVersionNode - [out] The NativeCodeVersionNode request that corresponds to this MethodDesc/code address, if it exists.
+ // NULL otherwise.
+ //
+ // Returns:
+ // S_OK regardless of whether a rejit request is active or not, as long as the answer is certain
+ // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible
+ //
+ virtual
+ HRESULT GetNativeCodeVersionNode(VMPTR_MethodDesc vmMethod, CORDB_ADDRESS codeStartAddress, OUT VMPTR_NativeCodeVersionNode* pVmNativeCodeVersionNode) = 0;
+
+ // Retrieves the ILCodeVersionNode for a given NativeCodeVersionNode.
+ // This may return a NULL node if the native code belongs to the default IL version for this this method.
+ //
+ //
+ // Arguments:
+ // vmNativeCodeVersionNode - The NativeCodeVersionNode to inspect
+ // pVmILCodeVersionNode - [out] The ILCodeVersionNode that is pointed to by vmNativeCodeVersionNode, if any.
+ //
+ // Returns:
+ // S_OK if no error
+ // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible
+ //
+ virtual
+ HRESULT GetILCodeVersionNode(VMPTR_NativeCodeVersionNode vmNativeCodeVersionNode, VMPTR_ILCodeVersionNode* pVmILCodeVersionNode) = 0;
+
+ // Retrieves useful data from an ILCodeVersion such as IL code and IL mapping.
+ //
+ //
+ // Arguments:
+ // ilCodeVersionNode - The ILCodeVersionNode to inspect
+ // pData - [out] Various properties of the ILCodeVersionNode such as IL code and IL mapping.
+ //
+ // Returns:
+ // S_OK if no error
+ // error HRESULTs such as CORDBG_READ_VIRTUAL_FAILURE are possible
+ //
+ virtual
+ HRESULT GetILCodeVersionNodeData(VMPTR_ILCodeVersionNode ilCodeVersionNode, DacSharedReJitInfo* pData) = 0;
+
// The following tag tells the DD-marshalling tool to stop scanning.
// END_MARSHAL
diff --git a/src/debug/inc/dacdbistructures.h b/src/debug/inc/dacdbistructures.h
index d6f2c0b943..a8ff06745b 100644
--- a/src/debug/inc/dacdbistructures.h
+++ b/src/debug/inc/dacdbistructures.h
@@ -328,7 +328,7 @@ public:
// number of fixed arguments plus number of varargs
ULONG32 m_allArgsCount;
- // indicates whether an attempt has been made toinitialize the var data already
+ // indicates whether an attempt has been made to initialize the var data already
bool m_fInitialized;
}; // class NativeVarData
diff --git a/src/debug/inc/dbgipcevents.h b/src/debug/inc/dbgipcevents.h
index 1b4dec7c16..dc900660c3 100644
--- a/src/debug/inc/dbgipcevents.h
+++ b/src/debug/inc/dbgipcevents.h
@@ -879,7 +879,8 @@ DEFINE_VMPTR(class SimpleRWLock, PTR_SimpleRWLock, VMPTR_SimpleRWLock);
DEFINE_VMPTR(class SimpleRWLock, PTR_SimpleRWLock, VMPTR_RWLock);
DEFINE_VMPTR(struct ReJitInfo, PTR_ReJitInfo, VMPTR_ReJitInfo);
DEFINE_VMPTR(struct SharedReJitInfo, PTR_SharedReJitInfo, VMPTR_SharedReJitInfo);
-
+DEFINE_VMPTR(class NativeCodeVersionNode, PTR_NativeCodeVersionNode, VMPTR_NativeCodeVersionNode);
+DEFINE_VMPTR(class ILCodeVersionNode, PTR_ILCodeVersionNode, VMPTR_ILCodeVersionNode);
typedef CORDB_ADDRESS GENERICS_TYPE_TOKEN;
diff --git a/src/dlls/mscordac/mscordac_unixexports.src b/src/dlls/mscordac/mscordac_unixexports.src
index d898d823e4..99ed375ce7 100644
--- a/src/dlls/mscordac/mscordac_unixexports.src
+++ b/src/dlls/mscordac/mscordac_unixexports.src
@@ -22,6 +22,7 @@ PAL_GetResourceString
PAL_get_stdout
PAL_get_stderr
PAL_GetCurrentThread
+PAL_GetCpuLimit
PAL_GetSymbolModuleBase
PAL_GetTransportPipeName
PAL_InitializeDLL
@@ -40,6 +41,7 @@ PAL__wcstoui64
PAL_wcstoul
PAL_iswprint
PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange
+PAL_VirtualUnwindOutOfProc
PAL_wcslen
PAL_wcsncmp
PAL_wcsrchr
diff --git a/src/dlls/mscoree/coreclr/CMakeLists.txt b/src/dlls/mscoree/coreclr/CMakeLists.txt
index 81aaad45f0..8796cc16a5 100644
--- a/src/dlls/mscoree/coreclr/CMakeLists.txt
+++ b/src/dlls/mscoree/coreclr/CMakeLists.txt
@@ -73,10 +73,6 @@ if(FEATURE_MERGE_JIT_AND_ENGINE)
set(CLRJIT_STATIC clrjit_static)
endif(FEATURE_MERGE_JIT_AND_ENGINE)
-if(FEATURE_STANDALONE_GC_ONLY)
- set(STANDALONE_GC gc_standalone)
-endif(FEATURE_STANDALONE_GC_ONLY)
-
# IMPORTANT! Please do not rearrange the order of the libraries. The linker on Linux is
# order dependent and changing the order can result in undefined symbols in the shared
# library.
@@ -87,7 +83,6 @@ set(CORECLR_LIBRARIES
debug-pal
${LIB_UNWINDER}
cee_wks
- ${STANDALONE_GC}
${END_LIBRARY_GROUP} # End group of libraries that have circular references
mdcompiler_wks
mdruntime_wks
@@ -133,12 +128,6 @@ else()
)
endif(WIN32)
-if(CLR_CMAKE_PLATFORM_UNIX AND FEATURE_STANDALONE_GC)
- list(APPEND CORECLR_LIBRARIES
- gc_unix
- )
-endif(CLR_CMAKE_PLATFORM_UNIX AND FEATURE_STANDALONE_GC)
-
if(CLR_CMAKE_PLATFORM_UNIX AND FEATURE_EVENT_TRACE)
list(APPEND CORECLR_LIBRARIES
eventprovider
diff --git a/src/dlls/mscoree/mscorwks_unixexports.src b/src/dlls/mscoree/mscorwks_unixexports.src
index 5ca311906e..0da8942a2b 100644
--- a/src/dlls/mscoree/mscorwks_unixexports.src
+++ b/src/dlls/mscoree/mscorwks_unixexports.src
@@ -44,8 +44,6 @@ FlushFileBuffers
FormatMessageW
FreeEnvironmentStringsW
GetACP
-GetComputerNameW
-GetConsoleCP
GetConsoleOutputCP
GetCurrentDirectoryW
GetCurrentProcess
@@ -55,7 +53,6 @@ GetEnvironmentStringsW
GetEnvironmentVariableW
GetFileAttributesExW
GetFileSize
-GetFileType
GetFullPathNameW
GetLongPathNameW
GetProcAddress
@@ -67,7 +64,6 @@ GetTempPathW
LocalAlloc
LocalReAlloc
LocalFree
-LockFile
lstrlenA
lstrlenW
MapViewOfFile
@@ -87,7 +83,6 @@ ReleaseMutex
ReleaseSemaphore
RemoveDirectoryW
ResetEvent
-RtlZeroMemory
SetCurrentDirectoryW
SetEndOfFile
SetEnvironmentVariableW
@@ -95,11 +90,9 @@ SetErrorMode
SetEvent
SetFileAttributesW
SetFilePointer
-SetFileTime
SysAllocStringLen
SysFreeString
SysStringLen
-UnlockFile
UnmapViewOfFile
VirtualAlloc
VirtualFree
@@ -113,5 +106,3 @@ SetThreadAffinityMask
GetThreadGroupAffinity
GetCurrentProcessorNumberEx
-; Function for initializing a standalone GC
-InitializeGarbageCollector
diff --git a/src/gc/CMakeLists.txt b/src/gc/CMakeLists.txt
index 4de3f4e412..21eb66070a 100644
--- a/src/gc/CMakeLists.txt
+++ b/src/gc/CMakeLists.txt
@@ -1,15 +1,5 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
-include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(BEFORE ${CLR_DIR}/src/vm)
-include_directories(BEFORE ${CLR_DIR}/src/vm/${ARCH_SOURCES_DIR})
-
-add_definitions(-DBUILD_AS_STANDALONE)
-
-if(CLR_CMAKE_PLATFORM_UNIX)
- add_compile_options(-fPIC)
-endif(CLR_CMAKE_PLATFORM_UNIX)
-
set( GC_SOURCES
gcconfig.cpp
gccommon.cpp
@@ -26,12 +16,48 @@ set( GC_SOURCES
gceewks.cpp
handletablecache.cpp)
-if(NOT CLR_CMAKE_PLATFORM_UNIX)
-set ( GC_SOURCES
- ${GC_SOURCES}
- windows/gcenv.windows.cpp)
-endif(NOT CLR_CMAKE_PLATFORM_UNIX)
+if(CLR_CMAKE_PLATFORM_UNIX)
+ include(unix/configure.cmake)
+ set ( GC_SOURCES
+ ${GC_SOURCES}
+ unix/gcenv.unix.cpp
+ unix/events.cpp
+ unix/cgroup.cpp)
+else()
+ set ( GC_SOURCES
+ ${GC_SOURCES}
+ windows/gcenv.windows.cpp)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+if(WIN32)
+ set (GC_LINK_LIBRARIES
+ ${STATIC_MT_CRT_LIB}
+ ${STATIC_MT_VCRT_LIB}
+ kernel32.lib)
+else()
+ set (GC_LINK_LIBRARIES)
+endif(WIN32)
convert_to_absolute_path(GC_SOURCES ${GC_SOURCES})
-add_library_clr(gc_standalone STATIC ${GC_SOURCES})
+add_library_clr(gc SHARED ${GC_SOURCES})
+target_link_libraries(gc ${GC_LINK_LIBRARIES})
+install_clr(gc)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_compile_options(-fPIC)
+ # dprintf causes many warnings
+ add_compile_options(-Wno-format)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+add_definitions(-DBUILD_AS_STANDALONE)
+if(CLR_CMAKE_PLATFORM_DARWIN)
+ # The implementation of GCToOSInterface on MacOS makes use of non-POSIX
+ # pthreads APIs, which by default are not included in the pthreads header
+ # unless we define this macro.
+ add_definitions(-D_DARWIN_C_SOURCE)
+endif(CLR_CMAKE_PLATFORM_DARWIN)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/env)
+
diff --git a/src/gc/WarningControl.h b/src/gc/WarningControl.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/gc/WarningControl.h
diff --git a/src/gc/env/gcenv.base.h b/src/gc/env/gcenv.base.h
index 1e9406c296..fb2f9da488 100644
--- a/src/gc/env/gcenv.base.h
+++ b/src/gc/env/gcenv.base.h
@@ -7,10 +7,11 @@
// Sets up basic environment for CLR GC
//
-#define FEATURE_REDHAWK 1
-#define FEATURE_CONSERVATIVE_GC 1
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif // _MSC_VER
-#define GCENV_INCLUDED
+#define FEATURE_REDHAWK 1
#define REDHAWK_PALIMPORT extern "C"
#define REDHAWK_PALAPI __stdcall
@@ -22,7 +23,11 @@
#else // __clang__
#define __forceinline inline
#endif // __clang__
-#endif // !_MSC_VER
+// [LOCALGC TODO] is there a better place for this?
+#define NOINLINE __attribute__((noinline))
+#else // !_MSC_VER
+#define NOINLINE __declspec(noinline)
+#endif // _MSC_VER
#ifndef SIZE_T_MAX
#define SIZE_T_MAX ((size_t)-1)
@@ -39,6 +44,8 @@
typedef int BOOL;
typedef uint32_t DWORD;
+typedef uint64_t DWORD64;
+typedef uint32_t ULONG;
// -----------------------------------------------------------------------------------------------------------
// HRESULT subset.
@@ -59,12 +66,8 @@ inline HRESULT HRESULT_FROM_WIN32(unsigned long x)
}
#define S_OK 0x0
-#define S_FALSE 0x1
#define E_FAIL 0x80004005
#define E_OUTOFMEMORY 0x8007000E
-#define E_UNEXPECTED 0x8000FFFF
-#define E_NOTIMPL 0x80004001
-#define E_INVALIDARG 0x80070057
#define COR_E_EXECUTIONENGINE 0x80131506
#define NOERROR 0x0
@@ -100,6 +103,8 @@ inline HRESULT HRESULT_FROM_WIN32(unsigned long x)
#define _vsnprintf_s(string, sizeInBytes, count, format, args) vsnprintf(string, sizeInBytes, format, args)
#define sprintf_s snprintf
#define swprintf_s swprintf
+#define _snprintf_s(string, sizeInBytes, count, format, ...) \
+ snprintf(string, sizeInBytes, format, ## __VA_ARGS__)
#endif
#ifdef UNICODE
@@ -161,8 +166,8 @@ typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(void* lpThreadParameter);
#elif defined(_X86_)
#define YieldProcessor() __asm { rep nop }
-
- __forceinline void MemoryBarrier()
+ #define MemoryBarrier() MemoryBarrierImpl()
+ __forceinline void MemoryBarrierImpl()
{
int32_t Barrier;
__asm {
@@ -175,8 +180,138 @@ typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(void* lpThreadParameter);
#endif
#else // _MSC_VER
+// Only clang defines __has_builtin, so we first test for a GCC define
+// before using __has_builtin.
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#if (__GNUC__ > 4 && __GNUC_MINOR > 7) || __has_builtin(__builtin_ia32_pause)
+ // clang added this intrinsic in 3.8
+ // gcc added this intrinsic by 4.7.1
+ #define YieldProcessor __builtin_ia32_pause
+#endif // __has_builtin(__builtin_ia32_pause)
+
+#if defined(__GNUC__) || __has_builtin(__builtin_ia32_mfence)
+ // clang has had this intrinsic since at least 3.0
+ // gcc has had this intrinsic since forever
+ #define MemoryBarrier __builtin_ia32_mfence
+#endif // __has_builtin(__builtin_ia32_mfence)
+
+// If we don't have intrinsics, we can do some inline asm instead.
+#ifndef YieldProcessor
+ #define YieldProcessor() asm volatile ("pause")
+#endif // YieldProcessor
+
+#ifndef MemoryBarrier
+ #define MemoryBarrier() asm volatile ("mfence")
+#endif // MemoryBarrier
+
+#endif // defined(__i386__) || defined(__x86_64__)
+
+#endif // _MSC_VER
+
+#ifdef _MSC_VER
+#pragma intrinsic(_BitScanForward)
+#if WIN64
+ #pragma intrinsic(_BitScanForward64)
+#endif
#endif // _MSC_VER
+// Cross-platform wrapper for the _BitScanForward compiler intrinsic.
+inline uint8_t BitScanForward(uint32_t *bitIndex, uint32_t mask)
+{
+#ifdef _MSC_VER
+ return _BitScanForward((unsigned long*)bitIndex, mask);
+#else // _MSC_VER
+ unsigned char ret = FALSE;
+ int iIndex = __builtin_ffsl(mask);
+ if (iIndex != 0)
+ {
+ *bitIndex = (uint32_t)(iIndex - 1);
+ ret = TRUE;
+ }
+
+ return ret;
+#endif // _MSC_VER
+}
+
+// Cross-platform wrapper for the _BitScanForward64 compiler intrinsic.
+inline uint8_t BitScanForward64(uint32_t *bitIndex, uint64_t mask)
+{
+#ifdef _MSC_VER
+ #if _WIN32
+ // MSVC targeting a 32-bit target does not support this intrinsic.
+ // We can fake it using two successive invocations of _BitScanForward.
+ uint32_t hi = (mask >> 32) & 0xFFFFFFFF;
+ uint32_t lo = mask & 0xFFFFFFFF;
+ uint32_t fakeBitIndex = 0;
+ if (BitScanForward(&fakeBitIndex, hi))
+ {
+ *bitIndex = fakeBitIndex + 32;
+ }
+
+ return BitScanForward(bitIndex, lo);
+ #else
+ return _BitScanForward64((unsigned long*)bitIndex, mask);
+ #endif // _WIN32
+#else
+ unsigned char ret = FALSE;
+ int iIndex = __builtin_ffsll(mask);
+ if (iIndex != 0)
+ {
+ *bitIndex = (uint32_t)(iIndex - 1);
+ ret = TRUE;
+ }
+
+ return ret;
+#endif // _MSC_VER
+}
+
+// Aligns a size_t to the specified alignment. Alignment must be a power
+// of two.
+inline size_t ALIGN_UP(size_t val, size_t alignment)
+{
+ // alignment factor must be power of two
+ assert((alignment & (alignment - 1)) == 0);
+ size_t result = (val + (alignment - 1)) & ~(alignment - 1);
+ assert(result >= val);
+ return result;
+}
+
+// Aligns a pointer to the specified alignment. Alignment must be a power
+// of two.
+inline uint8_t* ALIGN_UP(uint8_t* ptr, size_t alignment)
+{
+ size_t as_size_t = reinterpret_cast<size_t>(ptr);
+ return reinterpret_cast<uint8_t*>(ALIGN_UP(as_size_t, alignment));
+}
+
+// Aligns a size_t to the specified alignment by rounding down. Alignment must
+// be a power of two.
+inline size_t ALIGN_DOWN(size_t val, size_t alignment)
+{
+ // alignment factor must be power of two.
+ assert((alignment & (alignment - 1)) == 0);
+ size_t result = val & ~(alignment - 1);
+ return result;
+}
+
+// Aligns a pointer to the specified alignment by rounding down. Alignment
+// must be a power of two.
+inline uint8_t* ALIGN_DOWN(uint8_t* ptr, size_t alignment)
+{
+ size_t as_size_t = reinterpret_cast<size_t>(ptr);
+ return reinterpret_cast<uint8_t*>(ALIGN_DOWN(as_size_t, alignment));
+}
+
+// Aligns a void pointer to the specified alignment by rounding down. Alignment
+// must be a power of two.
+inline void* ALIGN_DOWN(void* ptr, size_t alignment)
+{
+ size_t as_size_t = reinterpret_cast<size_t>(ptr);
+ return reinterpret_cast<void*>(ALIGN_DOWN(as_size_t, alignment));
+}
+
typedef struct _PROCESSOR_NUMBER {
uint16_t Group;
uint8_t Number;
@@ -241,75 +376,25 @@ typedef struct _PROCESSOR_NUMBER {
//
// Data access macros
//
-#ifdef DACCESS_COMPILE
-#include "daccess.h"
-#else // DACCESS_COMPILE
typedef uintptr_t TADDR;
-
#define PTR_TO_TADDR(ptr) ((TADDR)(ptr))
#define DPTR(type) type*
#define SPTR(type) type*
-
-#define GVAL_DECL(type, var) \
- extern type var
-#define GVAL_IMPL(type, var) \
- type var
-#define GVAL_IMPL_INIT(type, var, init) \
- type var = init
-
-#define GPTR_DECL(type, var) \
- extern type* var
-#define GPTR_IMPL(type, var) \
- type* var
-#define GPTR_IMPL_INIT(type, var, init) \
- type* var = init
-
-#define SPTR_DECL(type, var) \
- static type* var
-#define SPTR_IMPL(type, cls, var) \
- type * cls::var
-#define SPTR_IMPL_NS(type, ns, cls, var) \
- type * cls::var
-#define SPTR_IMPL_NS_INIT(type, ns, cls, var, init) \
- type * cls::var = init
-
-#define SVAL_DECL(type, var) \
- static type var
-#define SVAL_IMPL_NS(type, ns, cls, var) \
- type cls::var
-#define SVAL_IMPL_NS_INIT(type, ns, cls, var, init) \
- type cls::var = init
-
-#define GARY_DECL(type, var, size) \
- extern type var[size]
-#define GARY_IMPL(type, var, size) \
- type var[size]
-
-struct _DacGlobals;
-#endif // DACCESS_COMPILE
-
typedef DPTR(size_t) PTR_size_t;
typedef DPTR(uint8_t) PTR_uint8_t;
// -----------------------------------------------------------------------------------------------------------
#define DATA_ALIGNMENT sizeof(uintptr_t)
-
#define RAW_KEYWORD(x) x
-
#define DECLSPEC_ALIGN(x) __declspec(align(x))
-
#ifndef _ASSERTE
#define _ASSERTE(_expr) ASSERT(_expr)
#endif
-
#define CONSISTENCY_CHECK(_expr) ASSERT(_expr)
-
#define PREFIX_ASSUME(cond) ASSERT(cond)
-
#define EEPOLICY_HANDLE_FATAL_ERROR(error) ASSERT(!"EEPOLICY_HANDLE_FATAL_ERROR")
-
#define UI64(_literal) _literal##ULL
class ObjHeader;
@@ -331,145 +416,16 @@ typedef PTR_PTR_Object PTR_UNCHECKED_OBJECTREF;
#define ObjectToOBJECTREF(_obj) (OBJECTREF)(_obj)
#define OBJECTREFToObject(_obj) (Object*)(_obj)
-#define VALIDATEOBJECTREF(_objref) _objref;
-
-#define VOLATILE(T) T volatile
-
-//
-// This code is extremely compiler- and CPU-specific, and will need to be altered to
-// support new compilers and/or CPUs. Here we enforce that we can only compile using
-// VC++, or Clang on x86, AMD64, ARM and ARM64.
-//
-#if !defined(_MSC_VER) && !defined(__clang__)
-#error The Volatile type is currently only defined for Visual C++ and Clang
-#endif
-
-#if defined(__clang__) && !defined(_X86_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_)
-#error The Volatile type is currently only defined for Clang when targeting x86, AMD64, ARM or ARM64 CPUs
-#endif
-
-#if defined(__clang__)
-#if defined(_ARM_) || defined(_ARM64_)
-// This is functionally equivalent to the MemoryBarrier() macro used on ARM on Windows.
-#define VOLATILE_MEMORY_BARRIER() asm volatile ("dmb ish" : : : "memory")
-#else
-//
-// For Clang, we prevent reordering by the compiler by inserting the following after a volatile
-// load (to prevent subsequent operations from moving before the read), and before a volatile
-// write (to prevent prior operations from moving past the write). We don't need to do anything
-// special to prevent CPU reorderings, because the x86 and AMD64 architectures are already
-// sufficiently constrained for our purposes. If we ever need to run on weaker CPU architectures
-// (such as PowerPC), then we will need to do more work.
-//
-// Please do not use this macro outside of this file. It is subject to change or removal without
-// notice.
-//
-#define VOLATILE_MEMORY_BARRIER() asm volatile ("" : : : "memory")
-#endif // !_ARM_
-#elif defined(_ARM_) && _ISO_VOLATILE
-// ARM has a very weak memory model and very few tools to control that model. We're forced to perform a full
-// memory barrier to preserve the volatile semantics. Technically this is only necessary on MP systems but we
-// currently don't have a cheap way to determine the number of CPUs from this header file. Revisit this if it
-// turns out to be a performance issue for the uni-proc case.
-#define VOLATILE_MEMORY_BARRIER() MemoryBarrier()
-#else
-//
-// On VC++, reorderings at the compiler and machine level are prevented by the use of the
-// "volatile" keyword in VolatileLoad and VolatileStore. This should work on any CPU architecture
-// targeted by VC++ with /iso_volatile-.
-//
-#define VOLATILE_MEMORY_BARRIER()
-#endif
+#define VALIDATEOBJECTREF(_objref) (void)_objref;
-//
-// VolatileLoad loads a T from a pointer to T. It is guaranteed that this load will not be optimized
-// away by the compiler, and that any operation that occurs after this load, in program order, will
-// not be moved before this load. In general it is not guaranteed that the load will be atomic, though
-// this is the case for most aligned scalar data types. If you need atomic loads or stores, you need
-// to consult the compiler and CPU manuals to find which circumstances allow atomicity.
-//
-template<typename T>
-inline
-T VolatileLoad(T const * pt)
-{
-#if defined(_ARM64_) && defined(__clang__)
- T val;
- static const unsigned lockFreeAtomicSizeMask = (1 << 1) | (1 << 2) | (1 << 4) | (1 << 8);
- if((1 << sizeof(T)) & lockFreeAtomicSizeMask)
- {
- __atomic_load((T volatile const *)pt, &val, __ATOMIC_ACQUIRE);
- }
- else
- {
- val = *(T volatile const *)pt;
- asm volatile ("dmb ishld" : : : "memory");
- }
-#else
- T val = *(T volatile const *)pt;
- VOLATILE_MEMORY_BARRIER();
-#endif
- return val;
-}
-
-template<typename T>
-inline
-T VolatileLoadWithoutBarrier(T const * pt)
-{
-#ifndef DACCESS_COMPILE
- T val = *(T volatile const *)pt;
-#else
- T val = *pt;
-#endif
- return val;
-}
+class Thread;
-//
-// VolatileStore stores a T into the target of a pointer to T. Is is guaranteed that this store will
-// not be optimized away by the compiler, and that any operation that occurs before this store, in program
-// order, will not be moved after this store. In general, it is not guaranteed that the store will be
-// atomic, though this is the case for most aligned scalar data types. If you need atomic loads or stores,
-// you need to consult the compiler and CPU manuals to find which circumstances allow atomicity.
-//
-template<typename T>
-inline
-void VolatileStore(T* pt, T val)
+inline bool IsGCSpecialThread()
{
-#if defined(_ARM64_) && defined(__clang__)
- static const unsigned lockFreeAtomicSizeMask = (1 << 1) | (1 << 2) | (1 << 4) | (1 << 8);
- if((1 << sizeof(T)) & lockFreeAtomicSizeMask)
- {
- __atomic_store((T volatile *)pt, &val, __ATOMIC_RELEASE);
- }
- else
- {
- VOLATILE_MEMORY_BARRIER();
- *(T volatile *)pt = val;
- }
-#else
- VOLATILE_MEMORY_BARRIER();
- *(T volatile *)pt = val;
-#endif
+ // [LOCALGC TODO] this is not correct
+ return false;
}
-extern GCSystemInfo g_SystemInfo;
-
-extern MethodTable * g_pFreeObjectMethodTable;
-
-extern int32_t g_TrapReturningThreads;
-
-//
-// Locks
-//
-
-struct gc_alloc_context;
-class Thread;
-
-Thread * GetThread();
-
-typedef void (CALLBACK *HANDLESCANPROC)(PTR_UNCHECKED_OBJECTREF pref, uintptr_t *pExtraInfo, uintptr_t param1, uintptr_t param2);
-
-bool IsGCSpecialThread();
-
inline bool dbgOnly_IsSpecialEEThread()
{
return false;
@@ -498,42 +454,11 @@ namespace ETW
} GC_ROOT_KIND;
};
-//
-// Logging
-//
-
-void LogSpewAlways(const char *fmt, ...);
-
-#define DEFAULT_GC_PRN_LVL 3
-
-// -----------------------------------------------------------------------------------------------------------
-
-bool IsGCThread();
-
-class CLRConfig
+inline bool IsGCThread()
{
-public:
- enum CLRConfigTypes
- {
- UNSUPPORTED_GCLogEnabled,
- UNSUPPORTED_GCLogFile,
- UNSUPPORTED_GCLogFileSize,
- UNSUPPORTED_GCConfigLogEnabled,
- UNSUPPORTED_GCConfigLogFile,
- UNSUPPORTED_BGCSpinCount,
- UNSUPPORTED_BGCSpin,
- EXTERNAL_GCStressStart,
- INTERNAL_GCStressStartAtJit,
- INTERNAL_DbgDACSkipVerifyDlls,
- Config_COUNT
- };
-
- typedef CLRConfigTypes ConfigDWORDInfo;
- typedef CLRConfigTypes ConfigStringInfo;
-
- static uint32_t GetConfigValue(ConfigDWORDInfo eType);
- static HRESULT GetConfigValue(ConfigStringInfo /*eType*/, __out_z TCHAR * * outVal);
-};
+ // [LOCALGC TODO] this is not correct
+ return false;
+}
inline bool FitsInU1(uint64_t val)
{
@@ -577,46 +502,50 @@ public:
DWORD GetTotalNumSizedRefHandles() { return 0; }
};
-#ifdef STRESS_HEAP
-namespace GCStressPolicy
+class NumaNodeInfo
{
- static volatile int32_t s_cGcStressDisables;
+public:
+ static bool CanEnableGCNumaAware()
+ {
+ // [LOCALGC TODO] enable NUMA node support
+ return false;
+ }
- inline bool IsEnabled() { return s_cGcStressDisables == 0; }
- inline void GlobalDisable() { Interlocked::Increment(&s_cGcStressDisables); }
- inline void GlobalEnable() { Interlocked::Decrement(&s_cGcStressDisables); }
-}
+ static void GetGroupForProcessor(uint16_t processor_number, uint16_t * group_number, uint16_t * group_processor_number)
+ {
+ // [LOCALGC TODO] enable NUMA node support
+ assert(!"should not be called");
+ }
-enum gcs_trigger_points
-{
- cfg_any,
+ static bool GetNumaProcessorNodeEx(PPROCESSOR_NUMBER proc_no, uint16_t * node_no)
+ {
+ // [LOCALGC TODO] enable NUMA node support
+ assert(!"should not be called");
+ return false;
+ }
};
-template <enum gcs_trigger_points tp>
-class GCStress
+class CPUGroupInfo
{
public:
- static inline bool IsEnabled()
+ static bool CanEnableGCCPUGroups()
{
- return g_pConfig->GetGCStressLevel() != 0;
+ // [LOCALGC TODO] enable CPU group support
+ return false;
}
-};
-#endif // STRESS_HEAP
-class NumaNodeInfo
-{
-public:
- static bool CanEnableGCNumaAware();
- static void GetGroupForProcessor(uint16_t processor_number, uint16_t * group_number, uint16_t * group_processor_number);
- static bool GetNumaProcessorNodeEx(PPROCESSOR_NUMBER proc_no, uint16_t * node_no);
-};
+ static uint32_t GetNumActiveProcessors()
+ {
+ // [LOCALGC TODO] enable CPU group support
+ assert(!"should not be called");
+ return 0;
+ }
-class CPUGroupInfo
-{
-public:
- static bool CanEnableGCCPUGroups();
- static uint32_t GetNumActiveProcessors();
- static void GetGroupForProcessor(uint16_t processor_number, uint16_t * group_number, uint16_t * group_processor_number);
+ static void GetGroupForProcessor(uint16_t processor_number, uint16_t * group_number, uint16_t * group_processor_number)
+ {
+ // [LOCALGC TODO] enable CPU group support
+ assert(!"should not be called");
+ }
};
diff --git a/src/gc/env/gcenv.h b/src/gc/env/gcenv.h
new file mode 100644
index 0000000000..3de756021d
--- /dev/null
+++ b/src/gc/env/gcenv.h
@@ -0,0 +1,73 @@
+// 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 defined(_DEBUG)
+#ifndef _DEBUG_IMPL
+#define _DEBUG_IMPL 1
+#endif
+#define ASSERT(_expr) assert(_expr)
+#else
+#define ASSERT(_expr)
+#endif
+
+#ifndef _ASSERTE
+#define _ASSERTE(_expr) ASSERT(_expr)
+#endif
+
+#include "gcenv.structs.h"
+#include "gcenv.base.h"
+#include "gcenv.os.h"
+#include "gcenv.interlocked.h"
+#include "gcenv.interlocked.inl"
+#include "gcenv.object.h"
+#include "gcenv.sync.h"
+#include "gcinterface.h"
+#include "gcenv.ee.h"
+#include "volatile.h"
+
+#define MAX_LONGPATH 1024
+
+#ifdef _MSC_VER
+#define SUPPRESS_WARNING_4127 \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) /* conditional expression is constant*/
+#define POP_WARNING_STATE \
+ __pragma(warning(pop))
+#else // _MSC_VER
+#define SUPPRESS_WARNING_4127
+#define POP_WARNING_STATE
+#endif // _MSC_VER
+
+#define WHILE_0 \
+ SUPPRESS_WARNING_4127 \
+ while(0) \
+ POP_WARNING_STATE \
+
+#define LL_INFO10 4
+
+#define STRESS_LOG_VA(msg) do { } WHILE_0
+#define STRESS_LOG0(facility, level, msg) do { } WHILE_0
+#define STRESS_LOG1(facility, level, msg, data1) do { } WHILE_0
+#define STRESS_LOG2(facility, level, msg, data1, data2) do { } WHILE_0
+#define STRESS_LOG3(facility, level, msg, data1, data2, data3) do { } WHILE_0
+#define STRESS_LOG4(facility, level, msg, data1, data2, data3, data4) do { } WHILE_0
+#define STRESS_LOG5(facility, level, msg, data1, data2, data3, data4, data5) do { } WHILE_0
+#define STRESS_LOG6(facility, level, msg, data1, data2, data3, data4, data5, data6) do { } WHILE_0
+#define STRESS_LOG7(facility, level, msg, data1, data2, data3, data4, data5, data6, data7) do { } WHILE_0
+#define STRESS_LOG_PLUG_MOVE(plug_start, plug_end, plug_delta) do { } WHILE_0
+#define STRESS_LOG_ROOT_PROMOTE(root_addr, objPtr, methodTable) do { } WHILE_0
+#define STRESS_LOG_ROOT_RELOCATE(root_addr, old_value, new_value, methodTable) do { } WHILE_0
+#define STRESS_LOG_GC_START(gcCount, Gen, collectClasses) do { } WHILE_0
+#define STRESS_LOG_GC_END(gcCount, Gen, collectClasses) do { } WHILE_0
+#define STRESS_LOG_OOM_STACK(size) do { } while(0)
+#define STRESS_LOG_RESERVE_MEM(numChunks) do {} while (0)
+#define STRESS_LOG_GC_STACK
+
+#define LOG(x)
+
+#define SVAL_IMPL_INIT(type, cls, var, init) \
+ type cls::var = init
+
+#include "etmdummy.h"
+#define ETW_EVENT_ENABLED(e,f) false
diff --git a/src/gc/env/gcenv.object.h b/src/gc/env/gcenv.object.h
index db8995a118..4261c1a148 100644
--- a/src/gc/env/gcenv.object.h
+++ b/src/gc/env/gcenv.object.h
@@ -33,9 +33,11 @@ public:
static_assert(sizeof(ObjHeader) == sizeof(uintptr_t), "this assumption is made by the VM!");
-#define MTFlag_ContainsPointers 1
-#define MTFlag_HasFinalizer 2
-#define MTFlag_IsArray 4
+#define MTFlag_ContainsPointers 0x0100
+#define MTFlag_HasFinalizer 0x0010
+#define MTFlag_IsArray 0x0008
+#define MTFlag_Collectible 0x1000
+#define MTFlag_HasComponentSize 0x8000
class MethodTable
{
@@ -64,6 +66,11 @@ public:
return m_componentSize;
}
+ bool Collectible()
+ {
+ return (m_flags & MTFlag_Collectible) != 0;
+ }
+
bool ContainsPointers()
{
return (m_flags & MTFlag_ContainsPointers) != 0;
@@ -71,12 +78,19 @@ public:
bool ContainsPointersOrCollectible()
{
- return ContainsPointers();
+ return ContainsPointers() || Collectible();
}
bool HasComponentSize()
{
- return m_componentSize != 0;
+ // Note that we can't just check m_componentSize != 0 here. The VM
+ // may still construct a method table that does not have a component
+ // size, according to this method, but still has a number in the low
+ // 16 bits of the method table flags parameter.
+ //
+ // The solution here is to do what the VM does and check the
+ // HasComponentSize flag so that we're on the same page.
+ return (m_flags & MTFlag_HasComponentSize) != 0;
}
bool HasFinalizer()
@@ -104,6 +118,12 @@ public:
{
return true;
}
+
+ uint8_t* GetLoaderAllocatorObjectForGC()
+ {
+ // [LOCALGC TODO] this is not correct
+ return nullptr;
+ }
};
class Object
diff --git a/src/gc/env/volatile.h b/src/gc/env/volatile.h
new file mode 100644
index 0000000000..782b6eff41
--- /dev/null
+++ b/src/gc/env/volatile.h
@@ -0,0 +1,479 @@
+// 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.
+//
+// Volatile.h
+//
+
+//
+// Defines the Volatile<T> type, which provides uniform volatile-ness on
+// Visual C++ and GNU C++.
+//
+// Visual C++ treats accesses to volatile variables as follows: no read or write
+// can be removed by the compiler, no global memory access can be moved backwards past
+// a volatile read, and no global memory access can be moved forward past a volatile
+// write.
+//
+// The GCC volatile semantic is straight out of the C standard: the compiler is not
+// allowed to remove accesses to volatile variables, and it is not allowed to reorder
+// volatile accesses relative to other volatile accesses. It is allowed to freely
+// reorder non-volatile accesses relative to volatile accesses.
+//
+// We have lots of code that assumes that ordering of non-volatile accesses will be
+// constrained relative to volatile accesses. For example, this pattern appears all
+// over the place:
+//
+// static volatile int lock = 0;
+//
+// while (InterlockedCompareExchange(&lock, 0, 1))
+// {
+// //spin
+// }
+//
+// //read and write variables protected by the lock
+//
+// lock = 0;
+//
+// This depends on the reads and writes in the critical section not moving past the
+// final statement, which releases the lock. If this should happen, then you have an
+// unintended race.
+//
+// The solution is to ban the use of the "volatile" keyword, and instead define our
+// own type Volatile<T>, which acts like a variable of type T except that accesses to
+// the variable are always given VC++'s volatile semantics.
+//
+// (NOTE: The code above is not intended to be an example of how a spinlock should be
+// implemented; it has many flaws, and should not be used. This code is intended only
+// to illustrate where we might get into trouble with GCC's volatile semantics.)
+//
+// @TODO: many of the variables marked volatile in the CLR do not actually need to be
+// volatile. For example, if a variable is just always passed to Interlocked functions
+// (such as a refcount variable), there is no need for it to be volatile. A future
+// cleanup task should be to examine each volatile variable and make them non-volatile
+// if possible.
+//
+// @TODO: link to a "Memory Models for CLR Devs" doc here (this doc does not yet exist).
+//
+
+#ifndef _VOLATILE_H_
+#define _VOLATILE_H_
+
+//
+// This code is extremely compiler- and CPU-specific, and will need to be altered to
+// support new compilers and/or CPUs. Here we enforce that we can only compile using
+// VC++, or GCC on x86 or AMD64.
+//
+#if !defined(_MSC_VER) && !defined(__GNUC__)
+#error The Volatile type is currently only defined for Visual C++ and GNU C++
+#endif
+
+#if defined(__GNUC__) && !defined(_X86_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_)
+#error The Volatile type is currently only defined for GCC when targeting x86, AMD64, ARM or ARM64 CPUs
+#endif
+
+#if defined(__GNUC__)
+#if defined(_ARM_) || defined(_ARM64_)
+// This is functionally equivalent to the MemoryBarrier() macro used on ARM on Windows.
+#define VOLATILE_MEMORY_BARRIER() asm volatile ("dmb ish" : : : "memory")
+#else
+//
+// For GCC, we prevent reordering by the compiler by inserting the following after a volatile
+// load (to prevent subsequent operations from moving before the read), and before a volatile
+// write (to prevent prior operations from moving past the write). We don't need to do anything
+// special to prevent CPU reorderings, because the x86 and AMD64 architectures are already
+// sufficiently constrained for our purposes. If we ever need to run on weaker CPU architectures
+// (such as PowerPC), then we will need to do more work.
+//
+// Please do not use this macro outside of this file. It is subject to change or removal without
+// notice.
+//
+#define VOLATILE_MEMORY_BARRIER() asm volatile ("" : : : "memory")
+#endif // _ARM_ || _ARM64_
+#elif (defined(_ARM_) || defined(_ARM64_)) && _ISO_VOLATILE
+// ARM & ARM64 have a very weak memory model and very few tools to control that model. We're forced to perform a full
+// memory barrier to preserve the volatile semantics. Technically this is only necessary on MP systems but we
+// currently don't have a cheap way to determine the number of CPUs from this header file. Revisit this if it
+// turns out to be a performance issue for the uni-proc case.
+#define VOLATILE_MEMORY_BARRIER() MemoryBarrier()
+#else
+//
+// On VC++, reorderings at the compiler and machine level are prevented by the use of the
+// "volatile" keyword in VolatileLoad and VolatileStore. This should work on any CPU architecture
+// targeted by VC++ with /iso_volatile-.
+//
+#define VOLATILE_MEMORY_BARRIER()
+#endif // __GNUC__
+
+template<typename T>
+struct RemoveVolatile
+{
+ typedef T type;
+};
+
+template<typename T>
+struct RemoveVolatile<volatile T>
+{
+ typedef T type;
+};
+
+
+//
+// VolatileLoad loads a T from a pointer to T. It is guaranteed that this load will not be optimized
+// away by the compiler, and that any operation that occurs after this load, in program order, will
+// not be moved before this load. In general it is not guaranteed that the load will be atomic, though
+// this is the case for most aligned scalar data types. If you need atomic loads or stores, you need
+// to consult the compiler and CPU manuals to find which circumstances allow atomicity.
+//
+// Starting at version 3.8, clang errors out on initializing of type int * to volatile int *. To fix this, we add two templates to cast away volatility
+// Helper structures for casting away volatileness
+
+
+template<typename T>
+inline
+T VolatileLoad(T const * pt)
+{
+#ifndef DACCESS_COMPILE
+#if defined(_ARM64_) && defined(__GNUC__)
+ T val;
+ static const unsigned lockFreeAtomicSizeMask = (1 << 1) | (1 << 2) | (1 << 4) | (1 << 8);
+ if((1 << sizeof(T)) & lockFreeAtomicSizeMask)
+ {
+ __atomic_load((T const *)pt, const_cast<typename RemoveVolatile<T>::type *>(&val), __ATOMIC_ACQUIRE);
+ }
+ else
+ {
+ val = *(T volatile const *)pt;
+ asm volatile ("dmb ishld" : : : "memory");
+ }
+#else
+ T val = *(T volatile const *)pt;
+ VOLATILE_MEMORY_BARRIER();
+#endif
+#else
+ T val = *pt;
+#endif
+ return val;
+}
+
+template<typename T>
+inline
+T VolatileLoadWithoutBarrier(T const * pt)
+{
+#ifndef DACCESS_COMPILE
+ T val = *(T volatile const *)pt;
+#else
+ T val = *pt;
+#endif
+ return val;
+}
+
+template <typename T> class Volatile;
+
+template<typename T>
+inline
+T VolatileLoad(Volatile<T> const * pt)
+{
+ return pt->Load();
+}
+
+//
+// VolatileStore stores a T into the target of a pointer to T. Is is guaranteed that this store will
+// not be optimized away by the compiler, and that any operation that occurs before this store, in program
+// order, will not be moved after this store. In general, it is not guaranteed that the store will be
+// atomic, though this is the case for most aligned scalar data types. If you need atomic loads or stores,
+// you need to consult the compiler and CPU manuals to find which circumstances allow atomicity.
+//
+template<typename T>
+inline
+void VolatileStore(T* pt, T val)
+{
+#ifndef DACCESS_COMPILE
+#if defined(_ARM64_) && defined(__GNUC__)
+ static const unsigned lockFreeAtomicSizeMask = (1 << 1) | (1 << 2) | (1 << 4) | (1 << 8);
+ if((1 << sizeof(T)) & lockFreeAtomicSizeMask)
+ {
+ __atomic_store((T volatile *)pt, &val, __ATOMIC_RELEASE);
+ }
+ else
+ {
+ VOLATILE_MEMORY_BARRIER();
+ *(T volatile *)pt = val;
+ }
+#else
+ VOLATILE_MEMORY_BARRIER();
+ *(T volatile *)pt = val;
+#endif
+#else
+ *pt = val;
+#endif
+}
+
+template<typename T>
+inline
+void VolatileStoreWithoutBarrier(T* pt, T val)
+{
+#ifndef DACCESS_COMPILE
+ *(T volatile *)pt = val;
+#else
+ *pt = val;
+#endif
+}
+
+//
+// Volatile<T> implements accesses with our volatile semantics over a variable of type T.
+// Wherever you would have used a "volatile Foo" or, equivalently, "Foo volatile", use Volatile<Foo>
+// instead. If Foo is a pointer type, use VolatilePtr.
+//
+// Note that there are still some things that don't work with a Volatile<T>,
+// that would have worked with a "volatile T". For example, you can't cast a Volatile<int> to a float.
+// You must instead cast to an int, then to a float. Or you can call Load on the Volatile<int>, and
+// cast the result to a float. In general, calling Load or Store explicitly will work around
+// any problems that can't be solved by operator overloading.
+//
+// @TODO: it's not clear that we actually *want* any operator overloading here. It's in here primarily
+// to ease the task of converting all of the old uses of the volatile keyword, but in the long
+// run it's probably better if users of this class are forced to call Load() and Store() explicitly.
+// This would make it much more clear where the memory barriers are, and which operations are actually
+// being performed, but it will have to wait for another cleanup effort.
+//
+template <typename T>
+class Volatile
+{
+private:
+ //
+ // The data which we are treating as volatile
+ //
+ T m_val;
+
+public:
+ //
+ // Default constructor. Results in an unitialized value!
+ //
+ inline Volatile()
+ {
+ }
+
+ //
+ // Allow initialization of Volatile<T> from a T
+ //
+ inline Volatile(const T& val)
+ {
+ ((volatile T &)m_val) = val;
+ }
+
+ //
+ // Copy constructor
+ //
+ inline Volatile(const Volatile<T>& other)
+ {
+ ((volatile T &)m_val) = other.Load();
+ }
+
+ //
+ // Loads the value of the volatile variable. See code:VolatileLoad for the semantics of this operation.
+ //
+ inline T Load() const
+ {
+ return VolatileLoad(&m_val);
+ }
+
+ //
+ // Loads the value of the volatile variable atomically without erecting the memory barrier.
+ //
+ inline T LoadWithoutBarrier() const
+ {
+ return ((volatile T &)m_val);
+ }
+
+ //
+ // Stores a new value to the volatile variable. See code:VolatileStore for the semantics of this
+ // operation.
+ //
+ inline void Store(const T& val)
+ {
+ VolatileStore(&m_val, val);
+ }
+
+
+ //
+ // Stores a new value to the volatile variable atomically without erecting the memory barrier.
+ //
+ inline void StoreWithoutBarrier(const T& val) const
+ {
+ ((volatile T &)m_val) = val;
+ }
+
+
+ //
+ // Gets a pointer to the volatile variable. This is dangerous, as it permits the variable to be
+ // accessed without using Load and Store, but it is necessary for passing Volatile<T> to APIs like
+ // InterlockedIncrement.
+ //
+ inline volatile T* GetPointer() { return (volatile T*)&m_val; }
+
+
+ //
+ // Gets the raw value of the variable. This is dangerous, as it permits the variable to be
+ // accessed without using Load and Store
+ //
+ inline T& RawValue() { return m_val; }
+
+ //
+ // Allow casts from Volatile<T> to T. Note that this allows implicit casts, so you can
+ // pass a Volatile<T> directly to a method that expects a T.
+ //
+ inline operator T() const
+ {
+ return this->Load();
+ }
+
+ //
+ // Assignment from T
+ //
+ inline Volatile<T>& operator=(T val) {Store(val); return *this;}
+
+ //
+ // Get the address of the volatile variable. This is dangerous, as it allows the value of the
+ // volatile variable to be accessed directly, without going through Load and Store, but it is
+ // necessary for passing Volatile<T> to APIs like InterlockedIncrement. Note that we are returning
+ // a pointer to a volatile T here, so we cannot accidentally pass this pointer to an API that
+ // expects a normal pointer.
+ //
+ inline T volatile * operator&() {return this->GetPointer();}
+ inline T volatile const * operator&() const {return this->GetPointer();}
+
+ //
+ // Comparison operators
+ //
+ template<typename TOther>
+ inline bool operator==(const TOther& other) const {return this->Load() == other;}
+
+ template<typename TOther>
+ inline bool operator!=(const TOther& other) const {return this->Load() != other;}
+
+ //
+ // Miscellaneous operators. Add more as necessary.
+ //
+ inline Volatile<T>& operator+=(T val) {Store(this->Load() + val); return *this;}
+ inline Volatile<T>& operator-=(T val) {Store(this->Load() - val); return *this;}
+ inline Volatile<T>& operator|=(T val) {Store(this->Load() | val); return *this;}
+ inline Volatile<T>& operator&=(T val) {Store(this->Load() & val); return *this;}
+ inline bool operator!() const { return !this->Load();}
+
+ //
+ // Prefix increment
+ //
+ inline Volatile& operator++() {this->Store(this->Load()+1); return *this;}
+
+ //
+ // Postfix increment
+ //
+ inline T operator++(int) {T val = this->Load(); this->Store(val+1); return val;}
+
+ //
+ // Prefix decrement
+ //
+ inline Volatile& operator--() {this->Store(this->Load()-1); return *this;}
+
+ //
+ // Postfix decrement
+ //
+ inline T operator--(int) {T val = this->Load(); this->Store(val-1); return val;}
+};
+
+//
+// A VolatilePtr builds on Volatile<T> by adding operators appropriate to pointers.
+// Wherever you would have used "Foo * volatile", use "VolatilePtr<Foo>" instead.
+//
+// VolatilePtr also allows the substution of other types for the underlying pointer. This
+// allows you to wrap a VolatilePtr around a custom type that looks like a pointer. For example,
+// if what you want is a "volatile DPTR<Foo>", use "VolatilePtr<Foo, DPTR<Foo>>".
+//
+template <typename T, typename P = T*>
+class VolatilePtr : public Volatile<P>
+{
+public:
+ //
+ // Default constructor. Results in an uninitialized pointer!
+ //
+ inline VolatilePtr()
+ {
+ }
+
+ //
+ // Allow assignment from the pointer type.
+ //
+ inline VolatilePtr(P val) : Volatile<P>(val)
+ {
+ }
+
+ //
+ // Copy constructor
+ //
+ inline VolatilePtr(const VolatilePtr& other) : Volatile<P>(other)
+ {
+ }
+
+ //
+ // Cast to the pointer type
+ //
+ inline operator P() const
+ {
+ return (P)this->Load();
+ }
+
+ //
+ // Member access
+ //
+ inline P operator->() const
+ {
+ return (P)this->Load();
+ }
+
+ //
+ // Dereference the pointer
+ //
+ inline T& operator*() const
+ {
+ return *(P)this->Load();
+ }
+
+ //
+ // Access the pointer as an array
+ //
+ template <typename TIndex>
+ inline T& operator[](TIndex index)
+ {
+ return ((P)this->Load())[index];
+ }
+};
+
+
+//
+// Warning: workaround
+//
+// At the bottom of this file, we are going to #define the "volatile" keyword such that it is illegal
+// to use it. Unfortunately, VC++ uses the volatile keyword in stddef.h, in the definition of "offsetof".
+// GCC does not use volatile in its definition.
+//
+// To get around this, we include stddef.h here (even if we're on GCC, for consistency). We then need
+// to redefine offsetof such that it does not use volatile, if we're building with VC++.
+//
+#include <stddef.h>
+#ifdef _MSC_VER
+#undef offsetof
+#ifdef _WIN64
+#define offsetof(s,m) (size_t)( (ptrdiff_t)&reinterpret_cast<const char&>((((s *)0)->m)) )
+#else
+#define offsetof(s,m) (size_t)&reinterpret_cast<const char&>((((s *)0)->m))
+#endif //_WIN64
+
+// These also use volatile, so we'll include them here.
+//#include <intrin.h>
+//#include <memory>
+
+#endif //_MSC_VER
+
+#define VOLATILE(T) Volatile<T>
+
+#endif //_VOLATILE_H_
diff --git a/src/gc/gc.cpp b/src/gc/gc.cpp
index 9bc1ada81a..89454c6bb0 100644
--- a/src/gc/gc.cpp
+++ b/src/gc/gc.cpp
@@ -308,6 +308,92 @@ void GCStatistics::DisplayAndUpdate()
#endif // GC_STATS
+#ifdef BIT64
+#define TOTAL_TIMES_TO_SHIFT 6
+#else
+#define TOTAL_TIMES_TO_SHIFT 5
+#endif // BIT64
+
+size_t round_up_power2 (size_t size)
+{
+ unsigned short shift = 1;
+ size_t shifted = 0;
+
+ size--;
+ for (unsigned short i = 0; i < TOTAL_TIMES_TO_SHIFT; i++)
+ {
+ shifted = size | (size >> shift);
+ if (shifted == size)
+ {
+ break;
+ }
+
+ size = shifted;
+ shift <<= 1;
+ }
+ shifted++;
+
+ return shifted;
+}
+
+inline
+size_t round_down_power2 (size_t size)
+{
+ size_t power2 = round_up_power2 (size);
+
+ if (power2 != size)
+ {
+ power2 >>= 1;
+ }
+
+ return power2;
+}
+
+// the index starts from 0.
+int index_of_set_bit (size_t power2)
+{
+ int low = 0;
+ int high = sizeof (size_t) * 8 - 1;
+ int mid;
+ while (low <= high)
+ {
+ mid = ((low + high)/2);
+ size_t temp = (size_t)1 << mid;
+ if (power2 & temp)
+ {
+ return mid;
+ }
+ else if (power2 < temp)
+ {
+ high = mid - 1;
+ }
+ else
+ {
+ low = mid + 1;
+ }
+ }
+
+ return -1;
+}
+
+inline
+int relative_index_power2_plug (size_t power2)
+{
+ int index = index_of_set_bit (power2);
+ assert (index <= MAX_INDEX_POWER2);
+
+ return ((index < MIN_INDEX_POWER2) ? 0 : (index - MIN_INDEX_POWER2));
+}
+
+inline
+int relative_index_power2_free_space (size_t power2)
+{
+ int index = index_of_set_bit (power2);
+ assert (index <= MAX_INDEX_POWER2);
+
+ return ((index < MIN_INDEX_POWER2) ? -1 : (index - MIN_INDEX_POWER2));
+}
+
#ifdef BACKGROUND_GC
uint32_t bgc_alloc_spin_count = 140;
uint32_t bgc_alloc_spin_count_loh = 16;
@@ -510,6 +596,7 @@ static const unsigned int log_interval = 5000;
// Time (in ms) when we start a new log interval.
static unsigned int log_start_tick;
static unsigned int gc_lock_contended;
+static int64_t log_start_hires;
// Cycles accumulated in SuspendEE during log_interval.
static uint64_t suspend_ee_during_log;
// Cycles accumulated in RestartEE during log_interval.
@@ -531,6 +618,7 @@ init_sync_log_stats()
gc_lock_contended = 0;
log_start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
+ log_start_hires = GCToOSInterface::QueryPerformanceCounter();
}
gc_count_during_log++;
#endif //SYNCHRONIZATION_STATS
@@ -545,16 +633,18 @@ process_sync_log_stats()
if (log_elapsed > log_interval)
{
+ uint64_t total = GCToOSInterface::QueryPerformanceCounter() - log_start_hires;
// Print out the cycles we spent on average in each suspend and restart.
printf("\n_________________________________________________________________________________\n"
"Past %d(s): #%3d GCs; Total gc_lock contended: %8u; GC: %12u\n"
- "SuspendEE: %8u; RestartEE: %8u\n",
+ "SuspendEE: %8u; RestartEE: %8u GC %.3f%%\n",
log_interval / 1000,
gc_count_during_log,
gc_lock_contended,
(unsigned int)(gc_during_log / gc_count_during_log),
(unsigned int)(suspend_ee_during_log / gc_count_during_log),
- (unsigned int)(restart_ee_during_log / gc_count_during_log));
+ (unsigned int)(restart_ee_during_log / gc_count_during_log),
+ (double)(100.0f * gc_during_log / total));
gc_heap::print_sync_stats(gc_count_during_log);
gc_count_during_log = 0;
@@ -615,20 +705,23 @@ enum gc_join_flavor
};
#define first_thread_arrived 2
-struct join_structure
+struct DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE) join_structure
{
+ // Shared non volatile keep on separate line to prevent eviction
+ int n_threads;
+
+ // Keep polling/wait structures on separate line write once per join
+ DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE)
GCEvent joined_event[3]; // the last event in the array is only used for first_thread_arrived.
+ Volatile<int> lock_color;
+ VOLATILE(BOOL) wait_done;
+ VOLATILE(BOOL) joined_p;
+
+ // Keep volatile counted locks on separate cache line write many per join
+ DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE)
VOLATILE(int32_t) join_lock;
VOLATILE(int32_t) r_join_lock;
- VOLATILE(int32_t) join_restart;
- VOLATILE(int32_t) r_join_restart; // only used by get_here_first and friends.
- int n_threads;
- VOLATILE(BOOL) joined_p;
- // avoid lock_color and join_lock being on same cache line
- // make sure to modify this if adding/removing variables to layout
- char cache_line_separator[HS_CACHE_LINE_SIZE - (3*sizeof(int) + sizeof(int) + sizeof(BOOL))];
- VOLATILE(int) lock_color;
- VOLATILE(BOOL) wait_done;
+
};
enum join_type
@@ -667,13 +760,13 @@ class t_join
gc_join_flavor flavor;
#ifdef JOIN_STATS
- unsigned int start[MAX_SUPPORTED_CPUS], end[MAX_SUPPORTED_CPUS], start_seq;
+ uint64_t start[MAX_SUPPORTED_CPUS], end[MAX_SUPPORTED_CPUS], start_seq;
// remember join id and last thread to arrive so restart can use these
int thd;
// we want to print statistics every 10 seconds - this is to remember the start of the 10 sec interval
uint32_t start_tick;
// counters for joins, in 1000's of clock cycles
- unsigned int elapsed_total[gc_join_max], seq_loss_total[gc_join_max], par_loss_total[gc_join_max], in_join_total[gc_join_max];
+ uint64_t elapsed_total[gc_join_max], wake_total[gc_join_max], seq_loss_total[gc_join_max], par_loss_total[gc_join_max], in_join_total[gc_join_max];
#endif //JOIN_STATS
public:
@@ -700,9 +793,7 @@ public:
}
}
join_struct.join_lock = join_struct.n_threads;
- join_struct.join_restart = join_struct.n_threads - 1;
join_struct.r_join_lock = join_struct.n_threads;
- join_struct.r_join_restart = join_struct.n_threads - 1;
join_struct.wait_done = FALSE;
flavor = f;
@@ -732,11 +823,11 @@ public:
{
#ifdef JOIN_STATS
// parallel execution ends here
- end[gch->heap_number] = GetCycleCount32();
+ end[gch->heap_number] = get_ts();
#endif //JOIN_STATS
assert (!join_struct.joined_p);
- int color = join_struct.lock_color;
+ int color = join_struct.lock_color.LoadWithoutBarrier();
if (Interlocked::Decrement(&join_struct.join_lock) != 0)
{
@@ -746,13 +837,13 @@ public:
fire_event (gch->heap_number, time_start, type_join, join_id);
//busy wait around the color
- if (color == join_struct.lock_color)
+ if (color == join_struct.lock_color.LoadWithoutBarrier())
{
respin:
int spin_count = 4096 * (gc_heap::n_heaps - 1);
for (int j = 0; j < spin_count; j++)
{
- if (color != join_struct.lock_color)
+ if (color != join_struct.lock_color.LoadWithoutBarrier())
{
break;
}
@@ -760,7 +851,7 @@ respin:
}
// we've spun, and if color still hasn't changed, fall into hard wait
- if (color == join_struct.lock_color)
+ if (color == join_struct.lock_color.LoadWithoutBarrier())
{
dprintf (JOIN_LOG, ("join%d(%d): Join() hard wait on reset event %d, join_lock is now %d",
flavor, join_id, color, (int32_t)(join_struct.join_lock)));
@@ -778,7 +869,7 @@ respin:
}
// avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
- if (color == join_struct.lock_color)
+ if (color == join_struct.lock_color.LoadWithoutBarrier())
{
goto respin;
}
@@ -789,18 +880,10 @@ respin:
fire_event (gch->heap_number, time_end, type_join, join_id);
- // last thread out should reset event
- if (Interlocked::Decrement(&join_struct.join_restart) == 0)
- {
- // the joined event must be set at this point, because the restarting must have done this
- join_struct.join_restart = join_struct.n_threads - 1;
-// printf("Reset joined_event %d\n", color);
- }
-
#ifdef JOIN_STATS
// parallel execution starts here
- start[gch->heap_number] = GetCycleCount32();
- Interlocked::ExchangeAdd(&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number])/1000);
+ start[gch->heap_number] = get_ts();
+ Interlocked::ExchangeAdd(&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number]));
#endif //JOIN_STATS
}
else
@@ -816,29 +899,25 @@ respin:
// remember the join id, the last thread arriving, the start of the sequential phase,
// and keep track of the cycles spent waiting in the join
thd = gch->heap_number;
- start_seq = GetCycleCount32();
- Interlocked::ExchangeAdd(&in_join_total[join_id], (start_seq - end[gch->heap_number])/1000);
+ start_seq = get_ts();
+ Interlocked::ExchangeAdd(&in_join_total[join_id], (start_seq - end[gch->heap_number]));
#endif //JOIN_STATS
}
}
// Reverse join - first thread gets here does the work; other threads will only proceed
- // afte the work is done.
+ // after the work is done.
// Note that you cannot call this twice in a row on the same thread. Plus there's no
// need to call it twice in row - you should just merge the work.
BOOL r_join (gc_heap* gch, int join_id)
{
-#ifdef JOIN_STATS
- // parallel execution ends here
- end[gch->heap_number] = GetCycleCount32();
-#endif //JOIN_STATS
if (join_struct.n_threads == 1)
{
return TRUE;
}
- if (Interlocked::Decrement(&join_struct.r_join_lock) != (join_struct.n_threads - 1))
+ if (Interlocked::CompareExchange(&join_struct.r_join_lock, 0, join_struct.n_threads) == 0)
{
if (!join_struct.wait_done)
{
@@ -882,12 +961,6 @@ respin:
}
fire_event (gch->heap_number, time_end, type_join, join_id);
-
-#ifdef JOIN_STATS
- // parallel execution starts here
- start[gch->heap_number] = GetCycleCount32();
- Interlocked::ExchangeAdd(&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number])/1000);
-#endif //JOIN_STATS
}
return FALSE;
@@ -899,39 +972,69 @@ respin:
}
}
+#ifdef JOIN_STATS
+ uint64_t get_ts()
+ {
+ return GCToOSInterface::QueryPerformanceCounter();
+ }
+
+ void start_ts (gc_heap* gch)
+ {
+ // parallel execution ends here
+ start[gch->heap_number] = get_ts();
+ }
+#endif //JOIN_STATS
+
void restart()
{
#ifdef JOIN_STATS
- unsigned int elapsed_seq = GetCycleCount32() - start_seq;
- unsigned int max = 0, sum = 0;
+ uint64_t elapsed_seq = get_ts() - start_seq;
+ uint64_t max = 0, sum = 0, wake = 0;
+ uint64_t min_ts = start[0];
+ for (int i = 1; i < join_struct.n_threads; i++)
+ {
+ if(min_ts > start[i]) min_ts = start[i];
+ }
+
for (int i = 0; i < join_struct.n_threads; i++)
{
- unsigned int elapsed = end[i] - start[i];
+ uint64_t wake_delay = start[i] - min_ts;
+ uint64_t elapsed = end[i] - start[i];
if (max < elapsed)
max = elapsed;
sum += elapsed;
+ wake += wake_delay;
}
- unsigned int seq_loss = (join_struct.n_threads - 1)*elapsed_seq;
- unsigned int par_loss = join_struct.n_threads*max - sum;
+ uint64_t seq_loss = (join_struct.n_threads - 1)*elapsed_seq;
+ uint64_t par_loss = join_struct.n_threads*max - sum;
double efficiency = 0.0;
if (max > 0)
efficiency = sum*100.0/(join_struct.n_threads*max);
+ const double ts_scale = 1e-6;
+
// enable this printf to get statistics on each individual join as it occurs
-// printf("join #%3d seq_loss = %5d par_loss = %5d efficiency = %3.0f%%\n", join_id, seq_loss/1000, par_loss/1000, efficiency);
+// printf("join #%3d seq_loss = %5g par_loss = %5g efficiency = %3.0f%%\n", join_id, ts_scale*seq_loss, ts_scale*par_loss, efficiency);
- elapsed_total[join_id] += sum/1000;
- seq_loss_total[join_id] += seq_loss/1000;
- par_loss_total[join_id] += par_loss/1000;
+ elapsed_total[id] += sum;
+ wake_total[id] += wake;
+ seq_loss_total[id] += seq_loss;
+ par_loss_total[id] += par_loss;
- // every 10 seconds, print a summary of the time spent in each type of join, in 1000's of clock cycles
+ // every 10 seconds, print a summary of the time spent in each type of join
if (GCToOSInterface::GetLowPrecisionTimeStamp() - start_tick > 10*1000)
{
printf("**** summary *****\n");
for (int i = 0; i < 16; i++)
{
- printf("join #%3d seq_loss = %8u par_loss = %8u in_join_total = %8u\n", i, seq_loss_total[i], par_loss_total[i], in_join_total[i]);
- elapsed_total[i] = seq_loss_total[i] = par_loss_total[i] = in_join_total[i] = 0;
+ printf("join #%3d elapsed_total = %8g wake_loss = %8g seq_loss = %8g par_loss = %8g in_join_total = %8g\n",
+ i,
+ ts_scale*elapsed_total[i],
+ ts_scale*wake_total[i],
+ ts_scale*seq_loss_total[i],
+ ts_scale*par_loss_total[i],
+ ts_scale*in_join_total[i]);
+ elapsed_total[i] = wake_total[i] = seq_loss_total[i] = par_loss_total[i] = in_join_total[i] = 0;
}
start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
}
@@ -943,7 +1046,7 @@ respin:
join_struct.join_lock = join_struct.n_threads;
dprintf (JOIN_LOG, ("join%d(%d): Restarting from join: join_lock is %d", flavor, id, (int32_t)(join_struct.join_lock)));
// printf("restart from join #%d at cycle %u from start of gc\n", join_id, GetCycleCount32() - gc_start);
- int color = join_struct.lock_color;
+ int color = join_struct.lock_color.LoadWithoutBarrier();
join_struct.lock_color = !color;
join_struct.joined_event[color].Set();
@@ -952,7 +1055,7 @@ respin:
fire_event (join_heap_restart, time_end, type_restart, -1);
#ifdef JOIN_STATS
- start[thd] = GetCycleCount32();
+ start[thd] = get_ts();
#endif //JOIN_STATS
}
@@ -978,7 +1081,6 @@ respin:
if (join_struct.n_threads != 1)
{
join_struct.r_join_lock = join_struct.n_threads;
- join_struct.r_join_restart = join_struct.n_threads - 1;
join_struct.wait_done = FALSE;
join_struct.joined_event[first_thread_arrived].Reset();
}
@@ -1067,7 +1169,7 @@ public:
{
dprintf (3, ("cm: probing %Ix", obj));
retry:
- if (Interlocked::Exchange (&needs_checking, 1) == 0)
+ if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0)
{
// If we spend too much time spending all the allocs,
// consider adding a high water mark and scan up
@@ -1106,7 +1208,7 @@ retry:
retry:
dprintf (3, ("loh alloc: probing %Ix", obj));
- if (Interlocked::Exchange (&needs_checking, 1) == 0)
+ if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0)
{
if (obj == rwp_object)
{
@@ -1398,7 +1500,8 @@ BOOL recursive_gc_sync::allow_foreground()
#endif //BACKGROUND_GC
#endif //DACCESS_COMPILE
-#if defined(COUNT_CYCLES) || defined(JOIN_STATS) || defined(SYNCHRONIZATION_STATS)
+
+#if defined(COUNT_CYCLES)
#ifdef _MSC_VER
#pragma warning(disable:4035)
#endif //_MSC_VER
@@ -1414,7 +1517,7 @@ __asm pop EDX
#pragma warning(default:4035)
-#endif //COUNT_CYCLES || JOIN_STATS || SYNCHRONIZATION_STATS
+#endif //COUNT_CYCLES
#ifdef TIME_GC
int mark_time, plan_time, sweep_time, reloc_time, compact_time;
@@ -1564,7 +1667,7 @@ static void enter_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock)
{
retry:
- if (Interlocked::Exchange (lock, 0) >= 0)
+ if (Interlocked::CompareExchange(lock, 0, -1) >= 0)
{
unsigned int i = 0;
while (VolatileLoad(lock) >= 0)
@@ -1606,7 +1709,7 @@ retry:
inline
static BOOL try_enter_spin_lock_noinstru(RAW_KEYWORD(volatile) int32_t* lock)
{
- return (Interlocked::Exchange (&*lock, 0) < 0);
+ return (Interlocked::CompareExchange(&*lock, 0, -1) < 0);
}
inline
@@ -1657,7 +1760,7 @@ static void leave_spin_lock(GCSpinLock *pSpinLock)
//the gc thread call WaitLonger.
void WaitLonger (int i
#ifdef SYNCHRONIZATION_STATS
- , VOLATILE(GCSpinLock)* spin_lock
+ , GCSpinLock* spin_lock
#endif //SYNCHRONIZATION_STATS
)
{
@@ -1725,7 +1828,7 @@ static void enter_spin_lock (GCSpinLock* spin_lock)
{
retry:
- if (Interlocked::Exchange (&spin_lock->lock, 0) >= 0)
+ if (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) >= 0)
{
unsigned int i = 0;
while (spin_lock->lock >= 0)
@@ -1776,7 +1879,7 @@ retry:
inline BOOL try_enter_spin_lock(GCSpinLock* spin_lock)
{
- return (Interlocked::Exchange (&spin_lock->lock, 0) < 0);
+ return (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) < 0);
}
inline
@@ -2668,7 +2771,10 @@ GCSpinLock gc_heap::gc_lock;
size_t gc_heap::eph_gen_starts_size = 0;
heap_segment* gc_heap::segment_standby_list;
size_t gc_heap::last_gc_index = 0;
+#ifdef SEG_MAPPING_TABLE
size_t gc_heap::min_segment_size = 0;
+size_t gc_heap::min_segment_size_shr = 0;
+#endif //SEG_MAPPING_TABLE
size_t gc_heap::soh_segment_size = 0;
size_t gc_heap::min_loh_segment_size = 0;
size_t gc_heap::segment_info_size = 0;
@@ -3430,8 +3536,8 @@ size_t size_seg_mapping_table_of (uint8_t* from, uint8_t* end)
{
from = align_lower_segment (from);
end = align_on_segment (end);
- dprintf (1, ("from: %Ix, end: %Ix, size: %Ix", from, end, sizeof (seg_mapping)*(((end - from) / gc_heap::min_segment_size))));
- return sizeof (seg_mapping)*((end - from) / gc_heap::min_segment_size);
+ dprintf (1, ("from: %Ix, end: %Ix, size: %Ix", from, end, sizeof (seg_mapping)*(((size_t)(end - from) >> gc_heap::min_segment_size_shr))));
+ return sizeof (seg_mapping)*((size_t)(end - from) >> gc_heap::min_segment_size_shr);
}
// for seg_mapping_table we want it to start from a pointer sized address.
@@ -3444,7 +3550,7 @@ size_t align_for_seg_mapping_table (size_t size)
inline
size_t seg_mapping_word_of (uint8_t* add)
{
- return (size_t)add / gc_heap::min_segment_size;
+ return (size_t)add >> gc_heap::min_segment_size_shr;
}
#else //GROWABLE_SEG_MAPPING_TABLE
BOOL seg_mapping_table_init()
@@ -3455,7 +3561,7 @@ BOOL seg_mapping_table_init()
uint64_t total_address_space = (uint64_t)4*1024*1024*1024;
#endif // BIT64
- size_t num_entries = (size_t)(total_address_space / gc_heap::min_segment_size);
+ size_t num_entries = (size_t)(total_address_space >> gc_heap::min_segment_size_shr);
seg_mapping_table = new seg_mapping[num_entries];
if (seg_mapping_table)
@@ -3478,16 +3584,16 @@ BOOL seg_mapping_table_init()
inline
size_t ro_seg_begin_index (heap_segment* seg)
{
- size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
- begin_index = max (begin_index, (size_t)g_gc_lowest_address / gc_heap::min_segment_size);
+ size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
+ begin_index = max (begin_index, (size_t)g_gc_lowest_address >> gc_heap::min_segment_size_shr);
return begin_index;
}
inline
size_t ro_seg_end_index (heap_segment* seg)
{
- size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) / gc_heap::min_segment_size;
- end_index = min (end_index, (size_t)g_gc_highest_address / gc_heap::min_segment_size);
+ size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) >> gc_heap::min_segment_size_shr;
+ end_index = min (end_index, (size_t)g_gc_highest_address >> gc_heap::min_segment_size_shr);
return end_index;
}
@@ -3528,10 +3634,11 @@ heap_segment* ro_segment_lookup (uint8_t* o)
void gc_heap::seg_mapping_table_add_segment (heap_segment* seg, gc_heap* hp)
{
size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
- size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
+ size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
seg_mapping* begin_entry = &seg_mapping_table[begin_index];
- size_t end_index = seg_end / gc_heap::min_segment_size;
+ size_t end_index = seg_end >> gc_heap::min_segment_size_shr;
seg_mapping* end_entry = &seg_mapping_table[end_index];
+
dprintf (1, ("adding seg %Ix(%d)-%Ix(%d)",
seg, begin_index, heap_segment_reserved (seg), end_index));
@@ -3589,9 +3696,9 @@ void gc_heap::seg_mapping_table_add_segment (heap_segment* seg, gc_heap* hp)
void gc_heap::seg_mapping_table_remove_segment (heap_segment* seg)
{
size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
- size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
+ size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
seg_mapping* begin_entry = &seg_mapping_table[begin_index];
- size_t end_index = seg_end / gc_heap::min_segment_size;
+ size_t end_index = seg_end >> gc_heap::min_segment_size_shr;
seg_mapping* end_entry = &seg_mapping_table[end_index];
dprintf (1, ("removing seg %Ix(%d)-%Ix(%d)",
seg, begin_index, heap_segment_reserved (seg), end_index));
@@ -3637,7 +3744,7 @@ void gc_heap::seg_mapping_table_remove_segment (heap_segment* seg)
inline
gc_heap* seg_mapping_table_heap_of_worker (uint8_t* o)
{
- size_t index = (size_t)o / gc_heap::min_segment_size;
+ size_t index = (size_t)o >> gc_heap::min_segment_size_shr;
seg_mapping* entry = &seg_mapping_table[index];
gc_heap* hp = ((o > entry->boundary) ? entry->h1 : entry->h0);
@@ -3708,7 +3815,7 @@ heap_segment* seg_mapping_table_segment_of (uint8_t* o)
#endif //FEATURE_BASICFREEZE
#endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
- size_t index = (size_t)o / gc_heap::min_segment_size;
+ size_t index = (size_t)o >> gc_heap::min_segment_size_shr;
seg_mapping* entry = &seg_mapping_table[index];
dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, seg0: %Ix, seg1: %Ix",
@@ -4421,6 +4528,14 @@ static size_t get_valid_segment_size (BOOL large_seg=FALSE)
seg_size = initial_seg_size;
}
+#ifdef SEG_MAPPING_TABLE
+#ifdef BIT64
+ seg_size = round_up_power2 (seg_size);
+#else
+ seg_size = round_down_power2 (seg_size);
+#endif // BIT64
+#endif //SEG_MAPPING_TABLE
+
return (seg_size);
}
@@ -5552,7 +5667,7 @@ public:
// We also need to recover the saved info because we'll need to recover it later.
//
// So we would call swap_p*_plug_and_saved once to recover the object info; then call
- // it again to recover the artifical gap.
+ // it again to recover the artificial gap.
void swap_pre_plug_and_saved()
{
gap_reloc_pair temp;
@@ -8388,92 +8503,6 @@ void gc_heap::combine_mark_lists()
#endif //MULTIPLE_HEAPS
#endif //MARK_LIST
-#ifdef BIT64
-#define TOTAL_TIMES_TO_SHIFT 6
-#else
-#define TOTAL_TIMES_TO_SHIFT 5
-#endif // BIT64
-
-size_t round_up_power2 (size_t size)
-{
- unsigned short shift = 1;
- size_t shifted = 0;
-
- size--;
- for (unsigned short i = 0; i < TOTAL_TIMES_TO_SHIFT; i++)
- {
- shifted = size | (size >> shift);
- if (shifted == size)
- {
- break;
- }
-
- size = shifted;
- shift <<= 1;
- }
- shifted++;
-
- return shifted;
-}
-
-inline
-size_t round_down_power2 (size_t size)
-{
- size_t power2 = round_up_power2 (size);
-
- if (power2 != size)
- {
- power2 >>= 1;
- }
-
- return power2;
-}
-
-// the index starts from 0.
-int index_of_set_bit (size_t power2)
-{
- int low = 0;
- int high = sizeof (size_t) * 8 - 1;
- int mid;
- while (low <= high)
- {
- mid = ((low + high)/2);
- size_t temp = (size_t)1 << mid;
- if (power2 & temp)
- {
- return mid;
- }
- else if (power2 < temp)
- {
- high = mid - 1;
- }
- else
- {
- low = mid + 1;
- }
- }
-
- return -1;
-}
-
-inline
-int relative_index_power2_plug (size_t power2)
-{
- int index = index_of_set_bit (power2);
- assert (index <= MAX_INDEX_POWER2);
-
- return ((index < MIN_INDEX_POWER2) ? 0 : (index - MIN_INDEX_POWER2));
-}
-
-inline
-int relative_index_power2_free_space (size_t power2)
-{
- int index = index_of_set_bit (power2);
- assert (index <= MAX_INDEX_POWER2);
-
- return ((index < MIN_INDEX_POWER2) ? -1 : (index - MIN_INDEX_POWER2));
-}
-
class seg_free_spaces
{
struct seg_free_space
@@ -9248,7 +9277,7 @@ void gc_heap::delete_heap_segment (heap_segment* seg, BOOL consider_hoarding)
}
}
-//resets the pages beyond alloctes size so they won't be swapped out and back in
+//resets the pages beyond allocates size so they won't be swapped out and back in
void gc_heap::reset_heap_segment_pages (heap_segment* seg)
{
@@ -10252,7 +10281,7 @@ gc_heap::enter_gc_done_event_lock()
uint32_t dwSwitchCount = 0;
retry:
- if (Interlocked::Exchange (&gc_done_event_lock, 0) >= 0)
+ if (Interlocked::CompareExchange(&gc_done_event_lock, 0, -1) >= 0)
{
while (gc_done_event_lock >= 0)
{
@@ -12388,7 +12417,7 @@ BOOL gc_heap::allocate_small (int gen_number,
if (!commit_failed_p)
{
// some other threads already grabbed the more space lock and allocated
- // so we should attemp an ephemeral GC again.
+ // so we should attempt an ephemeral GC again.
assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
soh_alloc_state = a_state_trigger_ephemeral_gc;
}
@@ -12460,7 +12489,7 @@ BOOL gc_heap::allocate_small (int gen_number,
if (!commit_failed_p)
{
// some other threads already grabbed the more space lock and allocated
- // so we should attemp an ephemeral GC again.
+ // so we should attempt an ephemeral GC again.
assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
soh_alloc_state = a_state_trigger_ephemeral_gc;
}
@@ -13096,13 +13125,13 @@ int gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size,
}
#ifdef SYNCHRONIZATION_STATS
- unsigned int msl_acquire_start = GetCycleCount32();
+ int64_t msl_acquire_start = GCToOSInterface::QueryPerformanceCounter();
#endif //SYNCHRONIZATION_STATS
enter_spin_lock (&more_space_lock);
add_saved_spinlock_info (me_acquire, mt_try_alloc);
dprintf (SPINLOCK_LOG, ("[%d]Emsl for alloc", heap_number));
#ifdef SYNCHRONIZATION_STATS
- unsigned int msl_acquire = GetCycleCount32() - msl_acquire_start;
+ int64_t msl_acquire = GCToOSInterface::QueryPerformanceCounter() - msl_acquire_start;
total_msl_acquire += msl_acquire;
num_msl_acquired++;
if (msl_acquire > 200)
@@ -16522,6 +16551,9 @@ int gc_heap::garbage_collect (int n)
fix_allocation_contexts (TRUE);
#ifdef MULTIPLE_HEAPS
+#ifdef JOIN_STATS
+ gc_t_join.start_ts(this);
+#endif //JOIN_STATS
clear_gen0_bricks();
#endif //MULTIPLE_HEAPS
@@ -22006,7 +22038,7 @@ void gc_heap::plan_phase (int condemned_gen_number)
set_node_relocation_distance (plug_start, (new_address - plug_start));
if (last_node && (node_relocation_distance (last_node) ==
(node_relocation_distance (plug_start) +
- (int)node_gap_size (plug_start))))
+ node_gap_size (plug_start))))
{
//dprintf(3,( " Lb"));
dprintf (3, ("%Ix Lb", plug_start));
@@ -22687,7 +22719,7 @@ void gc_heap::plan_phase (int condemned_gen_number)
assert (len >= Align (min_obj_size));
make_unused_array (arr, len);
// fix fully contained bricks + first one
- // if the array goes beyong the first brick
+ // if the array goes beyond the first brick
size_t start_brick = brick_of (arr);
size_t end_brick = brick_of (arr + len);
if (end_brick != start_brick)
@@ -24422,7 +24454,7 @@ void gc_heap::gcmemcopy (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_car
#endif //BACKGROUND_GC
//dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
- memcopy (dest - plug_skew, src - plug_skew, (int)len);
+ memcopy (dest - plug_skew, src - plug_skew, len);
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
if (SoftwareWriteWatch::IsEnabledForGCHeap())
{
@@ -29313,7 +29345,7 @@ generation* gc_heap::expand_heap (int condemned_generation,
eph_size += switch_alignment_size(FALSE);
#endif //RESPECT_LARGE_ALIGNMENT
//Since the generation start can be larger than min_obj_size
- //Compare the alignemnt of the first object in gen1
+ //Compare the alignment of the first object in gen1
if (grow_heap_segment (new_seg, heap_segment_mem (new_seg) + eph_size) == 0)
{
fgm_result.set_fgm (fgm_commit_eph_segment, eph_size, FALSE);
@@ -33454,6 +33486,9 @@ HRESULT GCHeap::Initialize ()
size_t large_seg_size = get_valid_segment_size(TRUE);
gc_heap::min_loh_segment_size = large_seg_size;
gc_heap::min_segment_size = min (seg_size, large_seg_size);
+#ifdef SEG_MAPPING_TABLE
+ gc_heap::min_segment_size_shr = index_of_set_bit (gc_heap::min_segment_size);
+#endif //SEG_MAPPING_TABLE
#ifdef MULTIPLE_HEAPS
if (GCConfig::GetNoAffinitize())
@@ -35460,7 +35495,7 @@ size_t GCHeap::GetValidGen0MaxSize(size_t seg_size)
// performance data seems to indicate halving the size results
// in optimal perf. Ask for adjusted gen0 size.
gen0size = max(GCToOSInterface::GetLargestOnDieCacheSize(FALSE)/GCToOSInterface::GetLogicalCpuCount(),(256*1024));
-#if (defined(_TARGET_AMD64_))
+
// if gen0 size is too large given the available memory, reduce it.
// Get true cache size, as we don't want to reduce below this.
size_t trueSize = max(GCToOSInterface::GetLargestOnDieCacheSize(TRUE)/GCToOSInterface::GetLogicalCpuCount(),(256*1024));
@@ -35480,8 +35515,6 @@ size_t GCHeap::GetValidGen0MaxSize(size_t seg_size)
break;
}
}
-#endif //_TARGET_AMD64_
-
#else //SERVER_GC
gen0size = max((4*GCToOSInterface::GetLargestOnDieCacheSize(TRUE)/5),(256*1024));
#endif //SERVER_GC
@@ -35697,7 +35730,7 @@ void CFinalize::EnterFinalizeLock()
GCToEEInterface::IsPreemptiveGCDisabled(GCToEEInterface::GetThread()));
retry:
- if (Interlocked::Exchange (&lock, 0) >= 0)
+ if (Interlocked::CompareExchange(&lock, 0, -1) >= 0)
{
unsigned int i = 0;
while (lock >= 0)
diff --git a/src/gc/gc.h b/src/gc/gc.h
index 52252b8be3..e16fccb91e 100644
--- a/src/gc/gc.h
+++ b/src/gc/gc.h
@@ -197,11 +197,6 @@ struct alloc_context : gc_alloc_context
};
class IGCHeapInternal : public IGCHeap {
- friend struct ::_DacGlobals;
-#ifdef DACCESS_COMPILE
- friend class ClrDataAccess;
-#endif
-
public:
virtual ~IGCHeapInternal() {}
@@ -249,11 +244,13 @@ void TouchPages(void * pStart, size_t cb);
void updateGCShadow(Object** ptr, Object* val);
#endif
+#ifndef DACCESS_COMPILE
// The single GC heap instance, shared with the VM.
extern IGCHeapInternal* g_theGCHeap;
// The single GC handle manager instance, shared with the VM.
extern IGCHandleManager* g_theGCHandleManager;
+#endif // DACCESS_COMPILE
#ifndef DACCESS_COMPILE
inline bool IsGCInProgress(bool bConsiderGCStart = false)
diff --git a/src/gc/gccommon.cpp b/src/gc/gccommon.cpp
index 3e45eb60d5..92c0e7b7b5 100644
--- a/src/gc/gccommon.cpp
+++ b/src/gc/gccommon.cpp
@@ -124,6 +124,8 @@ namespace SVR
extern void PopulateDacVars(GcDacVars* dacVars);
}
+extern void PopulateHandleTableDacVars(GcDacVars* dacVars);
+
//------------------------------------------------------------------
// Externally-facing GC symbols, used to initialize the GC
// -----------------------------------------------------------------
@@ -203,6 +205,7 @@ InitializeGarbageCollector(
WKS::PopulateDacVars(gcDacVars);
#endif
+ PopulateHandleTableDacVars(gcDacVars);
if (heap == nullptr)
{
return false;
diff --git a/src/gc/gcenv.ee.standalone.inl b/src/gc/gcenv.ee.standalone.inl
index c66acf0a61..0dcf05da4d 100644
--- a/src/gc/gcenv.ee.standalone.inl
+++ b/src/gc/gcenv.ee.standalone.inl
@@ -6,280 +6,244 @@
#define __GCTOENV_EE_STANDALONE_INL__
#include "gcinterface.h"
+#include "env/gcenv.ee.h"
// The singular interface instance. All calls in GCToEEInterface
// will be fowarded to this interface instance.
extern IGCToCLR* g_theGCToCLR;
-namespace
-{
-
-#include "env/gcenv.ee.h"
-
-// A note about this:
-// In general, we don't want to pretend to be smarter than the compiler
-// and force it to inline things. However, inlining is here is required
-// for correctness as it stands today (though it will not always be required).
-//
-// The reason for this is because:
-// 1) This file (and the GCToEEInterface class) define symbols that are inline
-// and static, so the symbol GCToEEInterface::XYZ defines a symbol with weak
-// linkage when the function is not inlined,
-// 2) src/vm/gcenv.ee.cpp all define symbols that are not inline and instance methods
-// of GCToEEInterface, with external linkage.
-// 3) When it comes time to link the GC and the VM, the linker observes the duplicate
-// symbols and discards the one with weak linkage.
-// 4) All of the calls within the GC to the functions in this file are replaced by
-// the linker to calls to the implementation of a pure virtual IGCToCLR. The
-// functions implementing IGCToCLR have an extra argument (this).
-// 5) Now, all calls to these functions from within the GC are doomed because of the
-// functions that actually get called expect this to be in rdi, where the compiler
-// has placed the first argument instead.
-//
-// For now, by forcing the compiler to inline these functions, the compiler won't actually
-// emit symbols for them and we'll avoid the linker havoc.
-#ifdef _MSC_VER
- #define ALWAYS_INLINE __forceinline
-#else
- #define ALWAYS_INLINE __attribute__((always_inline)) inline
-#endif
-
// When we are building the GC in a standalone environment, we
// will be dispatching virtually against g_theGCToCLR to call
// into the EE. This class provides an identical API to the existing
// GCToEEInterface, but only forwards the call onto the global
// g_theGCToCLR instance.
-ALWAYS_INLINE void GCToEEInterface::SuspendEE(SUSPEND_REASON reason)
+inline void GCToEEInterface::SuspendEE(SUSPEND_REASON reason)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->SuspendEE(reason);
}
-ALWAYS_INLINE void GCToEEInterface::RestartEE(bool bFinishedGC)
+inline void GCToEEInterface::RestartEE(bool bFinishedGC)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->RestartEE(bFinishedGC);
}
-ALWAYS_INLINE void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, ScanContext* sc)
+inline void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, ScanContext* sc)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->GcScanRoots(fn, condemned, max_gen, sc);
}
-ALWAYS_INLINE void GCToEEInterface::GcStartWork(int condemned, int max_gen)
+inline void GCToEEInterface::GcStartWork(int condemned, int max_gen)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->GcStartWork(condemned, max_gen);
}
-ALWAYS_INLINE void GCToEEInterface::AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc)
+inline void GCToEEInterface::AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->AfterGcScanRoots(condemned, max_gen, sc);
}
-ALWAYS_INLINE void GCToEEInterface::GcBeforeBGCSweepWork()
+inline void GCToEEInterface::GcBeforeBGCSweepWork()
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->GcBeforeBGCSweepWork();
}
-ALWAYS_INLINE void GCToEEInterface::GcDone(int condemned)
+inline void GCToEEInterface::GcDone(int condemned)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->GcDone(condemned);
}
-ALWAYS_INLINE bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject)
+inline bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->RefCountedHandleCallbacks(pObject);
}
-ALWAYS_INLINE void GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2)
+inline void GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->SyncBlockCacheWeakPtrScan(scanProc, lp1, lp2);
}
-ALWAYS_INLINE void GCToEEInterface::SyncBlockCacheDemote(int max_gen)
+inline void GCToEEInterface::SyncBlockCacheDemote(int max_gen)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->SyncBlockCacheDemote(max_gen);
}
-ALWAYS_INLINE void GCToEEInterface::SyncBlockCachePromotionsGranted(int max_gen)
+inline void GCToEEInterface::SyncBlockCachePromotionsGranted(int max_gen)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->SyncBlockCachePromotionsGranted(max_gen);
}
-ALWAYS_INLINE bool GCToEEInterface::IsPreemptiveGCDisabled(Thread * pThread)
+inline bool GCToEEInterface::IsPreemptiveGCDisabled(Thread * pThread)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->IsPreemptiveGCDisabled(pThread);
}
-ALWAYS_INLINE void GCToEEInterface::EnablePreemptiveGC(Thread * pThread)
+inline void GCToEEInterface::EnablePreemptiveGC(Thread * pThread)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->EnablePreemptiveGC(pThread);
}
-ALWAYS_INLINE void GCToEEInterface::DisablePreemptiveGC(Thread * pThread)
+inline void GCToEEInterface::DisablePreemptiveGC(Thread * pThread)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DisablePreemptiveGC(pThread);
}
-ALWAYS_INLINE Thread* GCToEEInterface::GetThread()
+inline Thread* GCToEEInterface::GetThread()
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->GetThread();
}
-ALWAYS_INLINE bool GCToEEInterface::TrapReturningThreads()
+inline bool GCToEEInterface::TrapReturningThreads()
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->TrapReturningThreads();
}
-ALWAYS_INLINE gc_alloc_context * GCToEEInterface::GetAllocContext(Thread * pThread)
+inline gc_alloc_context * GCToEEInterface::GetAllocContext(Thread * pThread)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->GetAllocContext(pThread);
}
-ALWAYS_INLINE bool GCToEEInterface::CatchAtSafePoint(Thread * pThread)
+inline bool GCToEEInterface::CatchAtSafePoint(Thread * pThread)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->CatchAtSafePoint(pThread);
}
-ALWAYS_INLINE void GCToEEInterface::GcEnumAllocContexts(enum_alloc_context_func* fn, void* param)
+inline void GCToEEInterface::GcEnumAllocContexts(enum_alloc_context_func* fn, void* param)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->GcEnumAllocContexts(fn, param);
}
-ALWAYS_INLINE Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg)
+inline Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->CreateBackgroundThread(threadStart, arg);
}
-ALWAYS_INLINE void GCToEEInterface::DiagGCStart(int gen, bool isInduced)
+inline void GCToEEInterface::DiagGCStart(int gen, bool isInduced)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagGCStart(gen, isInduced);
}
-ALWAYS_INLINE void GCToEEInterface::DiagUpdateGenerationBounds()
+inline void GCToEEInterface::DiagUpdateGenerationBounds()
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagUpdateGenerationBounds();
}
-ALWAYS_INLINE void GCToEEInterface::DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent)
+inline void GCToEEInterface::DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagGCEnd(index, gen, reason, fConcurrent);
}
-ALWAYS_INLINE void GCToEEInterface::DiagWalkFReachableObjects(void* gcContext)
+inline void GCToEEInterface::DiagWalkFReachableObjects(void* gcContext)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagWalkFReachableObjects(gcContext);
}
-ALWAYS_INLINE void GCToEEInterface::DiagWalkSurvivors(void* gcContext)
+inline void GCToEEInterface::DiagWalkSurvivors(void* gcContext)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagWalkSurvivors(gcContext);
}
-ALWAYS_INLINE void GCToEEInterface::DiagWalkLOHSurvivors(void* gcContext)
+inline void GCToEEInterface::DiagWalkLOHSurvivors(void* gcContext)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagWalkLOHSurvivors(gcContext);
}
-ALWAYS_INLINE void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext)
+inline void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->DiagWalkBGCSurvivors(gcContext);
}
-ALWAYS_INLINE void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
+inline void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->StompWriteBarrier(args);
}
-ALWAYS_INLINE void GCToEEInterface::EnableFinalization(bool foundFinalizers)
+inline void GCToEEInterface::EnableFinalization(bool foundFinalizers)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->EnableFinalization(foundFinalizers);
}
-ALWAYS_INLINE void GCToEEInterface::HandleFatalError(unsigned int exitCode)
+inline void GCToEEInterface::HandleFatalError(unsigned int exitCode)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->HandleFatalError(exitCode);
}
-ALWAYS_INLINE bool GCToEEInterface::ShouldFinalizeObjectForUnload(AppDomain* pDomain, Object* obj)
+inline bool GCToEEInterface::ShouldFinalizeObjectForUnload(AppDomain* pDomain, Object* obj)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->ShouldFinalizeObjectForUnload(pDomain, obj);
}
-ALWAYS_INLINE bool GCToEEInterface::ForceFullGCToBeBlocking()
+inline bool GCToEEInterface::ForceFullGCToBeBlocking()
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->ForceFullGCToBeBlocking();
}
-ALWAYS_INLINE bool GCToEEInterface::EagerFinalized(Object* obj)
+inline bool GCToEEInterface::EagerFinalized(Object* obj)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->EagerFinalized(obj);
}
-ALWAYS_INLINE MethodTable* GCToEEInterface::GetFreeObjectMethodTable()
+inline MethodTable* GCToEEInterface::GetFreeObjectMethodTable()
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->GetFreeObjectMethodTable();
}
-ALWAYS_INLINE bool GCToEEInterface::GetBooleanConfigValue(const char* key, bool* value)
+inline bool GCToEEInterface::GetBooleanConfigValue(const char* key, bool* value)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->GetBooleanConfigValue(key, value);
}
-ALWAYS_INLINE bool GCToEEInterface::GetIntConfigValue(const char* key, int64_t* value)
+inline bool GCToEEInterface::GetIntConfigValue(const char* key, int64_t* value)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->GetIntConfigValue(key, value);
}
-ALWAYS_INLINE bool GCToEEInterface::GetStringConfigValue(const char* key, const char** value)
+inline bool GCToEEInterface::GetStringConfigValue(const char* key, const char** value)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->GetStringConfigValue(key, value);
}
-ALWAYS_INLINE void GCToEEInterface::FreeStringConfigValue(const char* value)
+inline void GCToEEInterface::FreeStringConfigValue(const char* value)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->FreeStringConfigValue(value);
}
-#undef ALWAYS_INLINE
-
-} // anonymous namespace
-
#endif // __GCTOENV_EE_STANDALONE_INL__
diff --git a/src/gc/gcenv.inl b/src/gc/gcenv.inl
new file mode 100644
index 0000000000..f3d7d32922
--- /dev/null
+++ b/src/gc/gcenv.inl
@@ -0,0 +1,9 @@
+// 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.
+
+#ifdef _WIN32
+#include "gcenv.windows.inl"
+#else
+#include "gcenv.unix.inl"
+#endif // _WIN32
diff --git a/src/gc/gcinterface.dac.h b/src/gc/gcinterface.dac.h
index 647101fa1f..93698c05a9 100644
--- a/src/gc/gcinterface.dac.h
+++ b/src/gc/gcinterface.dac.h
@@ -11,12 +11,14 @@
// GC-internal type's fields, while still maintaining the same layout.
// This interface is strictly versioned, see gcinterface.dacvars.def for more information.
-#define NUM_GC_DATA_POINTS 9
-#define MAX_COMPACT_REASONS_COUNT 11
-#define MAX_EXPAND_MECHANISMS_COUNT 6
-#define MAX_GC_MECHANISM_BITS_COUNT 2
-#define MAX_GLOBAL_GC_MECHANISMS_COUNT 6
-#define NUMBERGENERATIONS 4
+#define NUM_GC_DATA_POINTS 9
+#define MAX_COMPACT_REASONS_COUNT 11
+#define MAX_EXPAND_MECHANISMS_COUNT 6
+#define MAX_GC_MECHANISM_BITS_COUNT 2
+#define MAX_GLOBAL_GC_MECHANISMS_COUNT 6
+#define NUMBERGENERATIONS 4
+#define INITIAL_HANDLE_TABLE_ARRAY_SIZE 10
+#define HANDLE_MAX_INTERNAL_TYPES 12
// Analogue for the GC heap_segment class, containing information regarding a single
// heap segment.
@@ -49,6 +51,32 @@ public:
uint8_t** m_FillPointers[NUMBERGENERATIONS + ExtraSegCount];
};
+class dac_handle_table {
+public:
+ // On the handle table side, this is an ADIndex. They should still have
+ // the same layout.
+ //
+ // We do try to keep everything that the DAC knows about as close to the
+ // start of the struct as possible to avoid having padding members. However,
+ // HandleTable has rgTypeFlags at offset 0 for performance reasons and
+ // we don't want to disrupt that.
+ uint32_t padding[HANDLE_MAX_INTERNAL_TYPES];
+ DWORD uADIndex;
+};
+
+class dac_handle_table_bucket {
+public:
+ DPTR(DPTR(dac_handle_table)) pTable;
+ uint32_t HandleTableIndex;
+};
+
+class dac_handle_table_map {
+public:
+ DPTR(DPTR(dac_handle_table_bucket)) pBuckets;
+ DPTR(dac_handle_table_map) pNext;
+ uint32_t dwMaxIndex;
+};
+
// Possible values of the current_c_gc_state dacvar, indicating the state of
// a background GC.
enum c_gc_state
@@ -132,6 +160,23 @@ public:
};
+// The DAC links against six symbols that build as part of the VM DACCESS_COMPILE
+// build. These symbols are considered to be GC-private functions, but the DAC needs
+// to use them in order to perform some handle-related functions. These six functions
+// are adorned by this macro to make clear that their implementations must be versioned
+// alongside the rest of this file.
+//
+// Practically, this macro ensures that the target symbols aren't mangled, since the
+// DAC calls them with a signature slightly different than the one used when they
+// were defined.
+#define GC_DAC_VISIBLE
+
+#ifdef DACCESS_COMPILE
+#define GC_DAC_VISIBLE_NO_MANGLE extern "C"
+#else
+#define GC_DAC_VISIBLE_NO_MANGLE
+#endif // DACCESS_COMPILE
+
// The actual structure containing the DAC variables. When DACCESS_COMPILE is not
// defined (i.e. the normal runtime build), this structure contains pointers to the
// GC's global DAC variabels. When DACCESS_COMPILE is defined (i.e. the DAC build),
@@ -142,9 +187,7 @@ struct GcDacVars {
uint8_t minor_version_number;
size_t generation_size;
#ifdef DACCESS_COMPILE
- #define GC_DAC_VAR(type, name) DPTR(type) name;
- // ArrayDPTR doesn't allow decaying arrays to pointers, which
- // avoids some accidental errors.
+ #define GC_DAC_VAR(type, name) DPTR(type) name;
#define GC_DAC_PTR_VAR(type, name) DPTR(type*) name;
#define GC_DAC_ARRAY_VAR(type, name) DPTR(type) name;
#else
diff --git a/src/gc/gcinterface.dacvars.def b/src/gc/gcinterface.dacvars.def
index b788079dcb..89cb01206f 100644
--- a/src/gc/gcinterface.dacvars.def
+++ b/src/gc/gcinterface.dacvars.def
@@ -60,6 +60,7 @@ GC_DAC_ARRAY_VAR (size_t, interesting_data_per_heap)
GC_DAC_ARRAY_VAR (size_t, compact_reasons_per_heap)
GC_DAC_ARRAY_VAR (size_t, expand_mechanisms_per_heap)
GC_DAC_ARRAY_VAR (size_t, interesting_mechanism_bits_per_heap)
+GC_DAC_VAR (dac_handle_table_map, handle_table_map)
#undef GC_DAC_VAR
#undef GC_DAC_ARRAY_VAR
diff --git a/src/gc/gcinterface.h b/src/gc/gcinterface.h
index ec7c457a6f..5bec7212e8 100644
--- a/src/gc/gcinterface.h
+++ b/src/gc/gcinterface.h
@@ -154,7 +154,10 @@ struct segment_info
};
#ifdef PROFILING_SUPPORTED
+#ifndef BUILD_AS_STANDALONE
+// [LOCALGC TODO] Enable profiling (GitHub #11515)
#define GC_PROFILING //Turn on profiling
+#endif // BUILD_AS_STANDALONE
#endif // PROFILING_SUPPORTED
#define LARGE_OBJECT_SIZE ((size_t)(85000))
diff --git a/src/gc/gcpriv.h b/src/gc/gcpriv.h
index 00092b1256..c9c6fa32f9 100644
--- a/src/gc/gcpriv.h
+++ b/src/gc/gcpriv.h
@@ -282,24 +282,13 @@ void GCLog (const char *fmt, ... );
#define ASSERT _ASSERTE
#endif // FEATURE_REDHAWK
-#ifdef _DEBUG
-
struct GCDebugSpinLock {
VOLATILE(int32_t) lock; // -1 if free, 0 if held
+#ifdef _DEBUG
VOLATILE(Thread *) holding_thread; // -1 if no thread holds the lock.
VOLATILE(BOOL) released_by_gc_p; // a GC thread released the lock.
-
- GCDebugSpinLock()
- : lock(-1), holding_thread((Thread*) -1)
- {
- }
-};
-typedef GCDebugSpinLock GCSpinLock;
-
-#elif defined (SYNCHRONIZATION_STATS)
-
-struct GCSpinLockInstru {
- VOLATILE(int32_t) lock;
+#endif
+#if defined (SYNCHRONIZATION_STATS)
// number of times we went into SwitchToThread in enter_spin_lock.
unsigned int num_switch_thread;
// number of times we went into WaitLonger.
@@ -308,12 +297,20 @@ struct GCSpinLockInstru {
unsigned int num_switch_thread_w;
// number of times we went to calling DisablePreemptiveGC in WaitLonger.
unsigned int num_disable_preemptive_w;
+#endif
- GCSpinLockInstru()
- : lock(-1), num_switch_thread(0), num_wait_longer(0), num_switch_thread_w(0), num_disable_preemptive_w(0)
+ GCDebugSpinLock()
+ : lock(-1)
+#ifdef _DEBUG
+ , holding_thread((Thread*) -1)
+#endif
+#if defined (SYNCHRONIZATION_STATS)
+ , num_switch_thread(0), num_wait_longer(0), num_switch_thread_w(0), num_disable_preemptive_w(0)
+#endif
{
}
+#if defined (SYNCHRONIZATION_STATS)
void init()
{
num_switch_thread = 0;
@@ -321,24 +318,10 @@ struct GCSpinLockInstru {
num_switch_thread_w = 0;
num_disable_preemptive_w = 0;
}
-};
-
-typedef GCSpinLockInstru GCSpinLock;
-
-#else
-
-struct GCDebugSpinLock {
- VOLATILE(int32_t) lock; // -1 if free, 0 if held
-
- GCDebugSpinLock()
- : lock(-1)
- {
- }
+#endif
};
typedef GCDebugSpinLock GCSpinLock;
-#endif
-
class mark;
class heap_segment;
class CObjectHeader;
@@ -1033,12 +1016,6 @@ enum interesting_data_point
//class definition of the internal class
class gc_heap
{
- friend struct ::_DacGlobals;
-#ifdef DACCESS_COMPILE
- friend class ::ClrDataAccess;
- friend class ::DacHeapWalker;
-#endif //DACCESS_COMPILE
-
friend class GCHeap;
#ifdef FEATURE_PREMORTEM_FINALIZATION
friend class CFinalize;
@@ -2956,9 +2933,14 @@ public:
PER_HEAP_ISOLATED
size_t last_gc_index;
+#ifdef SEG_MAPPING_TABLE
PER_HEAP_ISOLATED
size_t min_segment_size;
+ PER_HEAP_ISOLATED
+ size_t min_segment_size_shr;
+#endif //SEG_MAPPING_TABLE
+
// For SOH we always allocate segments of the same
// size unless no_gc_region requires larger ones.
PER_HEAP_ISOLATED
@@ -3639,7 +3621,7 @@ protected:
}; // class gc_heap
#define ASSERT_OFFSETS_MATCH(field) \
- static_assert_no_msg(offsetof(dac_gc_heap, field) == offsetof(gc_heap, field))
+ static_assert(offsetof(dac_gc_heap, field) == offsetof(gc_heap, field), #field " offset mismatch")
#ifdef MULTIPLE_HEAPS
ASSERT_OFFSETS_MATCH(alloc_allocated);
diff --git a/src/gc/gcscan.h b/src/gc/gcscan.h
index c7060f3f51..b183a78d44 100644
--- a/src/gc/gcscan.h
+++ b/src/gc/gcscan.h
@@ -33,8 +33,6 @@ struct DhContext
class GCScan
{
- friend struct ::_DacGlobals;
-
public:
static void GcScanSizedRefs(promote_func* fn, int condemned, int max_gen, ScanContext* sc);
diff --git a/src/gc/gcsvr.cpp b/src/gc/gcsvr.cpp
index 6f89cab132..db67aa3eef 100644
--- a/src/gc/gcsvr.cpp
+++ b/src/gc/gcsvr.cpp
@@ -16,6 +16,7 @@
#include "softwarewritewatch.h"
#include "handletable.h"
#include "handletable.inl"
+#include "gcenv.inl"
#define SERVER_GC 1
diff --git a/src/gc/gcwks.cpp b/src/gc/gcwks.cpp
index 07a4e20199..335755608d 100644
--- a/src/gc/gcwks.cpp
+++ b/src/gc/gcwks.cpp
@@ -14,6 +14,7 @@
#include "softwarewritewatch.h"
#include "handletable.h"
#include "handletable.inl"
+#include "gcenv.inl"
#ifdef SERVER_GC
#undef SERVER_GC
diff --git a/src/gc/handletable.cpp b/src/gc/handletable.cpp
index 8c6c835200..4942f017ac 100644
--- a/src/gc/handletable.cpp
+++ b/src/gc/handletable.cpp
@@ -263,6 +263,7 @@ ADIndex HndGetHandleTableADIndex(HHANDLETABLE hTable)
*
* Retrieves the AppDomain index associated with a handle table at creation
*/
+GC_DAC_VISIBLE
ADIndex HndGetHandleADIndex(OBJECTHANDLE handle)
{
WRAPPER_NO_CONTRACT;
@@ -570,6 +571,7 @@ uintptr_t HndCompareExchangeHandleExtraInfo(OBJECTHANDLE handle, uint32_t uType,
* Retrieves owner data from handle.
*
*/
+GC_DAC_VISIBLE
uintptr_t HndGetHandleExtraInfo(OBJECTHANDLE handle)
{
WRAPPER_NO_CONTRACT;
@@ -659,6 +661,7 @@ void HndLogSetEvent(OBJECTHANDLE handle, _UNCHECKED_OBJECTREF value)
#endif
}
+#ifndef DACCESS_COMPILE
/*
* HndWriteBarrier
*
@@ -733,6 +736,7 @@ void HndWriteBarrier(OBJECTHANDLE handle, OBJECTREF objref)
}
}
}
+#endif // DACCESS_COMPILE
/*
* HndEnumHandles
@@ -743,6 +747,7 @@ void HndWriteBarrier(OBJECTHANDLE handle, OBJECTREF objref)
* needs to enumerate all roots in the handle table.
*
*/
+GC_DAC_VISIBLE_NO_MANGLE
void HndEnumHandles(HHANDLETABLE hTable, const uint32_t *puType, uint32_t uTypeCount,
HANDLESCANPROC pfnEnum, uintptr_t lParam1, uintptr_t lParam2, bool fAsync)
{
@@ -803,6 +808,7 @@ void HndEnumHandles(HHANDLETABLE hTable, const uint32_t *puType, uint32_t uTypeC
* as it scans.
*
*/
+GC_DAC_VISIBLE_NO_MANGLE
void HndScanHandlesForGC(HHANDLETABLE hTable, HANDLESCANPROC scanProc, uintptr_t param1, uintptr_t param2,
const uint32_t *types, uint32_t typeCount, uint32_t condemned, uint32_t maxgen, uint32_t flags)
{
diff --git a/src/gc/handletable.h b/src/gc/handletable.h
index a952ad799f..70959edf3b 100644
--- a/src/gc/handletable.h
+++ b/src/gc/handletable.h
@@ -63,6 +63,8 @@ void HndDestroyHandleTable(HHANDLETABLE hTable);
void HndSetHandleTableIndex(HHANDLETABLE hTable, uint32_t uTableIndex);
uint32_t HndGetHandleTableIndex(HHANDLETABLE hTable);
ADIndex HndGetHandleTableADIndex(HHANDLETABLE hTable);
+
+GC_DAC_VISIBLE
ADIndex HndGetHandleADIndex(OBJECTHANDLE handle);
#ifndef DACCESS_COMPILE
@@ -81,6 +83,7 @@ void HndSetHandleExtraInfo(OBJECTHANDLE handle, uint32_t uType, uintp
uintptr_t HndCompareExchangeHandleExtraInfo(OBJECTHANDLE handle, uint32_t uType, uintptr_t lOldExtraInfo, uintptr_t lNewExtraInfo);
#endif // !DACCESS_COMPILE
+GC_DAC_VISIBLE
uintptr_t HndGetHandleExtraInfo(OBJECTHANDLE handle);
/*
@@ -101,6 +104,7 @@ void HndLogSetEvent(OBJECTHANDLE handle, _UNCHECKED_OBJECTREF value);
/*
* NON-GC handle enumeration
*/
+GC_DAC_VISIBLE_NO_MANGLE
void HndEnumHandles(HHANDLETABLE hTable, const uint32_t *puType, uint32_t uTypeCount,
HANDLESCANPROC pfnEnum, uintptr_t lParam1, uintptr_t lParam2, bool fAsync);
@@ -112,7 +116,7 @@ void HndEnumHandles(HHANDLETABLE hTable, const uint32_t *puType, uint32_t uTypeC
#define HNDGCF_ASYNC (0x00000002) // drop the table lock while scanning
#define HNDGCF_EXTRAINFO (0x00000004) // iterate per-handle data while scanning
-
+GC_DAC_VISIBLE_NO_MANGLE
void HndScanHandlesForGC(HHANDLETABLE hTable,
HANDLESCANPROC scanProc,
uintptr_t param1,
@@ -168,7 +172,9 @@ BOOL HndFirstAssignHandle(OBJECTHANDLE handle, OBJECTREF objref);
* on the VM side.
*
*/
-FORCEINLINE OBJECTREF HndFetchHandle(OBJECTHANDLE handle)
+GC_DAC_VISIBLE
+FORCEINLINE
+OBJECTREF HndFetchHandle(OBJECTHANDLE handle)
{
WRAPPER_NO_CONTRACT;
diff --git a/src/gc/handletablecore.cpp b/src/gc/handletablecore.cpp
index 4548237eda..2a69afc01d 100644
--- a/src/gc/handletablecore.cpp
+++ b/src/gc/handletablecore.cpp
@@ -14,6 +14,7 @@
#include "common.h"
#include "gcenv.h"
+#include "gcenv.inl"
#include "gc.h"
#ifndef FEATURE_REDHAWK
diff --git a/src/gc/handletablepriv.h b/src/gc/handletablepriv.h
index cda1cb08aa..f33a547a23 100644
--- a/src/gc/handletablepriv.h
+++ b/src/gc/handletablepriv.h
@@ -56,9 +56,6 @@
#define HANDLE_HANDLES_PER_BLOCK (64) // segment suballocation granularity
#define HANDLE_OPTIMIZE_FOR_64_HANDLE_BLOCKS // flag for certain optimizations
-// maximum number of internally supported handle types
-#define HANDLE_MAX_INTERNAL_TYPES (12) // should be a multiple of 4
-
// number of types allowed for public callers
#define HANDLE_MAX_PUBLIC_TYPES (HANDLE_MAX_INTERNAL_TYPES - 1) // reserve one internal type
@@ -513,6 +510,11 @@ struct HandleTable
uint32_t rgTypeFlags[HANDLE_MAX_INTERNAL_TYPES];
/*
+ * per-table AppDomain info
+ */
+ ADIndex uADIndex;
+
+ /*
* lock for this table
*/
CrstStatic Lock;
@@ -544,11 +546,6 @@ struct HandleTable
uint32_t uTableIndex;
/*
- * per-table AppDomain info
- */
- ADIndex uADIndex;
-
- /*
* one-level per-type 'quick' handle cache
*/
OBJECTHANDLE rgQuickCache[HANDLE_MAX_INTERNAL_TYPES]; // interlocked ops used here
diff --git a/src/gc/objecthandle.cpp b/src/gc/objecthandle.cpp
index d7c5d39de5..24db07dfb1 100644
--- a/src/gc/objecthandle.cpp
+++ b/src/gc/objecthandle.cpp
@@ -21,14 +21,16 @@
#include "gchandletableimpl.h"
+#ifndef BUILD_AS_STANDALONE
#ifdef FEATURE_COMINTEROP
#include "comcallablewrapper.h"
#endif // FEATURE_COMINTEROP
#ifndef FEATURE_REDHAWK
#include "nativeoverlapped.h"
#endif // FEATURE_REDHAWK
+#endif // BUILD_AS_STANDALONE
-GVAL_IMPL(HandleTableMap, g_HandleTableMap);
+HandleTableMap g_HandleTableMap;
// Array of contexts used while scanning dependent handles for promotion. There are as many contexts as GC
// heaps and they're allocated by Ref_Initialize and initialized during each GC by GcDhInitialScan.
@@ -137,7 +139,7 @@ void CALLBACK TraceDependentHandle(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pEx
// At this point, it's possible that either or both of the primary and secondary
// objects are NULL. However, if the secondary object is non-NULL, then the primary
// object should also be non-NULL.
- _ASSERTE(*pExtraInfo == NULL || *pObjRef != NULL);
+ _ASSERTE(*pExtraInfo == 0 || *pObjRef != NULL);
struct DIAG_DEPSCANINFO *pInfo = (struct DIAG_DEPSCANINFO*)lp2;
@@ -1894,9 +1896,24 @@ bool HandleTableBucket::Contains(OBJECTHANDLE handle)
#endif // !DACCESS_COMPILE
+GC_DAC_VISIBLE
OBJECTREF GetDependentHandleSecondary(OBJECTHANDLE handle)
{
WRAPPER_NO_CONTRACT;
return UNCHECKED_OBJECTREF_TO_OBJECTREF((_UNCHECKED_OBJECTREF)HndGetHandleExtraInfo(handle));
}
+
+void PopulateHandleTableDacVars(GcDacVars* gcDacVars)
+{
+ static_assert(offsetof(HandleTableMap, pBuckets) == offsetof(dac_handle_table_map, pBuckets), "handle table map DAC layout mismatch");
+ static_assert(offsetof(HandleTableMap, pNext) == offsetof(dac_handle_table_map, pNext), "handle table map DAC layout mismatch");
+ static_assert(offsetof(HandleTableMap, dwMaxIndex) == offsetof(dac_handle_table_map, dwMaxIndex), "handle table map DAC layout mismatch");
+ static_assert(offsetof(HandleTableBucket, pTable) == offsetof(dac_handle_table_bucket, pTable), "handle table bucket DAC layout mismatch");
+ static_assert(offsetof(HandleTableBucket, HandleTableIndex) == offsetof(dac_handle_table_bucket, HandleTableIndex), "handle table bucket DAC layout mismatch");
+ static_assert(offsetof(HandleTable, uADIndex) == offsetof(dac_handle_table, uADIndex), "handle table DAC layout mismatch");
+
+#ifndef DACCESS_COMPILE
+ gcDacVars->handle_table_map = reinterpret_cast<dac_handle_table_map*>(&g_HandleTableMap);
+#endif // DACCESS_COMPILE
+}
diff --git a/src/gc/objecthandle.h b/src/gc/objecthandle.h
index 6b8bcb70ed..a6d2259009 100644
--- a/src/gc/objecthandle.h
+++ b/src/gc/objecthandle.h
@@ -17,10 +17,6 @@
*/
#include "handletable.h"
-#ifdef FEATURE_COMINTEROP
-#include <weakreference.h>
-#endif // FEATURE_COMINTEROP
-
typedef DPTR(struct HandleTableMap) PTR_HandleTableMap;
typedef DPTR(struct HandleTableBucket) PTR_HandleTableBucket;
typedef DPTR(PTR_HandleTableBucket) PTR_PTR_HandleTableBucket;
@@ -32,9 +28,7 @@ struct HandleTableMap
uint32_t dwMaxIndex;
};
-GVAL_DECL(HandleTableMap, g_HandleTableMap);
-
-#define INITIAL_HANDLE_TABLE_ARRAY_SIZE 10
+extern HandleTableMap g_HandleTableMap;
// struct containing g_SystemInfo.dwNumberOfProcessors HHANDLETABLEs and current table index
// instead of just single HHANDLETABLE for on-fly balancing while adding handles on multiproc machines
@@ -61,6 +55,7 @@ struct HandleTableBucket
(flag == VHT_STRONG) || \
(flag == VHT_PINNED))
+GC_DAC_VISIBLE
OBJECTREF GetDependentHandleSecondary(OBJECTHANDLE handle);
#ifndef DACCESS_COMPILE
diff --git a/src/gc/sample/GCSample.cpp b/src/gc/sample/GCSample.cpp
index 43cb23878e..62eec6698f 100644
--- a/src/gc/sample/GCSample.cpp
+++ b/src/gc/sample/GCSample.cpp
@@ -125,7 +125,7 @@ int __cdecl main(int argc, char* argv[])
//
static MethodTable freeObjectMT;
freeObjectMT.InitializeFreeObject();
- g_pFreeObjectMethodTable = &freeObjectMT;
+ g_gc_pFreeObjectMethodTable = &freeObjectMT;
//
// Initialize GC heap
diff --git a/src/gc/sample/gcenv.ee.cpp b/src/gc/sample/gcenv.ee.cpp
index c0265d5fcf..de1a2ad5ee 100644
--- a/src/gc/sample/gcenv.ee.cpp
+++ b/src/gc/sample/gcenv.ee.cpp
@@ -320,15 +320,3 @@ MethodTable* GCToEEInterface::GetFreeObjectMethodTable()
{
return g_pFreeObjectMethodTable;
}
-
-bool IsGCSpecialThread()
-{
- // TODO: Implement for background GC
- return false;
-}
-
-bool IsGCThread()
-{
- return false;
-}
-
diff --git a/src/gc/sample/gcenv.h b/src/gc/sample/gcenv.h
index 9bace1d11c..a86cf535d5 100644
--- a/src/gc/sample/gcenv.h
+++ b/src/gc/sample/gcenv.h
@@ -29,6 +29,7 @@
#include "gcenv.object.h"
#include "gcenv.sync.h"
#include "gcenv.ee.h"
+#include "volatile.h"
#ifdef PLATFORM_UNIX
#include "gcenv.unix.inl"
diff --git a/src/gc/unix/cgroup.cpp b/src/gc/unix/cgroup.cpp
index 1775ef7ff0..992678b634 100644
--- a/src/gc/unix/cgroup.cpp
+++ b/src/gc/unix/cgroup.cpp
@@ -9,7 +9,7 @@ Module Name:
cgroup.cpp
Abstract:
- Read memory limits for the current process
+ Read memory and cpu limits for the current process
--*/
#include <cstdint>
#include <cstddef>
@@ -26,42 +26,24 @@ Abstract:
#define PROC_CGROUP_FILENAME "/proc/self/cgroup"
#define PROC_STATM_FILENAME "/proc/self/statm"
#define MEM_LIMIT_FILENAME "/memory.limit_in_bytes"
+#define CFS_QUOTA_FILENAME "/cpu.cfs_quota_us"
+#define CFS_PERIOD_FILENAME "/cpu.cfs_period_us"
class CGroup
{
char* m_memory_cgroup_path;
+ char* m_cpu_cgroup_path;
public:
CGroup()
{
- m_memory_cgroup_path = nullptr;
- char* memoryHierarchyMount = nullptr;
- char *cgroup_path_relative_to_mount = nullptr;
- size_t len;
- memoryHierarchyMount = FindMemoryHierarchyMount();
- if (memoryHierarchyMount == nullptr)
- goto done;
-
- cgroup_path_relative_to_mount = FindCGroupPathForMemorySubsystem();
- if (cgroup_path_relative_to_mount == nullptr)
- goto done;
-
- len = strlen(memoryHierarchyMount);
- len += strlen(cgroup_path_relative_to_mount);
- m_memory_cgroup_path = (char*)malloc(len+1);
- if (m_memory_cgroup_path == nullptr)
- goto done;
-
- strcpy(m_memory_cgroup_path, memoryHierarchyMount);
- strcat(m_memory_cgroup_path, cgroup_path_relative_to_mount);
-
- done:
- free(memoryHierarchyMount);
- free(cgroup_path_relative_to_mount);
+ m_memory_cgroup_path = FindCgroupPath(&IsMemorySubsystem);
+ m_cpu_cgroup_path = FindCgroupPath(&IsCpuSubsystem);
}
~CGroup()
{
free(m_memory_cgroup_path);
+ free(m_cpu_cgroup_path);
}
bool GetPhysicalMemoryLimit(size_t *val)
@@ -84,15 +66,89 @@ public:
free(mem_limit_filename);
return result;
}
+
+ bool GetCpuLimit(uint32_t *val)
+ {
+ long long quota;
+ long long period;
+ long long cpu_count;
+
+ quota = ReadCpuCGroupValue(CFS_QUOTA_FILENAME);
+ if (quota <= 0)
+ return false;
+
+ period = ReadCpuCGroupValue(CFS_PERIOD_FILENAME);
+ if (period <= 0)
+ return false;
+
+ // Cannot have less than 1 CPU
+ if (quota <= period)
+ {
+ *val = 1;
+ return true;
+ }
+
+ cpu_count = quota / period;
+ if (cpu_count < UINT32_MAX)
+ {
+ *val = cpu_count;
+ }
+ else
+ {
+ *val = UINT32_MAX;
+ }
+
+ return true;
+ }
private:
- char* FindMemoryHierarchyMount()
+ static bool IsMemorySubsystem(const char *strTok){
+ return strcmp("memory", strTok) == 0;
+ }
+
+ static bool IsCpuSubsystem(const char *strTok){
+ return strcmp("cpu", strTok) == 0;
+ }
+
+ static char* FindCgroupPath(bool (*is_subsystem)(const char *)){
+ char *cgroup_path = nullptr;
+ char *hierarchy_mount = nullptr;
+ char *hierarchy_root = nullptr;
+ char *cgroup_path_relative_to_mount = nullptr;
+
+ FindHierarchyMount(is_subsystem, &hierarchy_mount, &hierarchy_root);
+ if (hierarchy_mount == nullptr || hierarchy_root == nullptr)
+ goto done;
+
+ cgroup_path_relative_to_mount = FindCGroupPathForSubsystem(is_subsystem);
+ if (cgroup_path_relative_to_mount == nullptr)
+ goto done;
+
+ cgroup_path = (char*)malloc(strlen(hierarchy_mount) + strlen(cgroup_path_relative_to_mount) + 1);
+ if (cgroup_path == nullptr)
+ goto done;
+
+ strcpy(cgroup_path, hierarchy_mount);
+ // For a host cgroup, we need to append the relative path.
+ // In a docker container, the root and relative path are the same and we don't need to append.
+ if (strcmp(hierarchy_root, cgroup_path_relative_to_mount) != 0)
+ strcat(cgroup_path, cgroup_path_relative_to_mount);
+
+ done:
+ free(hierarchy_mount);
+ free(hierarchy_root);
+ free(cgroup_path_relative_to_mount);
+ return cgroup_path;
+ }
+
+ static void FindHierarchyMount(bool (*is_subsystem)(const char *), char** pmountpath, char** pmountroot)
{
char *line = nullptr;
size_t lineLen = 0, maxLineLen = 0;
char *filesystemType = nullptr;
char *options = nullptr;
- char* mountpath = nullptr;
+ char *mountpath = nullptr;
+ char *mountroot = nullptr;
FILE *mountinfofile = fopen(PROC_MOUNTINFO_FILENAME, "r");
if (mountinfofile == nullptr)
@@ -113,11 +169,11 @@ private:
maxLineLen = lineLen;
}
- char* separatorChar = strchr(line, '-');
+ char* separatorChar = strstr(line, " - ");
// See man page of proc to get format for /proc/self/mountinfo file
int sscanfRet = sscanf(separatorChar,
- "- %s %*s %s",
+ " - %s %*s %s",
filesystemType,
options);
if (sscanfRet != 2)
@@ -132,21 +188,26 @@ private:
char* strTok = strtok_r(options, ",", &context);
while (strTok != nullptr)
{
- if (strncmp("memory", strTok, 6) == 0)
+ if (is_subsystem(strTok))
{
mountpath = (char*)malloc(lineLen+1);
if (mountpath == nullptr)
goto done;
+ mountroot = (char*)malloc(lineLen+1);
+ if (mountroot == nullptr)
+ goto done;
sscanfRet = sscanf(line,
- "%*s %*s %*s %*s %s ",
+ "%*s %*s %*s %s %s ",
+ mountroot,
mountpath);
- if (sscanfRet != 1)
- {
- free(mountpath);
- mountpath = nullptr;
+ if (sscanfRet != 2)
assert(!"Failed to parse mount info file contents with sscanf.");
- }
+
+ // assign the output arguments and clear the locals so we don't free them.
+ *pmountpath = mountpath;
+ *pmountroot = mountroot;
+ mountpath = mountroot = nullptr;
goto done;
}
strTok = strtok_r(nullptr, ",", &context);
@@ -154,15 +215,16 @@ private:
}
}
done:
+ free(mountpath);
+ free(mountroot);
free(filesystemType);
free(options);
free(line);
if (mountinfofile)
fclose(mountinfofile);
- return mountpath;
}
- char* FindCGroupPathForMemorySubsystem()
+ static char* FindCGroupPathForSubsystem(bool (*is_subsystem)(const char *))
{
char *line = nullptr;
size_t lineLen = 0;
@@ -205,7 +267,7 @@ private:
char* strTok = strtok_r(subsystem_list, ",", &context);
while (strTok != nullptr)
{
- if (strncmp("memory", strTok, 6) == 0)
+ if (is_subsystem(strTok))
{
result = true;
break;
@@ -271,6 +333,59 @@ private:
free(line);
return result;
}
+
+ long long ReadCpuCGroupValue(const char* subsystemFilename){
+ char *filename = nullptr;
+ bool result = false;
+ long long val;
+
+ if (m_cpu_cgroup_path == nullptr)
+ return -1;
+
+ filename = (char*)malloc(strlen(m_cpu_cgroup_path) + strlen(subsystemFilename) + 1);
+ if (filename == nullptr)
+ return -1;
+
+ strcpy(filename, m_cpu_cgroup_path);
+ strcat(filename, subsystemFilename);
+ result = ReadLongLongValueFromFile(filename, &val);
+ free(filename);
+ if (!result)
+ return -1;
+
+ return val;
+ }
+
+ bool ReadLongLongValueFromFile(const char* filename, long long* val)
+ {
+ bool result = false;
+ char *line = nullptr;
+ size_t lineLen = 0;
+
+ FILE* file = nullptr;
+
+ if (val == nullptr)
+ goto done;
+
+ file = fopen(filename, "r");
+ if (file == nullptr)
+ goto done;
+
+ if (getline(&line, &lineLen, file) == -1)
+ goto done;
+
+ errno = 0;
+ *val = atoll(line);
+ if (errno != 0)
+ goto done;
+
+ result = true;
+ done:
+ if (file)
+ fclose(file);
+ free(line);
+ return result;
+ }
};
size_t GetRestrictedPhysicalMemoryLimit()
@@ -340,3 +455,13 @@ bool GetWorkingSetSize(size_t* val)
free(line);
return result;
}
+
+bool GetCpuLimit(uint32_t* val)
+{
+ CGroup cgroup;
+
+ if (val == nullptr)
+ return false;
+
+ return cgroup.GetCpuLimit(val);
+}
diff --git a/src/gc/unix/configure.cmake b/src/gc/unix/configure.cmake
index 74ae70b1a4..b118232b35 100644
--- a/src/gc/unix/configure.cmake
+++ b/src/gc/unix/configure.cmake
@@ -54,4 +54,4 @@ check_cxx_source_runs("
check_library_exists(c sched_getaffinity "" HAVE_SCHED_GETAFFINITY)
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+configure_file(${CMAKE_CURRENT_LIST_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
diff --git a/src/gc/unix/events.cpp b/src/gc/unix/events.cpp
index 7c665f4aaa..694b9ba17c 100644
--- a/src/gc/unix/events.cpp
+++ b/src/gc/unix/events.cpp
@@ -11,10 +11,6 @@
#include <errno.h>
#include "config.h"
-#ifndef __out_z
-#define __out_z
-#endif // __out_z
-
#include "gcenv.structs.h"
#include "gcenv.base.h"
#include "gcenv.os.h"
diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp
index 5693fb4ff5..f564b28239 100644
--- a/src/gc/unix/gcenv.unix.cpp
+++ b/src/gc/unix/gcenv.unix.cpp
@@ -6,32 +6,15 @@
#include <cstddef>
#include <cassert>
#include <memory>
-
-// The CoreCLR PAL defines _POSIX_C_SOURCE to avoid calling non-posix pthread functions.
-// This isn't something we want, because we're totally fine using non-posix functions.
-#if defined(__APPLE__)
- #define _DARWIN_C_SOURCE
-#endif // definfed(__APPLE__)
-
#include <pthread.h>
#include <signal.h>
#include "config.h"
-// clang typedefs uint64_t to be unsigned long long, which clashes with
-// PAL/MSVC's unsigned long, causing linker errors. This ugly hack
-// will go away once the GC doesn't depend on PAL headers.
-typedef unsigned long uint64_t_hack;
-#define uint64_t uint64_t_hack
-static_assert(sizeof(uint64_t) == 8, "unsigned long isn't 8 bytes");
-
-#ifndef __out_z
-#define __out_z
-#endif // __out_z
-
#include "gcenv.structs.h"
#include "gcenv.base.h"
#include "gcenv.os.h"
#include "gcenv.unix.inl"
+#include "volatile.h"
#if HAVE_SYS_TIME_H
#include <sys/time.h>
@@ -66,6 +49,7 @@ static pthread_mutex_t g_flushProcessWriteBuffersMutex;
size_t GetRestrictedPhysicalMemoryLimit();
bool GetWorkingSetSize(size_t* val);
+bool GetCpuLimit(uint32_t* val);
static size_t g_RestrictedPhysicalMemoryLimit = 0;
@@ -507,6 +491,7 @@ bool GCToOSInterface::GetCurrentProcessAffinityMask(uintptr_t* processAffinityMa
uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
{
uintptr_t pmask, smask;
+ uint32_t cpuLimit;
if (!GetCurrentProcessAffinityMask(&pmask, &smask))
return 1;
@@ -530,6 +515,9 @@ uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
if (count == 0 || count > 64)
count = 64;
+ if (GetCpuLimit(&cpuLimit) && cpuLimit < count)
+ count = cpuLimit;
+
return count;
}
diff --git a/src/gc/windows/gcenv.windows.cpp b/src/gc/windows/gcenv.windows.cpp
index c47f3702e2..e05233fe7c 100644
--- a/src/gc/windows/gcenv.windows.cpp
+++ b/src/gc/windows/gcenv.windows.cpp
@@ -12,6 +12,7 @@
#include "env/gcenv.base.h"
#include "env/gcenv.os.h"
#include "env/gcenv.windows.inl"
+#include "env/volatile.h"
GCSystemInfo g_SystemInfo;
diff --git a/src/ildasm/ceeload.cpp b/src/ildasm/ceeload.cpp
index a78ff72c44..ac60b9d2a8 100644
--- a/src/ildasm/ceeload.cpp
+++ b/src/ildasm/ceeload.cpp
@@ -143,7 +143,7 @@ BOOL PELoader::open(HMODULE hMod)
if ((m_pNT32->Signature != VAL32(IMAGE_NT_SIGNATURE)) ||
(m_pNT32->FileHeader.SizeOfOptionalHeader != VAL16(sizeof(IMAGE_OPTIONAL_HEADER32))))
{
- // Make this appear uninitalized because for some reason this file is toasted.
+ // Make this appear uninitialized because for some reason this file is toasted.
m_pNT32 = NULL;
m_hMod = NULL;
return FALSE;
@@ -155,7 +155,7 @@ BOOL PELoader::open(HMODULE hMod)
if ((m_pNT64->Signature != VAL32(IMAGE_NT_SIGNATURE)) ||
(m_pNT64->FileHeader.SizeOfOptionalHeader != VAL16(sizeof(IMAGE_OPTIONAL_HEADER64))))
{
- // Make this appear uninitalized because for some reason this file is toasted.
+ // Make this appear uninitialized because for some reason this file is toasted.
m_pNT64 = NULL;
m_hMod = NULL;
return FALSE;
@@ -166,7 +166,7 @@ BOOL PELoader::open(HMODULE hMod)
}
else
{
- // Make this appear uninitalized because for some reason this file is toasted.
+ // Make this appear uninitialized because for some reason this file is toasted.
m_hMod = NULL;
return FALSE;
}
diff --git a/src/inc/CMakeLists.txt b/src/inc/CMakeLists.txt
index 40499b44ea..4c82f157bc 100644
--- a/src/inc/CMakeLists.txt
+++ b/src/inc/CMakeLists.txt
@@ -64,6 +64,10 @@ endforeach(IDL_SOURCE)
add_compile_options(-fPIC)
endif(WIN32)
+if(FEATURE_JIT_PITCHING)
+ add_definitions(-DFEATURE_JIT_PITCHING)
+endif(FEATURE_JIT_PITCHING)
+
# Compile *_i.cpp to lib
_add_library(corguids ${CORGUIDS_SOURCES})
diff --git a/src/inc/MSCOREE.IDL b/src/inc/MSCOREE.IDL
index 53d4ce3bb0..a6b60b9278 100644
--- a/src/inc/MSCOREE.IDL
+++ b/src/inc/MSCOREE.IDL
@@ -164,7 +164,7 @@ typedef enum {
STARTUP_ARM = 0x400000, // Enable the ARM feature.
STARTUP_SINGLE_APPDOMAIN = 0x800000, // application runs in default domain, no more domains are created
STARTUP_APPX_APP_MODEL = 0x1000000, // jupiter app
- STARTUP_DISABLE_RANDOMIZED_STRING_HASHING = 0x2000000 // Disable the randomized string hashing
+ STARTUP_DISABLE_RANDOMIZED_STRING_HASHING = 0x2000000 // Disable the randomized string hashing (not supported)
} STARTUP_FLAGS;
typedef enum {
diff --git a/src/inc/clrconfigvalues.h b/src/inc/clrconfigvalues.h
index 8a21a9d8fd..1e27c729f8 100644
--- a/src/inc/clrconfigvalues.h
+++ b/src/inc/clrconfigvalues.h
@@ -134,6 +134,17 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_FinalizeOnShutdown, W("FinalizeOnShutdown"), D
RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ARMEnabled, W("ARMEnabled"), (DWORD)0, "Set it to 1 to enable ARM")
//
+// Jit Pitching
+//
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitPitchEnabled, W("JitPitchEnabled"), (DWORD)0, "Set it to 1 to enable Jit Pitching")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitPitchMemThreshold, W("JitPitchMemThreshold"), (DWORD)0, "Do Jit Pitching when code heap usage is larger than this (in bytes)")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitPitchMethodSizeThreshold, W("JitPitchMethodSizeThreshold"), (DWORD)0, "Do Jit Pitching for methods whose native code size larger than this (in bytes)")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitPitchTimeInterval, W("JitPitchTimeInterval"), (DWORD)0, "Time interval between Jit Pitchings in ms")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitPitchPrintStat, W("JitPitchPrintStat"), (DWORD)0, "Print statistics about Jit Pitching")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitPitchMinVal, W("JitPitchMinVal"), (DWORD)0, "Do Jit Pitching if the value of the inner counter greater than this value (for debugging purpose only)")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitPitchMaxVal, W("JitPitchMaxVal"), (DWORD)0xffffffff, "Do Jit Pitching the value of the inner counter less then this value (for debuggin purpose only)")
+
+//
// Assembly Loader
//
RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_DesignerNamespaceResolutionEnabled, W("designerNamespaceResolution"), FALSE, "Set it to 1 to enable DesignerNamespaceResolve event for WinRT types", CLRConfig::IgnoreEnv | CLRConfig::IgnoreHKLM | CLRConfig::IgnoreHKCU | CLRConfig::FavorConfigFile)
@@ -153,9 +164,6 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_NetFx45_LegacyManagedDeflateStream, W("NetFx45
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_DateTime_NetFX35ParseMode, W("DateTime_NetFX35ParseMode"), 0, "Flag to enable the .NET 3.5 System.DateTime Token Replacement Policy")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ThrowUnobservedTaskExceptions, W("ThrowUnobservedTaskExceptions"), 0, "Flag to propagate unobserved task exceptions on the finalizer thread.")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_DateTime_NetFX40AmPmParseAdjustment, W("EnableAmPmParseAdjustment"), 0, "Flag to enable the .NET 4.0 DateTimeParse to correctly parse AM/PM cases")
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
-RETAIL_CONFIG_DWORD_INFO(EXTERNAL_UseRandomizedStringHashAlgorithm, W("UseRandomizedStringHashAlgorithm"), 0, "Flag to use a string hashing algorithm who's behavior differs between AppDomains")
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
//
// Conditional breakpoints
@@ -836,6 +844,7 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ProfAPI_ValidateNGENInstrumentation, W("Pro
#ifdef FEATURE_PERFMAP
RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_PerfMapEnabled, W("PerfMapEnabled"), 0, "This flag is used on Linux to enable writing /tmp/perf-$pid.map. It is disabled by default", CLRConfig::REGUTIL_default)
+RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_PerfMapIgnoreSignal, W("PerfMapIgnoreSignal"), 0, "When perf map is enabled, this option will configure the specified signal to be accepeted and ignored as a marker in the perf logs. It is disabled by default", CLRConfig::REGUTIL_default)
#endif
//
@@ -1042,6 +1051,16 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_AllowDComReflection, W("AllowDComReflection"),
//
RETAIL_CONFIG_DWORD_INFO(INTERNAL_PerformanceTracing, W("PerformanceTracing"), 0, "Enable/disable performance tracing. Non-zero values enable tracing.")
+#ifdef FEATURE_GDBJIT
+//
+// GDBJIT
+//
+CONFIG_STRING_INFO(INTERNAL_GDBJitElfDump, W("GDBJitElfDump"), "Dump ELF for specified method")
+#ifdef FEATURE_GDBJIT_FRAME
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_GDBJitEmitDebugFrame, W("GDBJitEmitDebugFrame"), TRUE, "Enable .debug_frame generation")
+#endif
+#endif
+
//
// Unknown
//
diff --git a/src/inc/corcompile.h b/src/inc/corcompile.h
index 31bf213d41..3403e2254b 100644
--- a/src/inc/corcompile.h
+++ b/src/inc/corcompile.h
@@ -1331,6 +1331,7 @@ class ICorCompilePreloader
CORCOMPILE_SECTION(READONLY_HOT) \
CORCOMPILE_SECTION(READONLY_WARM) \
CORCOMPILE_SECTION(READONLY_COLD) \
+ CORCOMPILE_SECTION(READONLY_VCHUNKS_AND_DICTIONARY) \
CORCOMPILE_SECTION(CLASS_COLD) \
CORCOMPILE_SECTION(CROSS_DOMAIN_INFO) \
CORCOMPILE_SECTION(METHOD_PRECODE_COLD) \
diff --git a/src/inc/corhlpr.h b/src/inc/corhlpr.h
index 02555c9ec3..5b263a5382 100644
--- a/src/inc/corhlpr.h
+++ b/src/inc/corhlpr.h
@@ -633,6 +633,10 @@ extern "C" {
class COR_ILMETHOD_DECODER : public COR_ILMETHOD_FAT
{
public:
+ // This returns an uninitialized decoder, suitable for placement new but nothing
+ // else. Use with caution.
+ COR_ILMETHOD_DECODER() {}
+
// Typically the ONLY way you should access COR_ILMETHOD is through
// this constructor so format changes are easier.
COR_ILMETHOD_DECODER(const COR_ILMETHOD* header)
diff --git a/src/inc/corinfo.h b/src/inc/corinfo.h
index 4af22e53f4..3171d6b00d 100644
--- a/src/inc/corinfo.h
+++ b/src/inc/corinfo.h
@@ -213,11 +213,11 @@ TODO: Talk about initializing strutures before use
#define SELECTANY extern __declspec(selectany)
#endif
-SELECTANY const GUID JITEEVersionIdentifier = { /* 28eb875f-b6a9-4a04-9ba7-69ba59deed46 */
- 0x28eb875f,
- 0xb6a9,
- 0x4a04,
- { 0x9b, 0xa7, 0x69, 0xba, 0x59, 0xde, 0xed, 0x46 }
+SELECTANY const GUID JITEEVersionIdentifier = { /* 7f70c266-eada-427b-be8a-be1260e34b1b */
+ 0x7f70c266,
+ 0xeada,
+ 0x427b,
+ {0xbe, 0x8a, 0xbe, 0x12, 0x60, 0xe3, 0x4b, 0x1b}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -827,7 +827,7 @@ enum CorInfoFlag
CORINFO_FLG_NOSECURITYWRAP = 0x04000000, // The method requires no security checks
CORINFO_FLG_DONT_INLINE = 0x10000000, // The method should not be inlined
CORINFO_FLG_DONT_INLINE_CALLER = 0x20000000, // The method should not be inlined, nor should its callers. It cannot be tail called.
-// CORINFO_FLG_UNUSED = 0x40000000,
+ CORINFO_FLG_JIT_INTRINSIC = 0x40000000, // Method is a potential jit intrinsic; verify identity by name check
// These are internal flags that can only be on Classes
CORINFO_FLG_VALUECLASS = 0x00010000, // is the class a value class
@@ -1332,6 +1332,13 @@ struct CORINFO_RUNTIME_LOOKUP
// 1 means that value stored at first offset (offsets[0]) from pointer is offset1, and the next pointer is
// stored at pointer+offsets[0]+offset1.
bool indirectFirstOffset;
+
+ // If set, second offset is indirect.
+ // 0 means that value stored at second offset (offsets[1]) from pointer is next pointer, to which the next offset
+ // (offsets[2]) is added and so on.
+ // 1 means that value stored at second offset (offsets[1]) from pointer is offset2, and the next pointer is
+ // stored at pointer+offsets[1]+offset2.
+ bool indirectSecondOffset;
} ;
// Result of calling embedGenericHandle
@@ -2062,7 +2069,8 @@ public:
virtual void getMethodVTableOffset (
CORINFO_METHOD_HANDLE method, /* IN */
unsigned* offsetOfIndirection, /* OUT */
- unsigned* offsetAfterIndirection /* OUT */
+ unsigned* offsetAfterIndirection, /* OUT */
+ bool* isRelative /* OUT */
) = 0;
// Find the virtual method in implementingClass that overrides virtualMethod,
@@ -2252,7 +2260,6 @@ public:
CORINFO_CLASS_HANDLE cls
) = 0;
-
// Append a (possibly truncated) representation of the type cls to the preallocated buffer ppBuf of length pnBufLen
// If fNamespace=TRUE, include the namespace/enclosing classes
// If fFullInst=TRUE (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters
@@ -2783,6 +2790,15 @@ public:
const char **moduleName /* OUT */
) = 0;
+ // Return method name as in metadata, or nullptr if there is none,
+ // and optionally return the class and namespace names as in metadata.
+ // Suitable for non-debugging use.
+ virtual const char* getMethodNameFromMetadata(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **className, /* OUT */
+ const char **namespaceName /* OUT */
+ ) = 0;
+
// this function is for debugging only. It returns a value that
// is will always be the same for a given method. It is used
// to implement the 'jitRange' functionality
diff --git a/src/inc/corjit.h b/src/inc/corjit.h
index e6e8257afe..39eafe2a89 100644
--- a/src/inc/corjit.h
+++ b/src/inc/corjit.h
@@ -152,8 +152,10 @@ public:
#if defined(_TARGET_ARM_)
CORJIT_FLAG_RELATIVE_CODE_RELOCS = 41, // JIT should generate PC-relative address computations instead of EE relocation records
#else // !defined(_TARGET_ARM_)
- CORJIT_FLAG_UNUSED11 = 41
+ CORJIT_FLAG_UNUSED11 = 41,
#endif // !defined(_TARGET_ARM_)
+
+ CORJIT_FLAG_NO_INLINING = 42 // JIT should not inline any called method into this method
};
CORJIT_FLAGS()
diff --git a/src/inc/dacprivate.h b/src/inc/dacprivate.h
index a419c47fef..2f7482680d 100644
--- a/src/inc/dacprivate.h
+++ b/src/inc/dacprivate.h
@@ -507,7 +507,8 @@ struct MSLAYOUT DacpReJitData : ZeroInit<DacpReJitData>
Flags flags;
CLRDATA_ADDRESS NativeCodeAddr;
};
-
+
+
struct MSLAYOUT DacpMethodDescData : ZeroInit<DacpMethodDescData>
{
BOOL bHasNativeCode;
@@ -552,6 +553,7 @@ struct MSLAYOUT DacpMethodDescData : ZeroInit<DacpMethodDescData>
}
};
+
struct MSLAYOUT DacpMethodDescTransparencyData : ZeroInit<DacpMethodDescTransparencyData>
{
BOOL bHasCriticalTransparentInfo;
@@ -564,6 +566,21 @@ struct MSLAYOUT DacpMethodDescTransparencyData : ZeroInit<DacpMethodDescTranspar
}
};
+struct MSLAYOUT DacpTieredVersionData
+{
+ enum TieredState
+ {
+ NON_TIERED,
+ TIERED_0,
+ TIERED_1,
+ TIERED_UNKNOWN
+ };
+
+ CLRDATA_ADDRESS NativeCodeAddr;
+ TieredState TieredInfo;
+ CLRDATA_ADDRESS NativeCodeVersionNodePtr;
+};
+
// for JITType
enum JITTypes {TYPE_UNKNOWN=0,TYPE_JIT,TYPE_PJIT};
diff --git a/src/inc/dacvars.h b/src/inc/dacvars.h
index 9fe2382d86..c5eb2cf996 100644
--- a/src/inc/dacvars.h
+++ b/src/inc/dacvars.h
@@ -206,7 +206,6 @@ DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pICastableInterface, ::g_pICas
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pExecuteBackoutCodeHelperMethod, ::g_pExecuteBackoutCodeHelperMethod)
-DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pObjectCtorMD, ::g_pObjectCtorMD)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pObjectFinalizerMD, ::g_pObjectFinalizerMD)
DEFINE_DACVAR(ULONG, bool, dac__g_fProcessDetach, ::g_fProcessDetach)
@@ -242,7 +241,6 @@ DEFINE_DACVAR(ULONG, SIZE_T, dac__g_runtimeVirtualSize, ::g_runtimeVirtualSize)
DEFINE_DACVAR(ULONG, SyncBlockCache *, SyncBlockCache__s_pSyncBlockCache, SyncBlockCache::s_pSyncBlockCache)
-DEFINE_DACVAR(ULONG, HandleTableMap, dac__g_HandleTableMap, ::g_HandleTableMap)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pStressLog, ::g_pStressLog)
DEFINE_DACVAR(ULONG, SIZE_T, dac__s_gsCookie, ::s_gsCookie)
diff --git a/src/inc/fixuppointer.h b/src/inc/fixuppointer.h
index 83ff20e04f..a711418bd4 100644
--- a/src/inc/fixuppointer.h
+++ b/src/inc/fixuppointer.h
@@ -141,6 +141,12 @@ public:
LIMITED_METHOD_CONTRACT;
SetValueMaybeNull((TADDR)this, addr);
}
+
+ FORCEINLINE void SetValueVolatile(PTR_TYPE addr)
+ {
+ LIMITED_METHOD_CONTRACT;
+ SetValue(addr);
+ }
#endif
#ifndef DACCESS_COMPILE
@@ -208,15 +214,38 @@ public:
return dac_cast<PTR_TYPE>(addr);
}
+ // Returns value of the encoded pointer.
+ FORCEINLINE PTR_TYPE GetValueMaybeNull() const
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return GetValue();
+ }
+
+#ifndef DACCESS_COMPILE
// Returns the pointer to the indirection cell.
PTR_TYPE * GetValuePtr() const
{
- LIMITED_METHOD_DAC_CONTRACT;
+ LIMITED_METHOD_CONTRACT;
TADDR addr = m_addr;
if ((addr & FIXUP_POINTER_INDIRECTION) != 0)
- return dac_cast<DPTR(PTR_TYPE)>(addr - FIXUP_POINTER_INDIRECTION);
+ return (PTR_TYPE *)(addr - FIXUP_POINTER_INDIRECTION);
return (PTR_TYPE *)&m_addr;
}
+#endif // !DACCESS_COMPILE
+
+ // Static version of GetValue. It is meant to simplify access to arrays of pointers.
+ FORCEINLINE static PTR_TYPE GetValueAtPtr(TADDR base)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return dac_cast<DPTR(FixupPointer<PTR_TYPE>)>(base)->GetValue();
+ }
+
+ // Static version of GetValueMaybeNull. It is meant to simplify access to arrays of pointers.
+ FORCEINLINE static PTR_TYPE GetValueMaybeNullAtPtr(TADDR base)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return dac_cast<DPTR(FixupPointer<PTR_TYPE>)>(base)->GetValueMaybeNull();
+ }
// Returns value of the encoded pointer.
// Allows the value to be tagged.
@@ -229,12 +258,20 @@ public:
return addr;
}
+#ifndef DACCESS_COMPILE
void SetValue(PTR_TYPE addr)
{
LIMITED_METHOD_CONTRACT;
m_addr = dac_cast<TADDR>(addr);
}
+ void SetValueMaybeNull(PTR_TYPE addr)
+ {
+ LIMITED_METHOD_CONTRACT;
+ SetValue(addr);
+ }
+#endif // !DACCESS_COMPILE
+
private:
TADDR m_addr;
};
@@ -283,11 +320,12 @@ public:
}
#ifndef DACCESS_COMPILE
+ // Returns whether the indirection cell contain fixup that has not been converted to real pointer yet.
+ // Does not need explicit base and thus can be used in non-DAC builds only.
FORCEINLINE BOOL IsTagged() const
{
LIMITED_METHOD_CONTRACT;
- TADDR base = (TADDR) this;
- return IsTagged(base);
+ return IsTagged((TADDR)this);
}
#endif // !DACCESS_COMPILE
@@ -382,21 +420,14 @@ public:
}
#endif
- // Returns the pointer to the indirection cell.
- PTR_TYPE * GetValuePtr(TADDR base) const
- {
- LIMITED_METHOD_CONTRACT;
- TADDR addr = base + m_delta;
- _ASSERTE((addr & FIXUP_POINTER_INDIRECTION) != 0);
- return dac_cast<DPTR(PTR_TYPE)>(addr - FIXUP_POINTER_INDIRECTION);
- }
-
#ifndef DACCESS_COMPILE
+ // Returns the pointer to the indirection cell.
PTR_TYPE * GetValuePtr() const
{
LIMITED_METHOD_CONTRACT;
- TADDR base = (TADDR) this;
- return GetValuePtr(base);
+ TADDR addr = ((TADDR)this) + m_delta;
+ _ASSERTE((addr & FIXUP_POINTER_INDIRECTION) != 0);
+ return (PTR_TYPE *)(addr - FIXUP_POINTER_INDIRECTION);
}
#endif // !DACCESS_COMPILE
@@ -412,6 +443,48 @@ public:
return addr;
}
+ // Returns whether pointer is indirect. Assumes that the value is not NULL.
+ bool IsIndirectPtr(TADDR base) const
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ PRECONDITION(!IsNull());
+
+ TADDR addr = base + m_delta;
+
+ return (addr & FIXUP_POINTER_INDIRECTION) != 0;
+ }
+
+#ifndef DACCESS_COMPILE
+ // Returns whether pointer is indirect. Assumes that the value is not NULL.
+ // Does not need explicit base and thus can be used in non-DAC builds only.
+ bool IsIndirectPtr() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return IsIndirectPtr((TADDR)this);
+ }
+#endif
+
+ // Returns whether pointer is indirect. The value can be NULL.
+ bool IsIndirectPtrMaybeNull(TADDR base) const
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ if (m_delta == 0)
+ return false;
+
+ return IsIndirectPtr(base);
+ }
+
+#ifndef DACCESS_COMPILE
+ // Returns whether pointer is indirect. The value can be NULL.
+ // Does not need explicit base and thus can be used in non-DAC builds only.
+ bool IsIndirectPtrMaybeNull() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return IsIndirectPtrMaybeNull((TADDR)this);
+ }
+#endif
+
private:
#ifndef DACCESS_COMPILE
Volatile<TADDR> m_delta;
@@ -444,10 +517,20 @@ public:
}
// Returns whether the indirection cell contain fixup that has not been converted to real pointer yet.
+ BOOL IsTagged(TADDR base) const
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return IsTagged();
+ }
+
+ // Returns whether the indirection cell contain fixup that has not been converted to real pointer yet.
BOOL IsTagged() const
{
LIMITED_METHOD_DAC_CONTRACT;
- return m_ptr & 1;
+ TADDR addr = m_ptr;
+ if ((addr & FIXUP_POINTER_INDIRECTION) != 0)
+ return (*PTR_TADDR(addr - FIXUP_POINTER_INDIRECTION) & 1) != 0;
+ return FALSE;
}
// Returns value of the encoded pointer.
@@ -457,18 +540,22 @@ public:
return dac_cast<PTR_TYPE>(m_ptr);
}
+#ifndef DACCESS_COMPILE
// Returns the pointer to the indirection cell.
PTR_TYPE * GetValuePtr() const
{
- LIMITED_METHOD_DAC_CONTRACT;
+ LIMITED_METHOD_CONTRACT;
+ TADDR addr = m_ptr;
+ if ((addr & FIXUP_POINTER_INDIRECTION) != 0)
+ return (PTR_TYPE *)(addr - FIXUP_POINTER_INDIRECTION);
return (PTR_TYPE *)&m_ptr;
}
+#endif // !DACCESS_COMPILE
// Returns value of the encoded pointer. Assumes that the pointer is not NULL.
PTR_TYPE GetValue(TADDR base) const
{
LIMITED_METHOD_DAC_CONTRACT;
- PRECONDITION(!IsNull());
return dac_cast<PTR_TYPE>(m_ptr);
}
@@ -480,6 +567,13 @@ public:
}
// Returns value of the encoded pointer. The pointer can be NULL.
+ PTR_TYPE GetValueMaybeNull() const
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return dac_cast<PTR_TYPE>(m_ptr);
+ }
+
+ // Returns value of the encoded pointer. The pointer can be NULL.
PTR_TYPE GetValueMaybeNull(TADDR base) const
{
LIMITED_METHOD_DAC_CONTRACT;
@@ -493,6 +587,43 @@ public:
return dac_cast<DPTR(PlainPointer<PTR_TYPE>)>(base)->GetValueMaybeNull(base);
}
+ // Returns whether pointer is indirect. Assumes that the value is not NULL.
+ bool IsIndirectPtr(TADDR base) const
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (m_ptr & FIXUP_POINTER_INDIRECTION) != 0;
+ }
+
+#ifndef DACCESS_COMPILE
+ // Returns whether pointer is indirect. Assumes that the value is not NULL.
+ // Does not need explicit base and thus can be used in non-DAC builds only.
+ bool IsIndirectPtr() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return IsIndirectPtr((TADDR)this);
+ }
+#endif
+
+ // Returns whether pointer is indirect. The value can be NULL.
+ bool IsIndirectPtrMaybeNull(TADDR base) const
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return IsIndirectPtr(base);
+ }
+
+#ifndef DACCESS_COMPILE
+ // Returns whether pointer is indirect. The value can be NULL.
+ // Does not need explicit base and thus can be used in non-DAC builds only.
+ bool IsIndirectPtrMaybeNull() const
+ {
+ LIMITED_METHOD_CONTRACT;
+ return IsIndirectPtrMaybeNull((TADDR)this);
+ }
+#endif
+
+#ifndef DACCESS_COMPILE
void SetValue(PTR_TYPE addr)
{
LIMITED_METHOD_CONTRACT;
@@ -503,7 +634,6 @@ public:
void SetValue(TADDR base, PTR_TYPE addr)
{
LIMITED_METHOD_CONTRACT;
- PRECONDITION(addr != NULL);
m_ptr = dac_cast<TADDR>(addr);
}
@@ -521,7 +651,6 @@ public:
m_ptr = dac_cast<TADDR>(addr);
}
-#ifndef DACCESS_COMPILE
// Set encoded value of the pointer. The value can be NULL.
// Does not need explicit base and thus can be used in non-DAC builds only.
FORCEINLINE void SetValueMaybeNull(PTR_TYPE addr)
@@ -529,7 +658,6 @@ public:
LIMITED_METHOD_CONTRACT;
return SetValueMaybeNull((TADDR)this, addr);
}
-#endif
// Static version of SetValueMaybeNull. It is meant to simplify access to arrays of pointers.
FORCEINLINE static void SetValueMaybeNullAtPtr(TADDR base, PTR_TYPE addr)
@@ -538,6 +666,13 @@ public:
dac_cast<DPTR(PlainPointer<PTR_TYPE>)>(base)->SetValueMaybeNull(base, addr);
}
+ FORCEINLINE void SetValueVolatile(PTR_TYPE addr)
+ {
+ LIMITED_METHOD_CONTRACT;
+ VolatileStore((PTR_TYPE *)(&m_ptr), addr);
+ }
+#endif
+
private:
TADDR m_ptr;
};
diff --git a/src/inc/loaderheap.h b/src/inc/loaderheap.h
index 7d4c48f5e8..4333505e83 100644
--- a/src/inc/loaderheap.h
+++ b/src/inc/loaderheap.h
@@ -217,7 +217,7 @@ private:
size_t * m_pPrivatePerfCounter_LoaderBytes;
- DWORD m_flProtect;
+ DWORD m_Options;
LoaderHeapFreeBlock *m_pFirstFreeBlock;
@@ -288,7 +288,8 @@ protected:
SIZE_T dwReservedRegionSize,
size_t *pPrivatePerfCounter_LoaderBytes = NULL,
RangeList *pRangeList = NULL,
- BOOL fMakeExecutable = FALSE);
+ BOOL fMakeExecutable = FALSE,
+ BOOL fZeroInit = TRUE);
~UnlockedLoaderHeap();
#endif
@@ -398,10 +399,8 @@ public:
return m_dwTotalAlloc;
}
- BOOL IsExecutable()
- {
- return (PAGE_EXECUTE_READWRITE == m_flProtect);
- }
+ BOOL IsExecutable();
+ BOOL IsZeroInit();
public:
@@ -447,14 +446,16 @@ public:
DWORD dwCommitBlockSize,
size_t *pPrivatePerfCounter_LoaderBytes = NULL,
RangeList *pRangeList = NULL,
- BOOL fMakeExecutable = FALSE
+ BOOL fMakeExecutable = FALSE,
+ BOOL fZeroInit = TRUE
)
: UnlockedLoaderHeap(dwReserveBlockSize,
dwCommitBlockSize,
NULL, 0,
pPrivatePerfCounter_LoaderBytes,
pRangeList,
- fMakeExecutable)
+ fMakeExecutable,
+ fZeroInit)
{
WRAPPER_NO_CONTRACT;
m_CriticalSection = NULL;
@@ -469,7 +470,8 @@ public:
SIZE_T dwReservedRegionSize,
size_t *pPrivatePerfCounter_LoaderBytes = NULL,
RangeList *pRangeList = NULL,
- BOOL fMakeExecutable = FALSE
+ BOOL fMakeExecutable = FALSE,
+ BOOL fZeroInit = TRUE
)
: UnlockedLoaderHeap(dwReserveBlockSize,
dwCommitBlockSize,
@@ -477,7 +479,8 @@ public:
dwReservedRegionSize,
pPrivatePerfCounter_LoaderBytes,
pRangeList,
- fMakeExecutable)
+ fMakeExecutable,
+ fZeroInit)
{
WRAPPER_NO_CONTRACT;
m_CriticalSection = NULL;
diff --git a/src/inc/shash.h b/src/inc/shash.h
index cece2dd345..1650ca15b4 100644
--- a/src/inc/shash.h
+++ b/src/inc/shash.h
@@ -327,6 +327,7 @@ class SHash : public TRAITS
count_t m_tableSize;
count_t m_index;
+
Index(const SHash *hash, BOOL begin)
: m_table(hash->m_table),
m_tableSize(hash->m_tableSize),
diff --git a/src/inc/sospriv.idl b/src/inc/sospriv.idl
index 1f9028c8e1..5b718210d7 100644
--- a/src/inc/sospriv.idl
+++ b/src/inc/sospriv.idl
@@ -356,4 +356,14 @@ interface ISOSDacInterface3 : IUnknown
interface ISOSDacInterface4 : IUnknown
{
HRESULT GetClrNotification(CLRDATA_ADDRESS arguments[], int count, int *pNeeded);
-}; \ No newline at end of file
+};
+
+[
+ object,
+ local,
+ uuid(127d6abe-6c86-4e48-8e7b-220781c58101)
+]
+interface ISOSDacInterface5 : IUnknown
+{
+ HRESULT GetTieredVersions(CLRDATA_ADDRESS methodDesc, int rejitId, struct DacpTieredVersionData *nativeCodeAddrs, int cNativeCodeAddrs, int *pcNativeCodeAddrs);
+};
diff --git a/src/inc/utilcode.h b/src/inc/utilcode.h
index db8465ad89..f48af70f60 100644
--- a/src/inc/utilcode.h
+++ b/src/inc/utilcode.h
@@ -4687,6 +4687,8 @@ inline void ClrFlsClearThreadType (TlsThreadTypeFlag flag)
#define CLEAR_THREAD_TYPE_STACKWALKER() ClrFlsSetValue(TlsIdx_StackWalkerWalkingThread, NULL)
#endif // DACCESS_COMPILE
+HRESULT SetThreadName(HANDLE hThread, PCWSTR lpThreadDescription);
+
inline BOOL IsStackWalkerThread()
{
STATIC_CONTRACT_NOTHROW;
diff --git a/src/inc/volatile.h b/src/inc/volatile.h
index 5aa0e50866..ecf9ffe427 100644
--- a/src/inc/volatile.h
+++ b/src/inc/volatile.h
@@ -106,6 +106,19 @@
#define VOLATILE_MEMORY_BARRIER()
#endif // __GNUC__
+template<typename T>
+struct RemoveVolatile
+{
+ typedef T type;
+};
+
+template<typename T>
+struct RemoveVolatile<volatile T>
+{
+ typedef T type;
+};
+
+
//
// VolatileLoad loads a T from a pointer to T. It is guaranteed that this load will not be optimized
// away by the compiler, and that any operation that occurs after this load, in program order, will
@@ -113,6 +126,10 @@
// this is the case for most aligned scalar data types. If you need atomic loads or stores, you need
// to consult the compiler and CPU manuals to find which circumstances allow atomicity.
//
+// Starting at version 3.8, clang errors out on initializing of type int * to volatile int *. To fix this, we add two templates to cast away volatility
+// Helper structures for casting away volatileness
+
+
template<typename T>
inline
T VolatileLoad(T const * pt)
@@ -125,7 +142,7 @@ T VolatileLoad(T const * pt)
static const unsigned lockFreeAtomicSizeMask = (1 << 1) | (1 << 2) | (1 << 4) | (1 << 8);
if((1 << sizeof(T)) & lockFreeAtomicSizeMask)
{
- __atomic_load((T volatile const *)pt, &val, __ATOMIC_ACQUIRE);
+ __atomic_load((T const *)pt, const_cast<typename RemoveVolatile<T>::type *>(&val), __ATOMIC_ACQUIRE);
}
else
{
diff --git a/src/inc/winwrap.h b/src/inc/winwrap.h
index 299d163637..fbc565aa25 100644
--- a/src/inc/winwrap.h
+++ b/src/inc/winwrap.h
@@ -20,17 +20,6 @@
#if !defined(WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN
#endif
-#if !defined(WIN32_LEAN_AND_MEAN)
-#define INC_OLE2
-#endif
-
-#ifdef _WIN64
-#define HIWORD64(p) ((ULONG_PTR)(p) >> 16)
-#else
-#define HIWORD64 HIWORD
-#endif
-
-#define SAFEDELARRAY(p) if ((p) != NULL) { delete [] p; (p) = NULL; }
//
// WinCE uniformly uses cdecl calling convention on x86. __stdcall is defined as __cdecl in SDK.
@@ -67,7 +56,6 @@
// wincrypt.h
#undef CryptAcquireContext
-#undef CryptGetDefaultProvider
#undef CryptSignHash
#undef CryptVerifySignature
@@ -78,11 +66,6 @@
#undef GetEnvironmentStrings
#undef FreeEnvironmentStrings
#undef FormatMessage
-#undef CreateMailslot
-#undef EncryptFile
-#undef DecryptFile
-#undef OpenRaw
-#undef QueryRecoveryAgents
#undef lstrcmp
#undef lstrcmpi
#undef lstrcpyn
@@ -94,18 +77,14 @@
#undef CreateSemaphore
#undef OpenSemaphore
#undef CreateWaitableTimer
-#undef OpenWaitableTimer
#undef CreateFileMapping
#undef OpenFileMapping
-#undef GetLogicalDriveStrings
#undef LoadLibrary
#undef LoadLibraryEx
#undef GetModuleFileName
#undef GetModuleHandle
#undef GetModuleHandleEx
#undef CreateProcess
-#undef FatalAppExit
-#undef GetStartupInfo
#undef GetCommandLine
#undef GetEnvironmentVariable
#undef SetEnvironmentVariable
@@ -113,51 +92,22 @@
#undef OutputDebugString
#undef FindResource
#undef FindResourceEx
-#undef EnumResourceTypes
-#undef EnumResourceNames
-#undef EnumResourceLanguages
#undef BeginUpdateResource
#undef UpdateResource
#undef EndUpdateResource
-#undef GlobalAddAtom
-#undef GlobalFindAtom
-#undef GlobalGetAtomName
-#undef AddAtom
-#undef FindAtom
-#undef GetAtomName
-#undef GetProfileInt
-#undef GetProfileString
-#undef WriteProfileString
-#undef GetProfileSection
-#undef WriteProfileSection
#undef GetPrivateProfileInt
-#undef GetPrivateProfileString
-#undef WritePrivateProfileString
-#undef GetPrivateProfileSection
-#undef WritePrivateProfileSection
-#undef GetPrivateProfileSectionNames
-#undef GetPrivateProfileStruct
-#undef WritePrivateProfileStruct
-#undef GetDriveType
#undef GetSystemDirectory
#undef GetTempPath
#undef GetTempFileName
-#undef GetWindowsDirectory
#undef SetCurrentDirectory
#undef GetCurrentDirectory
-#undef GetDiskFreeSpace
-#undef GetDiskFreeSpaceEx
#undef CreateDirectory
-#undef CreateDirectoryEx
#undef RemoveDirectory
#undef GetFullPathName
-#undef DefineDosDevice
-#undef QueryDosDevice
#undef CreateFile
#undef SetFileAttributes
#undef GetFileAttributes
#undef GetFileAttributesEx
-#undef GetCompressedFileSize
#undef DeleteFile
#undef FindFirstFileEx
#undef FindFirstFile
@@ -167,194 +117,23 @@
#undef CopyFileEx
#undef MoveFile
#undef MoveFileEx
-#undef MoveFileWithProgress
-#undef CreateSymbolicLink
-#undef QuerySymbolicLink
#undef CreateHardLink
#undef CreateNamedPipe
-#undef GetNamedPipeHandleState
-#undef CallNamedPipe
#undef WaitNamedPipe
-#undef SetVolumeLabel
-#undef GetVolumeInformation
-#undef ClearEventLog
-#undef BackupEventLog
-#undef OpenEventLog
-#undef RegisterEventSource
-#undef OpenBackupEventLog
-#undef ReadEventLog
-#undef ReportEvent
-#undef AccessCheckAndAuditAlarm
-#undef AccessCheckByTypeAndAuditAlarm
-#undef AccessCheckByTypeResultListAndAuditAlarm
-#undef ObjectOpenAuditAlarm
-#undef ObjectPrivilegeAuditAlarm
-#undef ObjectCloseAuditAlarm
-#undef ObjectDeleteAuditAlarm
-#undef PrivilegedServiceAuditAlarm
-#undef SetFileSecurity
-#undef GetFileSecurity
-#undef FindFirstChangeNotification
-#undef IsBadStringPtr
-#undef LookupAccountSid
-#undef LookupAccountName
#undef LookupPrivilegeValue
-#undef LookupPrivilegeName
-#undef LookupPrivilegeDisplayName
-#undef BuildCommDCB
-#undef BuildCommDCBAndTimeouts
-#undef CommConfigDialog
-#undef GetDefaultCommConfig
-#undef SetDefaultCommConfig
-#undef GetComputerName
-#undef SetComputerName
-#undef GetUserName
-#undef LogonUser
-#undef CreateProcessAsUser
-#undef GetCurrentHwProfile
#undef GetVersionEx
-#undef CreateJobObject
-#undef OpenJobObject
-#undef SetDllDirectory
// winuser.h
#undef MAKEINTRESOURCE
-#undef wvsprintf
-#undef LoadKeyboardLayout
-#undef GetKeyboardLayoutName
-#undef CreateDesktop
-#undef OpenDesktop
-#undef EnumDesktops
-#undef CreateWindowStation
-#undef OpenWindowStation
-#undef EnumWindowStations
#undef GetUserObjectInformation
-#undef SetUserObjectInformation
-#undef RegisterWindowMessage
-#undef SIZEZOOMSHOW
-#undef WS_TILEDWINDOW
#undef GetMessage
-#undef DispatchMessage
-#undef PeekMessage
#undef SendMessage
-#undef SendMessageTimeout
-#undef SendNotifyMessage
-#undef SendMessageCallback
-#undef BroadcastSystemMessage
-#undef RegisterDeviceNotification
-#undef PostMessage
-#undef PostThreadMessage
-#undef PostAppMessage
-#undef DefWindowProc
-#undef CallWindowProc
-#undef RegisterClass
-#undef UnregisterClass
-#undef GetClassInfo
-#undef RegisterClassEx
-#undef GetClassInfoEx
-#undef CreateWindowEx
-#undef CreateWindow
-#undef CreateDialogParam
-#undef CreateDialogIndirectParam
-#undef CreateDialog
-#undef CreateDialogIndirect
-#undef DialogBoxParam
-#undef DialogBoxIndirectParam
-#undef DialogBox
-#undef DialogBoxIndirect
-#undef SetDlgItemText
-#undef GetDlgItemText
-#undef SendDlgItemMessage
-#undef DefDlgProc
-#undef CallMsgFilter
-#undef RegisterClipboardFormat
-#undef GetClipboardFormatName
-#undef CharToOem
-#undef OemToChar
-#undef CharToOemBuff
-#undef OemToCharBuff
-#undef CharUpper
-#undef CharUpperBuff
#undef CharLower
-#undef CharLowerBuff
#undef CharNext
-#undef IsCharAlpha
-#undef IsCharAlphaNumeric
-#undef IsCharUpper
-#undef IsCharLower
-#undef GetKeyNameText
-#undef VkKeyScan
-#undef VkKeyScanEx
-#undef MapVirtualKey
-#undef MapVirtualKeyEx
-#undef LoadAccelerators
-#undef CreateAcceleratorTable
-#undef CopyAcceleratorTable
-#undef TranslateAccelerator
-#undef LoadMenu
-#undef LoadMenuIndirect
-#undef ChangeMenu
-#undef GetMenuString
-#undef InsertMenu
-#undef AppendMenu
-#undef ModifyMenu
-#undef InsertMenuItem
-#undef GetMenuItemInfo
-#undef SetMenuItemInfo
-#undef DrawText
-#undef DrawTextEx
-#undef GrayString
-#undef DrawState
-#undef TabbedTextOut
-#undef GetTabbedTextExtent
-#undef SetProp
-#undef GetProp
-#undef RemoveProp
-#undef EnumPropsEx
-#undef EnumProps
-#undef SetWindowText
-#undef GetWindowText
-#undef GetWindowTextLength
#undef MessageBox
-#undef MessageBoxEx
-#undef MessageBoxIndirect
-#undef COLOR_3DSHADOW
-#undef GetWindowLong
-#undef SetWindowLong
-#undef GetClassLong
-#undef SetClassLong
-#undef FindWindow
-#undef FindWindowEx
#undef GetClassName
-#undef SetWindowsHook
-#undef SetWindowsHook
-#undef SetWindowsHookEx
-#undef MFT_OWNERDRAW
-#undef LoadBitmap
-#undef LoadCursor
-#undef LoadCursorFromFile
-#undef LoadIcon
-#undef LoadImage
#undef LoadString
-#undef IsDialogMessage
-#undef DlgDirList
-#undef DlgDirSelectEx
-#undef DlgDirListComboBox
-#undef DlgDirSelectComboBoxEx
-#undef DefFrameProc
-#undef DefMDIChildProc
-#undef CreateMDIWindow
-#undef WinHelp
-#undef ChangeDisplaySettings
-#undef ChangeDisplaySettingsEx
-#undef EnumDisplaySettings
-#undef EnumDisplayDevices
-#undef SystemParametersInfo
-#undef GetMonitorInfo
-#undef GetWindowModuleFileName
-#undef RealGetWindowClass
-#undef GetAltTabInfo
#undef GetCalendarInfo
#undef GetDateFormat
#undef GetTimeFormat
@@ -375,7 +154,6 @@
// wincrypt.h
#define WszCryptAcquireContext CryptAcquireContextW
-#define WszCryptGetDefaultProvider CryptGetDefaultProviderW
#define WszCryptSignHash CryptSignHashW
#define WszCryptVerifySignature CryptVerifySignatureW
@@ -387,9 +165,6 @@
#else
#define WszFormatMessage CCompRC::FormatMessage
#endif
-#define WszCreateMailslot CreateMailslotW
-#define WszOpenRaw OpenRawW
-#define WszQueryRecoveryAgents QueryRecoveryAgentsW
#define Wszlstrcmp lstrcmpW
#define Wszlstrcmpi lstrcmpiW
#define WszCreateMutex CreateMutexW
@@ -397,234 +172,35 @@
#define WszCreateEvent CreateEventW
#define WszOpenEvent OpenEventW
#define WszCreateWaitableTimer CreateWaitableTimerW
-#define WszOpenWaitableTimer OpenWaitableTimerW
#define WszCreateFileMapping CreateFileMappingW
#define WszOpenFileMapping OpenFileMappingW
-#define WszGetLogicalDriveStrings GetLogicalDriveStringsW
#define WszGetModuleHandle GetModuleHandleW
#define WszGetModuleHandleEx GetModuleHandleExW
-#define WszFatalAppExit FatalAppExitW
-#define WszGetStartupInfo GetStartupInfoW
#define WszGetCommandLine GetCommandLineW
#define WszSetEnvironmentVariable SetEnvironmentVariableW
#define WszExpandEnvironmentStrings ExpandEnvironmentStringsW
#define WszOutputDebugString OutputDebugStringW
#define WszFindResource FindResourceW
#define WszFindResourceEx FindResourceExW
-#define WszEnumResourceTypes EnumResourceTypesW
-#define WszEnumResourceNames EnumResourceNamesW
-#define WszEnumResourceLanguages EnumResourceLanguagesW
#define WszBeginUpdateResource BeginUpdateResourceW
#define WszUpdateResource UpdateResourceW
#define WszEndUpdateResource EndUpdateResourceW
-#define WszGlobalAddAtom GlobalAddAtomW
-#define WszGlobalFindAtom GlobalFindAtomW
-#define WszGlobalGetAtomName GlobalGetAtomNameW
-#define WszAddAtom AddAtomW
-#define WszFindAtom FindAtomW
-#define WszGetAtomName GetAtomNameW
-#define WszGetProfileInt GetProfileIntW
-#define WszGetProfileString GetProfileStringW
-#define WszWriteProfileString WriteProfileStringW
-#define WszGetProfileSection GetProfileSectionW
-#define WszWriteProfileSection WriteProfileSectionW
#define WszGetPrivateProfileInt GetPrivateProfileIntW
-#define WszGetPrivateProfileString GetPrivateProfileStringW
-#define WszWritePrivateProfileString WritePrivateProfileStringW
-#define WszGetPrivateProfileSection GetPrivateProfileSectionW
-#define WszWritePrivateProfileSection WritePrivateProfileSectionW
-#define WszGetPrivateProfileSectionNames GetPrivateProfileSectionNamesW
-#define WszGetPrivateProfileStruct GetPrivateProfileStructW
-#define WszWritePrivateProfileStruct WritePrivateProfileStructW
-#define WszGetDriveType GetDriveTypeW
#define WszGetSystemDirectory GetSystemDirectoryW
-#define WszGetWindowsDirectory GetWindowsDirectoryW
-#define WszGetDiskFreeSpace GetDiskFreeSpaceW
-#define WszGetDiskFreeSpaceEx GetDiskFreeSpaceExW
-#define WszDefineDosDevice DefineDosDeviceW
-#define WszQueryDosDevice QueryDosDeviceW
-#define WszQuerySymbolicLink QuerySymbolicLinkW
#define WszCreateNamedPipe CreateNamedPipeW
-#define WszGetNamedPipeHandleState GetNamedPipeHandleStateW
-#define WszCallNamedPipe CallNamedPipeW
#define WszWaitNamedPipe WaitNamedPipeW
-#define WszSetVolumeLabel SetVolumeLabelW
-#define WszGetVolumeInformation GetVolumeInformationW
-#define WszClearEventLog ClearEventLogW
-#define WszBackupEventLog BackupEventLogW
-#define WszOpenEventLog OpenEventLogW
-#define WszRegisterEventSource RegisterEventSourceW
-#define WszOpenBackupEventLog OpenBackupEventLogW
-#define WszReadEventLog ReadEventLogW
-#define WszReportEvent ReportEventW
-#define WszAccessCheckAndAuditAlarm AccessCheckAndAuditAlarmW
-#define WszAccessCheckByTypeAndAuditAlarm AccessCheckByTypeAndAuditAlarmW
-#define WszAccessCheckByTypeResultListAndAuditAlarm AccessCheckByTypeResultListAndAuditAlarmW
-#define WszObjectOpenAuditAlarm ObjectOpenAuditAlarmW
-#define WszObjectPrivilegeAuditAlarm ObjectPrivilegeAuditAlarmW
-#define WszObjectCloseAuditAlarm ObjectCloseAuditAlarmW
-#define WszObjectDeleteAuditAlarm ObjectDeleteAuditAlarmW
-#define WszPrivilegedServiceAuditAlarm PrivilegedServiceAuditAlarmW
-#define WszIsBadStringPtr __DO_NOT_USE__WszIsBadStringPtr__
-#if !defined(FEATURE_CORESYSTEM) || defined(CROSSGEN_COMPILE)
-#define WszLookupAccountSid LookupAccountSidW
-#endif
-#define WszLookupAccountName LookupAccountNameW
#define WszLookupPrivilegeValue LookupPrivilegeValueW
-#define WszLookupPrivilegeName LookupPrivilegeNameW
-#define WszLookupPrivilegeDisplayName LookupPrivilegeDisplayNameW
-#define WszBuildCommDCB BuildCommDCBW
-#define WszBuildCommDCBAndTimeouts BuildCommDCBAndTimeoutsW
-#define WszCommConfigDialog CommConfigDialogW
-#define WszGetDefaultCommConfig GetDefaultCommConfigW
-#define WszSetDefaultCommConfig SetDefaultCommConfigW
-#define WszGetComputerName GetComputerNameW
-#define WszSetComputerName SetComputerNameW
-#define WszGetUserName GetUserNameW
-#define WszLogonUser LogonUserW
-#define WszCreateProcessAsUser CreateProcessAsUserW
-#define WszGetCurrentHwProfile GetCurrentHwProfileW
-#define WszCreateJobObject CreateJobObjectW
-#define WszOpenJobObject OpenJobObjectW
// winuser.h
#define WszMAKEINTRESOURCE MAKEINTRESOURCEW
-#define Wszwvsprintf wvsprintfW
-#define WszLoadKeyboardLayout LoadKeyboardLayoutW
-#define WszGetKeyboardLayoutName GetKeyboardLayoutNameW
-#define WszCreateDesktop CreateDesktopW
-#define WszOpenDesktop OpenDesktopW
-#define WszEnumDesktops EnumDesktopsW
-#define WszCreateWindowStation CreateWindowStationW
-#define WszOpenWindowStation OpenWindowStationW
-#define WszEnumWindowStations EnumWindowStationsW
#define WszGetUserObjectInformation GetUserObjectInformationW
-#define WszSetUserObjectInformation SetUserObjectInformationW
-#define WszRegisterWindowMessage RegisterWindowMessageW
-#define WszSIZEZOOMSHOW SIZEZOOMSHOWW
-#define WszWS_TILEDWINDOW WS_TILEDWINDOWW
#define WszGetMessage GetMessageW
-#define WszDispatchMessage DispatchMessageW
-#define WszPostMessage PostMessageW
-#define WszPeekMessage PeekMessageW
#define WszSendMessage SendMessageW
-#define WszSendMessageTimeout SendMessageTimeoutW
-#define WszSendNotifyMessage SendNotifyMessageW
-#define WszSendMessageCallback SendMessageCallbackW
-#define WszBroadcastSystemMessage BroadcastSystemMessageW
-#define WszRegisterDeviceNotification RegisterDeviceNotificationW
-#define WszPostMessage PostMessageW
-#define WszPostThreadMessage PostThreadMessageW
-#define WszPostAppMessage PostAppMessageW
-#define WszDefWindowProc DefWindowProcW
-#define WszCallWindowProc CallWindowProcW
-#define WszRegisterClass RegisterClassW
-#define WszUnregisterClass UnregisterClassW
-#define WszGetClassInfo GetClassInfoW
-#define WszRegisterClassEx RegisterClassExW
-#define WszGetClassInfoEx GetClassInfoExW
-#define WszCreateWindowEx CreateWindowExW
-#define WszCreateWindow CreateWindowW
-#define WszCreateDialogParam CreateDialogParamW
-#define WszCreateDialogIndirectParam CreateDialogIndirectParamW
-#define WszCreateDialog CreateDialogW
-#define WszCreateDialogIndirect CreateDialogIndirectW
-#define WszDialogBoxParam DialogBoxParamW
-#define WszDialogBoxIndirectParam DialogBoxIndirectParamW
-#define WszDialogBox DialogBoxW
-#define WszDialogBoxIndirect DialogBoxIndirectW
-#define WszSetDlgItemText SetDlgItemTextW
-#define WszGetDlgItemText GetDlgItemTextW
-#define WszSendDlgItemMessage SendDlgItemMessageW
-#define WszDefDlgProc DefDlgProcW
-#define WszCallMsgFilter CallMsgFilterW
-#define WszRegisterClipboardFormat RegisterClipboardFormatW
-#define WszGetClipboardFormatName GetClipboardFormatNameW
-#define WszCharToOem CharToOemW
-#define WszOemToChar OemToCharW
-#define WszCharToOemBuff CharToOemBuffW
-#define WszOemToCharBuff OemToCharBuffW
-#define WszCharUpper CharUpperW
-#define WszCharUpperBuff CharUpperBuffW
#define WszCharLower CharLowerW
-#define WszCharLowerBuff CharLowerBuffW
#define WszCharNext CharNextW
-#define WszIsCharAlpha IsCharAlphaW
-#define WszIsCharAlphaNumeric IsCharAlphaNumericW
-#define WszIsCharUpper IsCharUpperW
-#define WszIsCharLower IsCharLowerW
-#define WszGetKeyNameText GetKeyNameTextW
-#define WszVkKeyScan VkKeyScanW
-#define WszVkKeyScanEx VkKeyScanExW
-#define WszMapVirtualKey MapVirtualKeyW
-#define WszMapVirtualKeyEx MapVirtualKeyExW
-#define WszLoadAccelerators LoadAcceleratorsW
-#define WszCreateAcceleratorTable CreateAcceleratorTableW
-#define WszCopyAcceleratorTable CopyAcceleratorTableW
-#define WszTranslateAccelerator TranslateAcceleratorW
-#define WszLoadMenu LoadMenuW
-#define WszLoadMenuIndirect LoadMenuIndirectW
-#define WszChangeMenu ChangeMenuW
-#define WszGetMenuString GetMenuStringW
-#define WszInsertMenu InsertMenuW
-#define WszAppendMenu AppendMenuW
-#define WszModifyMenu ModifyMenuW
-#define WszInsertMenuItem InsertMenuItemW
-#define WszGetMenuItemInfo GetMenuItemInfoW
-#define WszSetMenuItemInfo SetMenuItemInfoW
-#define WszDrawText DrawTextW
-#define WszDrawTextEx DrawTextExW
-#define WszGrayString GrayStringW
-#define WszDrawState DrawStateW
-#define WszTabbedTextOut TabbedTextOutW
-#define WszGetTabbedTextExtent GetTabbedTextExtentW
-#define WszSetProp SetPropW
-#define WszGetProp GetPropW
-#define WszRemoveProp RemovePropW
-#define WszEnumPropsEx EnumPropsExW
-#define WszEnumProps EnumPropsW
-#define WszSetWindowText SetWindowTextW
-#define WszGetWindowText GetWindowTextW
-#define WszGetWindowTextLength GetWindowTextLengthW
#define WszMessageBox LateboundMessageBoxW
-#define WszMessageBoxEx MessageBoxExW
-#define WszMessageBoxIndirect MessageBoxIndirectW
-#define WszGetWindowLong GetWindowLongW
-#define WszSetWindowLong SetWindowLongW
-#define WszSetWindowLongPtr SetWindowLongPtrW
-#define WszGetWindowLongPtr GetWindowLongPtrW
-#define WszGetClassLong GetClassLongW
-#define WszSetClassLong SetClassLongW
-#define WszFindWindow FindWindowW
-#define WszFindWindowEx FindWindowExW
#define WszGetClassName GetClassNameW
-#define WszSetWindowsHook SetWindowsHookW
-#define WszSetWindowsHook SetWindowsHookW
-#define WszSetWindowsHookEx SetWindowsHookExW
-#define WszLoadBitmap LoadBitmapW
-#define WszLoadCursor LoadCursorW
-#define WszLoadCursorFromFile LoadCursorFromFileW
-#define WszLoadIcon LoadIconW
-#define WszLoadImage LoadImageW
#define WszLoadString LoadStringW
-#define WszIsDialogMessage IsDialogMessageW
-#define WszDlgDirList DlgDirListW
-#define WszDlgDirSelectEx DlgDirSelectExW
-#define WszDlgDirListComboBox DlgDirListComboBoxW
-#define WszDlgDirSelectComboBoxEx DlgDirSelectComboBoxExW
-#define WszDefFrameProc DefFrameProcW
-#define WszDefMDIChildProc DefMDIChildProcW
-#define WszCreateMDIWindow CreateMDIWindowW
-#define WszWinHelp WinHelpW
-#define WszChangeDisplaySettings ChangeDisplaySettingsW
-#define WszChangeDisplaySettingsEx ChangeDisplaySettingsExW
-#define WszEnumDisplaySettings EnumDisplaySettingsW
-#define WszEnumDisplayDevices EnumDisplayDevicesW
-#define WszSystemParametersInfo SystemParametersInfoW
-#define WszGetMonitorInfo GetMonitorInfoW
-#define WszGetWindowModuleFileName GetWindowModuleFileNameW
-#define WszRealGetWindowClass RealGetWindowClassW
-#define WszGetAltTabInfo GetAltTabInfoW
#define WszRegOpenKeyEx ClrRegOpenKeyEx
#define WszRegOpenKey(hKey, wszSubKey, phkRes) ClrRegOpenKeyEx(hKey, wszSubKey, 0, KEY_ALL_ACCESS, phkRes)
#define WszRegQueryValue RegQueryValueW
@@ -645,8 +221,6 @@
#define WszCreateSemaphore CreateSemaphoreW
#define WszQueryActCtxW QueryActCtxW
-// winnetwk.h
-#define WszWNetGetConnection WNetGetConnectionW
#ifdef FEATURE_CORESYSTEM
@@ -666,9 +240,6 @@
#define _T(str) W(str)
#endif
-// on win98 and higher
-#define Wszlstrlen lstrlenW
-
//File and Directory Functions which need special handling for LongFile Names
//Note only the functions which are currently used are defined
#define WszLoadLibrary LoadLibraryExWrapper
@@ -706,17 +277,7 @@
//NOTE: IF the following API's are enabled ensure that they can work with LongFile Names
//See the usage and implementation of above API's
//
-//#define WszGetCompressedFileSize GetCompressedFileSizeW
-//#define WszMoveFileWithProgress MoveFileWithProgressW
-//#define WszEncryptFile EncryptFileW
-//#define WszDecryptFile DecryptFileW
-//#define WszSetFileSecurity SetFileSecurityW
-//#define WszGetFileSecurity GetFileSecurityW
-//#define WszFindFirstChangeNotification FindFirstChangeNotificationW
-//#define WszSetDllDirectory SetDllDirectoryW
//#define WszSetCurrentDirectory SetCurrentDirectoryW
-//#define WszCreateDirectoryEx CreateDirectoryExW
-//#define WszCreateSymbolicLink CreateSymbolicLinkW
//#define WszGetBinaryType GetBinaryTypeWrapper //Coresys does not seem to have this API
#if FEATURE_PAL
diff --git a/src/jit/ICorJitInfo_API_names.h b/src/jit/ICorJitInfo_API_names.h
index 1330f81097..34a8bc0626 100644
--- a/src/jit/ICorJitInfo_API_names.h
+++ b/src/jit/ICorJitInfo_API_names.h
@@ -106,6 +106,7 @@ DEF_CLR_API(getEEInfo)
DEF_CLR_API(getJitTimeLogFilename)
DEF_CLR_API(getMethodDefFromMethod)
DEF_CLR_API(getMethodName)
+DEF_CLR_API(getMethodNameFromMetadata)
DEF_CLR_API(getMethodHash)
DEF_CLR_API(findNameOfToken)
DEF_CLR_API(getSystemVAmd64PassStructInRegisterDescriptor)
diff --git a/src/jit/ICorJitInfo_API_wrapper.hpp b/src/jit/ICorJitInfo_API_wrapper.hpp
index b9fd876995..78177e16d7 100644
--- a/src/jit/ICorJitInfo_API_wrapper.hpp
+++ b/src/jit/ICorJitInfo_API_wrapper.hpp
@@ -122,10 +122,11 @@ CORINFO_MODULE_HANDLE WrapICorJitInfo::getMethodModule(
void WrapICorJitInfo::getMethodVTableOffset(
CORINFO_METHOD_HANDLE method, /* IN */
unsigned* offsetOfIndirection, /* OUT */
- unsigned* offsetAfterIndirection /* OUT */)
+ unsigned* offsetAfterIndirection, /* OUT */
+ bool* isRelative /* OUT */)
{
API_ENTER(getMethodVTableOffset);
- wrapHnd->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection);
+ wrapHnd->getMethodVTableOffset(method, offsetOfIndirection, offsetAfterIndirection, isRelative);
API_LEAVE(getMethodVTableOffset);
}
@@ -1028,6 +1029,17 @@ const char* WrapICorJitInfo::getMethodName(
return temp;
}
+const char* WrapICorJitInfo::getMethodNameFromMetadata(
+ CORINFO_METHOD_HANDLE ftn, /* IN */
+ const char **className, /* OUT */
+ const char **namespaceName /* OUT */)
+{
+ API_ENTER(getMethodNameFromMetadata);
+ const char* temp = wrapHnd->getMethodNameFromMetaData(ftn, moduleName, namespaceName);
+ API_LEAVE(getMethodNameFromMetadata);
+ return temp;
+}
+
unsigned WrapICorJitInfo::getMethodHash(
CORINFO_METHOD_HANDLE ftn /* IN */)
{
diff --git a/src/jit/alloc.cpp b/src/jit/alloc.cpp
index 5c5f712a3f..4cde8664dc 100644
--- a/src/jit/alloc.cpp
+++ b/src/jit/alloc.cpp
@@ -308,60 +308,6 @@ void ArenaAllocator::freeHostMemory(void* block)
#endif // !defined(DEBUG)
}
-#if defined(DEBUG)
-//------------------------------------------------------------------------
-// ArenaAllocator::alloateMemory:
-// Allocates memory using an `ArenaAllocator`.
-//
-// Arguments:
-// size - The number of bytes to allocate.
-//
-// Return Value:
-// A pointer to the allocated memory.
-//
-// Note:
-// This is the DEBUG-only version of `allocateMemory`; the release
-// version of this method is defined in the corresponding header file.
-// This version of the method has some abilities that the release
-// version does not: it may inject faults into the allocator and
-// seeds all allocations with a specified pattern to help catch
-// use-before-init problems.
-void* ArenaAllocator::allocateMemory(size_t size)
-{
- assert(isInitialized());
- assert(size != 0 && (size & (sizeof(int) - 1)) == 0);
-
- // Ensure that we always allocate in pointer sized increments.
- size = (size_t)roundUp(size, sizeof(size_t));
-
- if (JitConfig.ShouldInjectFault() != 0)
- {
- // Force the underlying memory allocator (either the OS or the CLR hoster)
- // to allocate the memory. Any fault injection will kick in.
- void* p = ClrAllocInProcessHeap(0, S_SIZE_T(1));
- if (p != nullptr)
- {
- ClrFreeInProcessHeap(0, p);
- }
- else
- {
- NOMEM(); // Throw!
- }
- }
-
- void* block = m_nextFreeByte;
- m_nextFreeByte += size;
-
- if (m_nextFreeByte > m_lastFreeByte)
- {
- block = allocateNewPage(size, true);
- }
-
- memset(block, UninitializedWord<char>(), size);
- return block;
-}
-#endif // defined(DEBUG)
-
//------------------------------------------------------------------------
// ArenaAllocator::getTotalBytesAllocated:
// Gets the total number of bytes allocated for all of the arena pages
diff --git a/src/jit/alloc.h b/src/jit/alloc.h
index a769341378..2186bcf5bd 100644
--- a/src/jit/alloc.h
+++ b/src/jit/alloc.h
@@ -67,22 +67,7 @@ public:
virtual void destroy();
-#if defined(DEBUG)
- void* allocateMemory(size_t sz);
-#else // defined(DEBUG)
- inline void* allocateMemory(size_t size)
- {
- void* block = m_nextFreeByte;
- m_nextFreeByte += size;
-
- if (m_nextFreeByte > m_lastFreeByte)
- {
- block = allocateNewPage(size, true);
- }
-
- return block;
- }
-#endif // !defined(DEBUG)
+ inline void* allocateMemory(size_t sz);
size_t getTotalBytesAllocated();
size_t getTotalBytesUsed();
@@ -96,4 +81,60 @@ public:
static ArenaAllocator* getPooledAllocator(IEEMemoryManager* memoryManager);
};
+//------------------------------------------------------------------------
+// ArenaAllocator::allocateMemory:
+// Allocates memory using an `ArenaAllocator`.
+//
+// Arguments:
+// size - The number of bytes to allocate.
+//
+// Return Value:
+// A pointer to the allocated memory.
+//
+// Note:
+// The DEBUG version of the method has some abilities that the release
+// version does not: it may inject faults into the allocator and
+// seeds all allocations with a specified pattern to help catch
+// use-before-init problems.
+//
+inline void* ArenaAllocator::allocateMemory(size_t size)
+{
+ assert(isInitialized());
+ assert(size != 0);
+
+ // Ensure that we always allocate in pointer sized increments.
+ size = (size_t)roundUp(size, sizeof(size_t));
+
+#if defined(DEBUG)
+ if (JitConfig.ShouldInjectFault() != 0)
+ {
+ // Force the underlying memory allocator (either the OS or the CLR hoster)
+ // to allocate the memory. Any fault injection will kick in.
+ void* p = ClrAllocInProcessHeap(0, S_SIZE_T(1));
+ if (p != nullptr)
+ {
+ ClrFreeInProcessHeap(0, p);
+ }
+ else
+ {
+ NOMEM(); // Throw!
+ }
+ }
+#endif
+
+ void* block = m_nextFreeByte;
+ m_nextFreeByte += size;
+
+ if (m_nextFreeByte > m_lastFreeByte)
+ {
+ block = allocateNewPage(size, true);
+ }
+
+#if defined(DEBUG)
+ memset(block, UninitializedWord<char>(), size);
+#endif
+
+ return block;
+}
+
#endif // _ALLOC_H_
diff --git a/src/jit/assertionprop.cpp b/src/jit/assertionprop.cpp
index bcdae634e0..ed49e841ea 100644
--- a/src/jit/assertionprop.cpp
+++ b/src/jit/assertionprop.cpp
@@ -524,7 +524,7 @@ ASSERT_TP& Compiler::GetAssertionDep(unsigned lclNum)
void Compiler::optAssertionTraitsInit(AssertionIndex assertionCount)
{
- apTraits = new (getAllocator()) BitVecTraits(assertionCount, this);
+ apTraits = new (this, CMK_AssertionProp) BitVecTraits(assertionCount, this);
apFull = BitVecOps::MakeFull(apTraits);
}
@@ -547,9 +547,9 @@ void Compiler::optAssertionInit(bool isLocalProp)
optMaxAssertionCount = countFunc[isLocalProp ? lowerBound : min(upperBound, codeSize)];
optLocalAssertionProp = isLocalProp;
- optAssertionTabPrivate = new (getAllocator()) AssertionDsc[optMaxAssertionCount];
+ optAssertionTabPrivate = new (this, CMK_AssertionProp) AssertionDsc[optMaxAssertionCount];
optComplementaryAssertionMap =
- new (getAllocator()) AssertionIndex[optMaxAssertionCount](); // zero-inited (NO_ASSERTION_INDEX.)
+ new (this, CMK_AssertionProp) AssertionIndex[optMaxAssertionCount + 1](); // zero-inited (NO_ASSERTION_INDEX)
assert(NO_ASSERTION_INDEX == 0);
if (!isLocalProp)
@@ -559,7 +559,7 @@ void Compiler::optAssertionInit(bool isLocalProp)
if (optAssertionDep == nullptr)
{
- optAssertionDep = new (getAllocator()) ExpandArray<ASSERT_TP>(getAllocator(), max(1, lvaCount));
+ optAssertionDep = new (this, CMK_AssertionProp) ExpandArray<ASSERT_TP>(getAllocator(), max(1, lvaCount));
}
optAssertionTraitsInit(optMaxAssertionCount);
@@ -2150,6 +2150,10 @@ void Compiler::optMapComplementary(AssertionIndex assertionIndex, AssertionIndex
{
return;
}
+
+ assert(assertionIndex <= optMaxAssertionCount);
+ assert(index <= optMaxAssertionCount);
+
optComplementaryAssertionMap[assertionIndex] = index;
optComplementaryAssertionMap[index] = assertionIndex;
}
@@ -3417,7 +3421,7 @@ GenTreePtr Compiler::optAssertionProp_Comma(ASSERT_VALARG_TP assertions, const G
if ((tree->gtGetOp1()->OperGet() == GT_ARR_BOUNDS_CHECK) &&
((tree->gtGetOp1()->gtFlags & GTF_ARR_BOUND_INBND) != 0))
{
- optRemoveRangeCheck(tree, stmt, true, GTF_ASG, true /* force remove */);
+ optRemoveRangeCheck(tree, stmt);
return optAssertionProp_Update(tree, tree, stmt);
}
return nullptr;
@@ -3471,6 +3475,7 @@ GenTreePtr Compiler::optAssertionProp_Ind(ASSERT_VALARG_TP assertions, const Gen
}
#endif
tree->gtFlags &= ~GTF_EXCEPT;
+ tree->gtFlags |= GTF_IND_NONFAULTING;
// Set this flag to prevent reordering
tree->gtFlags |= GTF_ORDER_SIDEEFF;
diff --git a/src/jit/block.cpp b/src/jit/block.cpp
index 8e5dc2999f..86760e4962 100644
--- a/src/jit/block.cpp
+++ b/src/jit/block.cpp
@@ -583,6 +583,17 @@ void BasicBlock::dspBlockHeader(Compiler* compiler,
printf("\n");
}
+const char* BasicBlock::dspToString(int blockNumPadding /* = 2*/)
+{
+ static char buffers[3][64]; // static array of 3 to allow 3 concurrent calls in one printf()
+ static int nextBufferIndex = 0;
+
+ auto& buffer = buffers[nextBufferIndex];
+ nextBufferIndex = (nextBufferIndex + 1) % _countof(buffers);
+ _snprintf_s(buffer, _countof(buffer), _countof(buffer), "BB%02u%*s [%04u]", bbNum, blockNumPadding, "", bbID);
+ return buffer;
+}
+
#endif // DEBUG
// Allocation function for MemoryPhiArg.
@@ -1283,6 +1294,10 @@ BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind)
block->bbCodeOffs = BAD_IL_OFFSET;
block->bbCodeOffsEnd = BAD_IL_OFFSET;
+#ifdef DEBUG
+ block->bbID = compBasicBlockID++;
+#endif
+
/* Give the block a number, set the ancestor count and weight */
++fgBBcount;
@@ -1323,7 +1338,7 @@ BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind)
#ifdef DEBUG
if (verbose)
{
- printf("New Basic Block BB%02u [%p] created.\n", block->bbNum, dspPtr(block));
+ printf("New Basic Block %s created.\n", block->dspToString());
}
#endif
diff --git a/src/jit/block.h b/src/jit/block.h
index ad120fc451..361b4519e3 100644
--- a/src/jit/block.h
+++ b/src/jit/block.h
@@ -517,7 +517,8 @@ struct BasicBlock : private LIR::Range
bool showFlags = false,
bool showPreds = true); // Print a simple basic block header for various output, including a
// list of predecessors and successors.
-#endif // DEBUG
+ const char* dspToString(int blockNumPadding = 0);
+#endif // DEBUG
typedef unsigned weight_t; // Type used to hold block and edge weights
// Note that for CLR v2.0 and earlier our
@@ -1052,6 +1053,7 @@ struct BasicBlock : private LIR::Range
// in the BB list with that stamp (in this field); then we can tell if (e.g.) predecessors are
// still in the BB list by whether they have the same stamp (with high probability).
unsigned bbTraversalStamp;
+ unsigned bbID;
#endif // DEBUG
ThisInitState bbThisOnEntry();
diff --git a/src/jit/codegenarm.cpp b/src/jit/codegenarm.cpp
index 4a37681234..26c6854d06 100644
--- a/src/jit/codegenarm.cpp
+++ b/src/jit/codegenarm.cpp
@@ -823,7 +823,7 @@ void CodeGen::genCodeForNegNot(GenTree* tree)
}
else
{
- getEmitter()->emitIns_R_R_I(ins, emitTypeSize(tree), targetReg, operandReg, 0);
+ getEmitter()->emitIns_R_R_I(ins, emitTypeSize(tree), targetReg, operandReg, 0, INS_FLAGS_SET);
}
genProduceReg(tree);
@@ -1528,7 +1528,7 @@ void CodeGen::genLongToIntCast(GenTree* cast)
}
//------------------------------------------------------------------------
-// genIntToFloatCast: Generate code to cast an int/long to float/double
+// genIntToFloatCast: Generate code to cast an int to float/double
//
// Arguments:
// treeNode - The GT_CAST node
@@ -1564,40 +1564,26 @@ void CodeGen::genIntToFloatCast(GenTreePtr treeNode)
srcType = genUnsignedType(srcType);
}
- // We should never see a srcType whose size is neither EA_4BYTE or EA_8BYTE
+ // We only expect a srcType whose size is EA_4BYTE.
// For conversions from small types (byte/sbyte/int16/uint16) to float/double,
// we expect the front-end or lowering phase to have generated two levels of cast.
//
emitAttr srcSize = EA_ATTR(genTypeSize(srcType));
- noway_assert((srcSize == EA_4BYTE) || (srcSize == EA_8BYTE));
+ noway_assert(srcSize == EA_4BYTE);
instruction insVcvt = INS_invalid;
if (dstType == TYP_DOUBLE)
{
- if (srcSize == EA_4BYTE)
- {
- insVcvt = (varTypeIsUnsigned(srcType)) ? INS_vcvt_u2d : INS_vcvt_i2d;
- }
- else
- {
- assert(srcSize == EA_8BYTE);
- NYI_ARM("Casting int64/uint64 to double in genIntToFloatCast");
- }
+ insVcvt = (varTypeIsUnsigned(srcType)) ? INS_vcvt_u2d : INS_vcvt_i2d;
}
else
{
assert(dstType == TYP_FLOAT);
- if (srcSize == EA_4BYTE)
- {
- insVcvt = (varTypeIsUnsigned(srcType)) ? INS_vcvt_u2f : INS_vcvt_i2f;
- }
- else
- {
- assert(srcSize == EA_8BYTE);
- NYI_ARM("Casting int64/uint64 to float in genIntToFloatCast");
- }
+ insVcvt = (varTypeIsUnsigned(srcType)) ? INS_vcvt_u2f : INS_vcvt_i2f;
}
+ // All other cast are implemented by different CORINFO_HELP_XX2XX
+ // Look to Compiler::fgMorphCast()
genConsumeOperands(treeNode->AsOp());
@@ -1609,7 +1595,7 @@ void CodeGen::genIntToFloatCast(GenTreePtr treeNode)
}
//------------------------------------------------------------------------
-// genFloatToIntCast: Generate code to cast float/double to int/long
+// genFloatToIntCast: Generate code to cast float/double to int
//
// Arguments:
// treeNode - The GT_CAST node
@@ -1640,40 +1626,26 @@ void CodeGen::genFloatToIntCast(GenTreePtr treeNode)
var_types srcType = op1->TypeGet();
assert(varTypeIsFloating(srcType) && !varTypeIsFloating(dstType));
- // We should never see a dstType whose size is neither EA_4BYTE or EA_8BYTE
+ // We only expect a dstType whose size is EA_4BYTE.
// For conversions to small types (byte/sbyte/int16/uint16) from float/double,
// we expect the front-end or lowering phase to have generated two levels of cast.
//
emitAttr dstSize = EA_ATTR(genTypeSize(dstType));
- noway_assert((dstSize == EA_4BYTE) || (dstSize == EA_8BYTE));
+ noway_assert(dstSize == EA_4BYTE);
instruction insVcvt = INS_invalid;
if (srcType == TYP_DOUBLE)
{
- if (dstSize == EA_4BYTE)
- {
- insVcvt = (varTypeIsUnsigned(dstType)) ? INS_vcvt_d2u : INS_vcvt_d2i;
- }
- else
- {
- assert(dstSize == EA_8BYTE);
- NYI_ARM("Casting double to int64/uint64 in genIntToFloatCast");
- }
+ insVcvt = (varTypeIsUnsigned(dstType)) ? INS_vcvt_d2u : INS_vcvt_d2i;
}
else
{
assert(srcType == TYP_FLOAT);
- if (dstSize == EA_4BYTE)
- {
- insVcvt = (varTypeIsUnsigned(dstType)) ? INS_vcvt_f2u : INS_vcvt_f2i;
- }
- else
- {
- assert(dstSize == EA_8BYTE);
- NYI_ARM("Casting float to int64/uint64 in genIntToFloatCast");
- }
+ insVcvt = (varTypeIsUnsigned(dstType)) ? INS_vcvt_f2u : INS_vcvt_f2i;
}
+ // All other cast are implemented by different CORINFO_HELP_XX2XX
+ // Look to Compiler::fgMorphCast()
genConsumeOperands(treeNode->AsOp());
diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp
index 2e98e21c4d..640478b4e7 100644
--- a/src/jit/codegenarm64.cpp
+++ b/src/jit/codegenarm64.cpp
@@ -1502,53 +1502,44 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre
// Generate code to get the high N bits of a N*N=2N bit multiplication result
void CodeGen::genCodeForMulHi(GenTreeOp* treeNode)
{
- assert(!(treeNode->gtFlags & GTF_UNSIGNED));
assert(!treeNode->gtOverflowEx());
-#if 0
+ genConsumeOperands(treeNode);
+
regNumber targetReg = treeNode->gtRegNum;
var_types targetType = treeNode->TypeGet();
- emitter *emit = getEmitter();
- emitAttr size = emitTypeSize(treeNode);
- GenTree *op1 = treeNode->gtOp1;
- GenTree *op2 = treeNode->gtOp2;
+ emitter* emit = getEmitter();
+ emitAttr attr = emitTypeSize(treeNode);
+ unsigned isUnsigned = (treeNode->gtFlags & GTF_UNSIGNED);
- // to get the high bits of the multiply, we are constrained to using the
- // 1-op form: RDX:RAX = RAX * rm
- // The 3-op form (Rx=Ry*Rz) does not support it.
+ GenTreePtr op1 = treeNode->gtGetOp1();
+ GenTreePtr op2 = treeNode->gtGetOp2();
- genConsumeOperands(treeNode);
+ assert(!varTypeIsFloating(targetType));
- GenTree* regOp = op1;
- GenTree* rmOp = op2;
-
- // Set rmOp to the contained memory operand (if any)
- //
- if (op1->isContained() || (!op2->isContained() && (op2->gtRegNum == targetReg)))
- {
- regOp = op2;
- rmOp = op1;
- }
- assert(!regOp->isContained());
-
- // Setup targetReg when neither of the source operands was a matching register
- if (regOp->gtRegNum != targetReg)
+ // The arithmetic node must be sitting in a register (since it's not contained)
+ assert(targetReg != REG_NA);
+
+ if (EA_SIZE(attr) == EA_8BYTE)
{
- inst_RV_RV(ins_Copy(targetType), targetReg, regOp->gtRegNum, targetType);
+ instruction ins = isUnsigned ? INS_umulh : INS_smulh;
+
+ regNumber r = emit->emitInsTernary(ins, attr, treeNode, op1, op2);
+
+ assert(r == targetReg);
}
-
- emit->emitInsBinary(INS_imulEAX, size, treeNode, rmOp);
-
- // Move the result to the desired register, if necessary
- if (targetReg != REG_RDX)
+ else
{
- inst_RV_RV(INS_mov, targetReg, REG_RDX, targetType);
+ assert(EA_SIZE(attr) == EA_4BYTE);
+
+ instruction ins = isUnsigned ? INS_umull : INS_smull;
+
+ regNumber r = emit->emitInsTernary(ins, EA_4BYTE, treeNode, op1, op2);
+
+ emit->emitIns_R_R_I(isUnsigned ? INS_lsr : INS_asr, EA_8BYTE, targetReg, targetReg, 32);
}
genProduceReg(treeNode);
-#else // !0
- NYI("genCodeForMulHi");
-#endif // !0
}
// Generate code for ADD, SUB, MUL, DIV, UDIV, AND, OR and XOR
@@ -1568,10 +1559,10 @@ void CodeGen::genCodeForBinary(GenTree* treeNode)
instruction ins = genGetInsForOper(treeNode->OperGet(), targetType);
// The arithmetic node must be sitting in a register (since it's not contained)
- noway_assert(targetReg != REG_NA);
+ assert(targetReg != REG_NA);
regNumber r = emit->emitInsTernary(ins, emitTypeSize(treeNode), treeNode, op1, op2);
- noway_assert(r == targetReg);
+ assert(r == targetReg);
genProduceReg(treeNode);
}
@@ -3501,14 +3492,16 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree)
cmpSize = EA_8BYTE;
}
+ instruction ins = tree->OperIs(GT_TEST_EQ, GT_TEST_NE) ? INS_tst : INS_cmp;
+
if (op2->isContainedIntOrIImmed())
{
GenTreeIntConCommon* intConst = op2->AsIntConCommon();
- emit->emitIns_R_I(INS_cmp, cmpSize, op1->gtRegNum, intConst->IconValue());
+ emit->emitIns_R_I(ins, cmpSize, op1->gtRegNum, intConst->IconValue());
}
else
{
- emit->emitIns_R_R(INS_cmp, cmpSize, op1->gtRegNum, op2->gtRegNum);
+ emit->emitIns_R_R(ins, cmpSize, op1->gtRegNum, op2->gtRegNum);
}
}
diff --git a/src/jit/codegenarmarch.cpp b/src/jit/codegenarmarch.cpp
index 8db2f12052..f9081e89c1 100644
--- a/src/jit/codegenarmarch.cpp
+++ b/src/jit/codegenarmarch.cpp
@@ -185,6 +185,10 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
genLeaInstruction(treeNode->AsAddrMode());
break;
+ case GT_INDEX_ADDR:
+ genCodeForIndexAddr(treeNode->AsIndexAddr());
+ break;
+
case GT_IND:
genCodeForIndir(treeNode->AsIndir());
break;
@@ -231,6 +235,10 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
case GT_GE:
case GT_GT:
case GT_CMP:
+#ifdef _TARGET_ARM64_
+ case GT_TEST_EQ:
+ case GT_TEST_NE:
+#endif // _TARGET_ARM64_
genCodeForCompare(treeNode->AsOp());
break;
@@ -265,7 +273,8 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
case GT_LIST:
case GT_FIELD_LIST:
case GT_ARGPLACE:
- // Nothing to do
+ // Should always be marked contained.
+ assert(!"LIST, FIELD_LIST and ARGPLACE nodes should always be marked contained.");
break;
case GT_PUTARG_STK:
@@ -540,13 +549,15 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
// If it is contained then source must be the integer constant zero
if (source->isContained())
{
+#ifdef _TARGET_ARM64_
assert(source->OperGet() == GT_CNS_INT);
assert(source->AsIntConCommon()->IconValue() == 0);
- NYI_ARM("genPutArgStk: contained zero source");
-#ifdef _TARGET_ARM64_
emit->emitIns_S_R(storeIns, storeAttr, REG_ZR, varNumOut, argOffsetOut);
-#endif // _TARGET_ARM64_
+#else // !_TARGET_ARM64_
+ // There is no zero register on ARM32
+ unreached();
+#endif // !_TARGET_ARM64
}
else
{
@@ -654,6 +665,11 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
// only used when we have a multireg struct with a LclVar source
unsigned varNumInp = BAD_VAR_NUM;
+#ifdef _TARGET_ARM_
+ // On ARM32, size of reference map can be larger than MAX_ARG_REG_COUNT
+ gcPtrs = treeNode->gtGcPtrs;
+ gcPtrCount = treeNode->gtNumberReferenceSlots;
+#endif
// Setup the structSize, isHFa, and gcPtrCount
if (varNode != nullptr)
{
@@ -661,17 +677,10 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
assert(varNumInp < compiler->lvaCount);
LclVarDsc* varDsc = &compiler->lvaTable[varNumInp];
+ // This struct also must live in the stack frame
+ // And it can't live in a register (SIMD)
assert(varDsc->lvType == TYP_STRUCT);
-#ifdef _TARGET_ARM_
- if (varDsc->lvPromoted)
- {
- NYI_ARM("CodeGen::genPutArgStk - promoted struct");
- }
- else
-#endif // _TARGET_ARM_
- // This struct also must live in the stack frame
- // And it can't live in a register (SIMD)
- assert(varDsc->lvOnFrame && !varDsc->lvRegister);
+ assert(varDsc->lvOnFrame && !varDsc->lvRegister);
structSize = varDsc->lvSize(); // This yields the roundUp size, but that is fine
// as that is how much stack is allocated for this LclVar
@@ -680,9 +689,6 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
gcPtrCount = varDsc->lvStructGcCount;
for (unsigned i = 0; i < gcPtrCount; ++i)
gcPtrs[i] = varDsc->lvGcLayout[i];
-#else // _TARGET_ARM_
- gcPtrs = treeNode->gtGcPtrs;
- gcPtrCount = treeNode->gtNumberReferenceSlots;
#endif // _TARGET_ARM_
}
else // addrNode is used
@@ -707,7 +713,9 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
structSize = compiler->info.compCompHnd->getClassSize(objClass);
isHfa = compiler->IsHfa(objClass);
+#ifdef _TARGET_ARM64_
gcPtrCount = compiler->info.compCompHnd->getClassGClayout(objClass, &gcPtrs[0]);
+#endif
}
// If we have an HFA we can't have any GC pointers,
@@ -1011,10 +1019,11 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
// handle promote situation
LclVarDsc* varDsc = compiler->lvaTable + srcVarNum;
- if (varDsc->lvPromoted)
- {
- NYI_ARM("CodeGen::genPutArgSplit - promoted struct");
- }
+
+ // This struct also must live in the stack frame
+ // And it can't live in a register (SIMD)
+ assert(varDsc->lvType == TYP_STRUCT);
+ assert(varDsc->lvOnFrame && !varDsc->lvRegister);
// We don't split HFA struct
assert(!varDsc->lvIsHfa());
@@ -1267,7 +1276,7 @@ void CodeGen::genCodeForNullCheck(GenTreeOp* tree)
#ifdef _TARGET_ARM64_
regNumber targetReg = REG_ZR;
#else
- regNumber targetReg = tree->gtRegNum;
+ regNumber targetReg = tree->GetSingleTempReg();
#endif
getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, targetReg, addrReg, 0);
@@ -1539,6 +1548,101 @@ void CodeGen::genCodeForLclFld(GenTreeLclFld* tree)
}
//------------------------------------------------------------------------
+// genCodeForIndexAddr: Produce code for a GT_INDEX_ADDR node.
+//
+// Arguments:
+// tree - the GT_INDEX_ADDR node
+//
+void CodeGen::genCodeForIndexAddr(GenTreeIndexAddr* node)
+{
+ GenTree* const base = node->Arr();
+ GenTree* const index = node->Index();
+
+ genConsumeReg(base);
+ genConsumeReg(index);
+
+ // NOTE: `genConsumeReg` marks the consumed register as not a GC pointer, as it assumes that the input registers
+ // die at the first instruction generated by the node. This is not the case for `INDEX_ADDR`, however, as the
+ // base register is multiply-used. As such, we need to mark the base register as containing a GC pointer until
+ // we are finished generating the code for this node.
+
+ gcInfo.gcMarkRegPtrVal(base->gtRegNum, base->TypeGet());
+ assert(!varTypeIsGC(index->TypeGet()));
+
+ const regNumber tmpReg = node->GetSingleTempReg();
+
+ // Generate the bounds check if necessary.
+ if ((node->gtFlags & GTF_INX_RNGCHK) != 0)
+ {
+ // Create a GT_IND(GT_LEA)) tree for the array length access and load the length into a register.
+ GenTreeAddrMode arrLenAddr(base->TypeGet(), base, nullptr, 0, static_cast<unsigned>(node->gtLenOffset));
+ arrLenAddr.gtRegNum = REG_NA;
+ arrLenAddr.SetContained();
+ arrLenAddr.gtNext = (GenTree*)(-1);
+
+ GenTreeIndir arrLen = indirForm(TYP_INT, &arrLenAddr);
+ arrLen.gtRegNum = tmpReg;
+ arrLen.ClearContained();
+
+ getEmitter()->emitInsLoadStoreOp(ins_Load(TYP_INT), emitTypeSize(TYP_INT), arrLen.gtRegNum, &arrLen);
+
+#ifdef _TARGET_64BIT_
+ // The CLI Spec allows an array to be indexed by either an int32 or a native int. In the case that the index
+ // is a native int on a 64-bit platform, we will need to widen the array length and the compare.
+ if (index->TypeGet() == TYP_I_IMPL)
+ {
+ // Extend the array length as needed.
+ getEmitter()->emitIns_R_R(ins_Move_Extend(TYP_INT, true), EA_8BYTE, arrLen.gtRegNum, arrLen.gtRegNum);
+ }
+#endif
+
+ // Generate the range check.
+ getEmitter()->emitInsBinary(INS_cmp, emitTypeSize(TYP_I_IMPL), index, &arrLen);
+ genJumpToThrowHlpBlk(genJumpKindForOper(GT_GE, CK_UNSIGNED), SCK_RNGCHK_FAIL, node->gtIndRngFailBB);
+ }
+
+ // Compute the address of the array element.
+ switch (node->gtElemSize)
+ {
+ case 1:
+ // dest = base + index
+ getEmitter()->emitIns_R_R_R(INS_add, emitTypeSize(node), node->gtRegNum, base->gtRegNum, index->gtRegNum);
+ break;
+
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ {
+ DWORD lsl;
+ BitScanForward(&lsl, node->gtElemSize);
+
+ // dest = base + index * scale
+ genScaledAdd(emitTypeSize(node), node->gtRegNum, base->gtRegNum, index->gtRegNum, lsl);
+ break;
+ }
+
+ default:
+ {
+ // tmp = scale
+ CodeGen::genSetRegToIcon(tmpReg, (ssize_t)node->gtElemSize, TYP_INT);
+
+ // dest = index * tmp + base
+ getEmitter()->emitIns_R_R_R_R(INS_MULADD, emitTypeSize(node), node->gtRegNum, index->gtRegNum, tmpReg,
+ base->gtRegNum);
+ break;
+ }
+ }
+
+ // dest = dest + elemOffs
+ getEmitter()->emitIns_R_R_I(INS_add, emitTypeSize(node), node->gtRegNum, node->gtRegNum, node->gtElemOffset);
+
+ gcInfo.gcMarkRegSetNpt(base->gtGetRegMask());
+
+ genProduceReg(node);
+}
+
+//------------------------------------------------------------------------
// genCodeForIndir: Produce code for a GT_IND node.
//
// Arguments:
@@ -1554,11 +1658,13 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree)
emitAttr attr = emitTypeSize(tree);
instruction ins = ins_Load(targetType);
- assert((attr != EA_1BYTE) || !(tree->gtFlags & GTF_IND_UNALIGNED));
-
genConsumeAddress(tree->Addr());
- if (tree->gtFlags & GTF_IND_VOLATILE)
+ if ((tree->gtFlags & GTF_IND_VOLATILE) != 0)
{
+ bool isAligned = ((tree->gtFlags & GTF_IND_UNALIGNED) == 0);
+
+ assert((attr != EA_1BYTE) || isAligned);
+
#ifdef _TARGET_ARM64_
GenTree* addr = tree->Addr();
bool useLoadAcquire = genIsValidIntReg(targetReg) && !addr->isContained() &&
@@ -1797,9 +1903,12 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
GenTreePtr argNode = list->Current();
- fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode->gtSkipReloadOrCopy());
+ fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
assert(curArgTabEntry);
+ // GT_RELOAD/GT_COPY use the child node
+ argNode = argNode->gtSkipReloadOrCopy();
+
if (curArgTabEntry->regNum == REG_STK)
continue;
@@ -1998,16 +2107,26 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
void* addr = nullptr;
if (callType == CT_HELPER)
{
- // Direct call to a helper method.
- CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(methHnd);
- noway_assert(helperNum != CORINFO_HELP_UNDEF);
+// Direct call to a helper method.
+#ifdef FEATURE_READYTORUN_COMPILER
+ if (call->gtEntryPoint.addr != NULL)
+ {
+ addr = call->gtEntryPoint.addr;
+ assert(call->gtEntryPoint.accessType == IAT_VALUE);
+ }
+ else
+#endif // FEATURE_READYTORUN_COMPILER
+ {
+ CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(methHnd);
+ noway_assert(helperNum != CORINFO_HELP_UNDEF);
- void* pAddr = nullptr;
- addr = compiler->compGetHelperFtn(helperNum, (void**)&pAddr);
+ void* pAddr = nullptr;
+ addr = compiler->compGetHelperFtn(helperNum, (void**)&pAddr);
- if (addr == nullptr)
- {
- addr = pAddr;
+ if (addr == nullptr)
+ {
+ addr = pAddr;
+ }
}
}
else
@@ -2222,7 +2341,23 @@ void CodeGen::genJmpMethod(GenTreePtr jmp)
var_types storeType = genActualType(varDsc->TypeGet());
emitAttr storeSize = emitActualTypeSize(storeType);
- getEmitter()->emitIns_S_R(ins_Store(storeType), storeSize, varDsc->lvRegNum, varNum, 0);
+#ifdef _TARGET_ARM_
+ if (varDsc->TypeGet() == TYP_LONG)
+ {
+ // long - at least the low half must be enregistered
+ getEmitter()->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, varDsc->lvRegNum, varNum, 0);
+
+ // Is the upper half also enregistered?
+ if (varDsc->lvOtherReg != REG_STK)
+ {
+ getEmitter()->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, varDsc->lvOtherReg, varNum, sizeof(int));
+ }
+ }
+ else
+#endif // _TARGET_ARM_
+ {
+ getEmitter()->emitIns_S_R(ins_Store(storeType), storeSize, varDsc->lvRegNum, varNum, 0);
+ }
// Update lvRegNum life and GC info to indicate lvRegNum is dead and varDsc stack slot is going live.
// Note that we cannot modify varDsc->lvRegNum here because another basic block may not be expecting it.
// Therefore manually update life of varDsc->lvRegNum.
@@ -2268,6 +2403,7 @@ void CodeGen::genJmpMethod(GenTreePtr jmp)
regNumber argReg = varDsc->lvArgReg; // incoming arg register
regNumber argRegNext = REG_NA;
+#ifdef _TARGET_ARM64_
if (varDsc->lvRegNum != argReg)
{
var_types loadType = TYP_UNDEF;
@@ -2295,7 +2431,6 @@ void CodeGen::genJmpMethod(GenTreePtr jmp)
{
if (varDsc->lvIsHfa())
{
- NYI_ARM("CodeGen::genJmpMethod with multireg HFA arg");
NYI_ARM64("CodeGen::genJmpMethod with multireg HFA arg");
}
@@ -2335,6 +2470,104 @@ void CodeGen::genJmpMethod(GenTreePtr jmp)
firstArgVarNum = varNum;
}
}
+#else
+ bool twoParts = false;
+ var_types loadType = TYP_UNDEF;
+ if (varDsc->TypeGet() == TYP_LONG)
+ {
+ twoParts = true;
+ }
+ else if (varDsc->TypeGet() == TYP_DOUBLE)
+ {
+ if (compiler->info.compIsVarArgs || compiler->opts.compUseSoftFP)
+ {
+ twoParts = true;
+ }
+ }
+
+ if (twoParts)
+ {
+ argRegNext = genRegArgNext(argReg);
+
+ if (varDsc->lvRegNum != argReg)
+ {
+ getEmitter()->emitIns_R_S(INS_ldr, EA_PTRSIZE, argReg, varNum, 0);
+ getEmitter()->emitIns_R_S(INS_ldr, EA_PTRSIZE, argRegNext, varNum, REGSIZE_BYTES);
+ }
+
+ if (compiler->info.compIsVarArgs)
+ {
+ fixedIntArgMask |= genRegMask(argReg);
+ fixedIntArgMask |= genRegMask(argRegNext);
+ }
+ }
+ else if (varDsc->lvIsHfaRegArg())
+ {
+ loadType = varDsc->GetHfaType();
+ regNumber fieldReg = argReg;
+ emitAttr loadSize = emitActualTypeSize(loadType);
+ unsigned maxSize = min(varDsc->lvSize(), (LAST_FP_ARGREG + 1 - argReg) * REGSIZE_BYTES);
+
+ for (unsigned ofs = 0; ofs < maxSize; ofs += (unsigned)loadSize)
+ {
+ if (varDsc->lvRegNum != argReg)
+ {
+ getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, fieldReg, varNum, ofs);
+ }
+ assert(genIsValidFloatReg(fieldReg)); // we don't use register tracking for FP
+ fieldReg = regNextOfType(fieldReg, loadType);
+ }
+ }
+ else if (varTypeIsStruct(varDsc))
+ {
+ regNumber slotReg = argReg;
+ unsigned maxSize = min(varDsc->lvSize(), (REG_ARG_LAST + 1 - argReg) * REGSIZE_BYTES);
+
+ for (unsigned ofs = 0; ofs < maxSize; ofs += REGSIZE_BYTES)
+ {
+ unsigned idx = ofs / REGSIZE_BYTES;
+ loadType = compiler->getJitGCType(varDsc->lvGcLayout[idx]);
+
+ if (varDsc->lvRegNum != argReg)
+ {
+ emitAttr loadSize = emitActualTypeSize(loadType);
+
+ getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, slotReg, varNum, ofs);
+ }
+
+ regSet.AddMaskVars(genRegMask(slotReg));
+ gcInfo.gcMarkRegPtrVal(slotReg, loadType);
+ if (genIsValidIntReg(slotReg) && compiler->info.compIsVarArgs)
+ {
+ fixedIntArgMask |= genRegMask(slotReg);
+ }
+
+ slotReg = genRegArgNext(slotReg);
+ }
+ }
+ else
+ {
+ loadType = compiler->mangleVarArgsType(genActualType(varDsc->TypeGet()));
+
+ if (varDsc->lvRegNum != argReg)
+ {
+ getEmitter()->emitIns_R_S(ins_Load(loadType), emitTypeSize(loadType), argReg, varNum, 0);
+ }
+
+ regSet.AddMaskVars(genRegMask(argReg));
+ gcInfo.gcMarkRegPtrVal(argReg, loadType);
+
+ if (genIsValidIntReg(argReg) && compiler->info.compIsVarArgs)
+ {
+ fixedIntArgMask |= genRegMask(argReg);
+ }
+ }
+
+ if (compiler->lvaIsGCTracked(varDsc))
+ {
+ VarSetOps::RemoveElemD(compiler, gcInfo.gcVarPtrSetCur, varNum);
+ }
+#endif
}
// Jmp call to a vararg method - if the method has fewer than fixed arguments that can be max size of reg,
@@ -3028,7 +3261,7 @@ void CodeGen::genLeaInstruction(GenTreeAddrMode* lea)
genConsumeOperands(lea);
emitter* emit = getEmitter();
emitAttr size = emitTypeSize(lea);
- unsigned offset = lea->gtOffset;
+ int offset = lea->Offset();
// In ARM we can only load addresses of the form:
//
@@ -3048,7 +3281,6 @@ void CodeGen::genLeaInstruction(GenTreeAddrMode* lea)
{
GenTree* memBase = lea->Base();
GenTree* index = lea->Index();
- unsigned offset = lea->gtOffset;
DWORD lsl;
@@ -3118,7 +3350,10 @@ void CodeGen::genLeaInstruction(GenTreeAddrMode* lea)
}
else // offset is zero
{
- emit->emitIns_R_R(INS_mov, size, lea->gtRegNum, memBase->gtRegNum);
+ if (lea->gtRegNum != memBase->gtRegNum)
+ {
+ emit->emitIns_R_R(INS_mov, size, lea->gtRegNum, memBase->gtRegNum);
+ }
}
}
else
diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp
index 113720702d..07a52415d0 100644
--- a/src/jit/codegencommon.cpp
+++ b/src/jit/codegencommon.cpp
@@ -1587,8 +1587,12 @@ BasicBlock* CodeGen::genCreateTempLabel()
block->bbFlags |= (compiler->compCurBB->bbFlags & BBF_COLD);
#ifdef DEBUG
+#ifdef UNIX_X86_ABI
+ block->bbTgtStkDepth = (genStackLevel - curNestedAlignment) / sizeof(int);
+#else
block->bbTgtStkDepth = genStackLevel / sizeof(int);
#endif
+#endif
return block;
}
@@ -2384,6 +2388,17 @@ FOUND_AM:
}
/* Special case: constant array index (that is range-checked) */
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#if defined(LEGACY_BACKEND)
+ // If we've already placed rv2 in a register, we are probably being called in a context that has already
+ // presumed that an addressing mode will be created, even if rv2 is constant, and if we fold we may not find a
+ // useful addressing mode (e.g. if we had [mul * rv2 + cns] it might happen to fold to [cns2].
+ if (mode == -1 && rv2->InReg())
+ {
+ fold = false;
+ }
+#endif
if (fold)
{
@@ -2458,6 +2473,8 @@ FOUND_AM:
#if SCALED_ADDR_MODES
*mulPtr = mul;
#endif
+ // TODO-Cleanup: The offset is signed and it should be returned as such. See also
+ // GenTreeAddrMode::gtOffset and its associated cleanup note.
*cnsPtr = (unsigned)cns;
return true;
@@ -2494,6 +2511,10 @@ emitJumpKind CodeGen::genJumpKindForOper(genTreeOps cmp, CompareKind compareKind
EJ_le, // GT_LE
EJ_ge, // GT_GE
EJ_gt, // GT_GT
+#if defined(_TARGET_ARM64_)
+ EJ_eq, // GT_TEST_EQ
+ EJ_ne, // GT_TEST_NE
+#endif
#endif
};
@@ -2517,6 +2538,10 @@ emitJumpKind CodeGen::genJumpKindForOper(genTreeOps cmp, CompareKind compareKind
EJ_ls, // GT_LE
EJ_hs, // GT_GE
EJ_hi, // GT_GT
+#if defined(_TARGET_ARM64_)
+ EJ_eq, // GT_TEST_EQ
+ EJ_ne, // GT_TEST_NE
+#endif
#endif
};
@@ -2540,6 +2565,10 @@ emitJumpKind CodeGen::genJumpKindForOper(genTreeOps cmp, CompareKind compareKind
EJ_NONE, // GT_LE
EJ_pl, // GT_GE (N == 0)
EJ_NONE, // GT_GT
+#if defined(_TARGET_ARM64_)
+ EJ_eq, // GT_TEST_EQ
+ EJ_ne, // GT_TEST_NE
+#endif
#endif
};
@@ -4438,7 +4467,9 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
if ((regSet.rsMaskPreSpillRegs(false) & genRegMask(regNum)) == 0)
#endif // _TARGET_ARM_
{
- noway_assert(xtraReg != varDsc->lvArgReg + i);
+#if !defined(UNIX_AMD64_ABI)
+ noway_assert(xtraReg != (varDsc->lvArgReg + i));
+#endif
noway_assert(regArgMaskLive & genRegMask(regNum));
}
@@ -7488,7 +7519,9 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed)
return;
}
-#if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI) // No profiling for System V systems yet.
+#if defined(_TARGET_AMD64_)
+#if !defined(UNIX_AMD64_ABI)
+
unsigned varNum;
LclVarDsc* varDsc;
@@ -7617,6 +7650,57 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed)
*pInitRegZeroed = false;
}
+#else // !defined(UNIX_AMD64_ABI)
+
+ // Emit profiler EnterCallback(ProfilerMethHnd, caller's SP)
+ // R14 = ProfilerMethHnd
+ if (compiler->compProfilerMethHndIndirected)
+ {
+ // Profiler hooks enabled during Ngen time.
+ // Profiler handle needs to be accessed through an indirection of a pointer.
+ getEmitter()->emitIns_R_AI(INS_mov, EA_PTR_DSP_RELOC, REG_PROFILER_ENTER_ARG_0,
+ (ssize_t)compiler->compProfilerMethHnd);
+ }
+ else
+ {
+ // No need to record relocations, if we are generating ELT hooks under the influence
+ // of COMPlus_JitELTHookEnabled=1
+ if (compiler->opts.compJitELTHookEnabled)
+ {
+ genSetRegToIcon(REG_PROFILER_ENTER_ARG_0, (ssize_t)compiler->compProfilerMethHnd, TYP_I_IMPL);
+ }
+ else
+ {
+ instGen_Set_Reg_To_Imm(EA_8BYTE, REG_PROFILER_ENTER_ARG_0, (ssize_t)compiler->compProfilerMethHnd);
+ }
+ }
+
+ // R15 = caller's SP
+ // Notes
+ // 1) Here we can query caller's SP offset since prolog will be generated after final frame layout.
+ // 2) caller's SP relative offset to FramePointer will be negative. We need to add absolute value
+ // of that offset to FramePointer to obtain caller's SP value.
+ assert(compiler->lvaOutgoingArgSpaceVar != BAD_VAR_NUM);
+ int callerSPOffset = compiler->lvaToCallerSPRelativeOffset(0, isFramePointerUsed());
+ getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_PROFILER_ENTER_ARG_1, genFramePointerReg(), -callerSPOffset);
+
+ // Can't have a call until we have enough padding for rejit
+ genPrologPadForReJit();
+
+ // We can use any callee trash register (other than RAX, RDI, RSI) for call target.
+ // We use R11 here. This will emit either
+ // "call ip-relative 32-bit offset" or
+ // "mov r11, helper addr; call r11"
+ genEmitHelperCall(CORINFO_HELP_PROF_FCN_ENTER, 0, EA_UNKNOWN, REG_DEFAULT_PROFILER_CALL_TARGET);
+
+ // If initReg is one of RBM_CALLEE_TRASH, then it needs to be zero'ed before using.
+ if ((RBM_CALLEE_TRASH & genRegMask(initReg)) != 0)
+ {
+ *pInitRegZeroed = false;
+ }
+
+#endif // !defined(UNIX_AMD64_ABI)
+
#elif defined(_TARGET_X86_) || (defined(_TARGET_ARM_) && defined(LEGACY_BACKEND))
unsigned saveStackLvl2 = genStackLevel;
@@ -7737,7 +7821,8 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FC
// Need to save on to the stack level, since the helper call will pop the argument
unsigned saveStackLvl2 = genStackLevel;
-#if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI) // No profiling for System V systems yet.
+#if defined(_TARGET_AMD64_)
+#if !defined(UNIX_AMD64_ABI)
// Since the method needs to make a profiler callback, it should have out-going arg space allocated.
noway_assert(compiler->lvaOutgoingArgSpaceVar != BAD_VAR_NUM);
@@ -7808,6 +7893,48 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FC
// "mov r8, helper addr; call r8"
genEmitHelperCall(helper, 0, EA_UNKNOWN, REG_ARG_2);
+#else // !defined(UNIX_AMD64_ABI)
+
+ // RDI = ProfilerMethHnd
+ if (compiler->compProfilerMethHndIndirected)
+ {
+ getEmitter()->emitIns_R_AI(INS_mov, EA_PTR_DSP_RELOC, REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd);
+ }
+ else
+ {
+ if (compiler->opts.compJitELTHookEnabled)
+ {
+ genSetRegToIcon(REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd, TYP_I_IMPL);
+ }
+ else
+ {
+ instGen_Set_Reg_To_Imm(EA_8BYTE, REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd);
+ }
+ }
+
+ // RSI = caller's SP
+ if (compiler->lvaDoneFrameLayout == Compiler::FINAL_FRAME_LAYOUT)
+ {
+ int callerSPOffset = compiler->lvaToCallerSPRelativeOffset(0, isFramePointerUsed());
+ getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_ARG_1, genFramePointerReg(), -callerSPOffset);
+ }
+ else
+ {
+ LclVarDsc* varDsc = compiler->lvaTable;
+ NYI_IF((varDsc == nullptr) || !varDsc->lvIsParam, "Profiler ELT callback for a method without any params");
+
+ // lea rdx, [FramePointer + Arg0's offset]
+ getEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, REG_ARG_1, 0, 0);
+ }
+
+ // We can use any callee trash register (other than RAX, RDI, RSI) for call target.
+ // We use R11 here. This will emit either
+ // "call ip-relative 32-bit offset" or
+ // "mov r11, helper addr; call r11"
+ genEmitHelperCall(helper, 0, EA_UNKNOWN, REG_DEFAULT_PROFILER_CALL_TARGET);
+
+#endif // !defined(UNIX_AMD64_ABI)
+
#elif defined(_TARGET_X86_)
//
@@ -8249,6 +8376,14 @@ void CodeGen::genFinalizeFrame()
regSet.rsSetRegsModified(RBM_INT_CALLEE_SAVED & ~RBM_FPBASE);
}
+#ifdef UNIX_AMD64_ABI
+ // On Unix x64 we also save R14 and R15 for ELT profiler hook generation.
+ if (compiler->compIsProfilerHookNeeded())
+ {
+ regSet.rsSetRegsModified(RBM_PROFILER_ENTER_ARG_0 | RBM_PROFILER_ENTER_ARG_1);
+ }
+#endif
+
/* Count how many callee-saved registers will actually be saved (pushed) */
// EBP cannot be (directly) modified for EBP frame and double-aligned frames
diff --git a/src/jit/codegenlegacy.cpp b/src/jit/codegenlegacy.cpp
index 69a74477b0..fa85df63a3 100644
--- a/src/jit/codegenlegacy.cpp
+++ b/src/jit/codegenlegacy.cpp
@@ -207,6 +207,12 @@ bool CodeGenInterface::genMarkLclVar(GenTreePtr tree)
assert(varNum < compiler->lvaCount);
varDsc = compiler->lvaTable + varNum;
+ // Retype byref-typed appearances of intptr-typed lclVars as type intptr.
+ if ((varDsc->TypeGet() == TYP_I_IMPL) && (tree->TypeGet() == TYP_BYREF))
+ {
+ tree->gtType = TYP_I_IMPL;
+ }
+
if (varDsc->lvRegister)
{
genBashLclVar(tree, varNum, varDsc);
@@ -2001,16 +2007,16 @@ void CodeGen::genRangeCheck(GenTreePtr oper)
// Free the registers that were used.
if (!index->IsCnsIntOrI())
{
- regSet.rsMarkRegFree(index->gtRegNum, index);
+ genReleaseReg(index);
}
if (arrRef != NULL)
{
- regSet.rsMarkRegFree(arrRef->gtRegNum, arrRef);
+ genReleaseReg(arrRef);
}
else if (!arrLen->IsCnsIntOrI())
{
- regSet.rsMarkRegFree(arrLen->gtRegNum, arrLen);
+ genReleaseReg(arrLen);
}
}
@@ -2840,8 +2846,6 @@ GenTreePtr CodeGen::genMakeAddrOrFPstk(GenTreePtr tree, regMaskTP* regMaskPtr, b
* Generate code to check that the GS cookie wasn't thrashed by a buffer
* overrun. If pushReg is true, preserve all registers around code sequence.
* Otherwise, ECX maybe modified.
- *
- * TODO-ARM-Bug?: pushReg is not implemented (is it needed for ARM?)
*/
void CodeGen::genEmitGSCookieCheck(bool pushReg)
{
@@ -2857,13 +2861,20 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal);
+#if CPU_LOAD_STORE_ARCH
+ // Lock all ABI argument registers before generating the check. All other registers should be dead, so this
+ // shouldn't over-constrain us.
+ const regMaskTP unlockedArgRegs = RBM_ARG_REGS & ~regSet.rsMaskLock;
+ regMaskTP usedArgRegs;
+ regSet.rsLockReg(unlockedArgRegs, &usedArgRegs);
+#endif
+
if (compiler->gsGlobalSecurityCookieAddr == NULL)
{
// JIT case
CLANG_FORMAT_COMMENT_ANCHOR;
#if CPU_LOAD_STORE_ARCH
-
regNumber reg = regSet.rsGrabReg(RBM_ALLINT);
getEmitter()->emitIns_R_S(ins_Load(TYP_INT), EA_4BYTE, reg, compiler->lvaGSSecurityCookie, 0);
regTracker.rsTrackRegTrash(reg);
@@ -2941,6 +2952,11 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
genDefineTempLabel(gsCheckBlk);
genPopRegs(pushedRegs, byrefPushedRegs, norefPushedRegs);
+
+#if CPU_LOAD_STORE_ARCH
+ // Unlock all ABI argument registers.
+ regSet.rsUnlockReg(unlockedArgRegs, usedArgRegs);
+#endif
}
/*****************************************************************************
@@ -5121,7 +5137,7 @@ GenTreePtr CodeGen::genCodeForCommaTree(GenTreePtr tree)
while (tree->OperGet() == GT_COMMA)
{
GenTreePtr op1 = tree->gtOp.gtOp1;
- genCodeForTree(op1, RBM_NONE);
+ genEvalSideEffects(op1);
gcInfo.gcMarkRegPtrVal(op1);
tree = tree->gtOp.gtOp2;
@@ -14672,7 +14688,7 @@ void CodeGen::genCodeForTreeLng(GenTreePtr tree, regMaskTP needReg, regMaskTP av
if (!genMaxOneBit(needReg))
{
regPair = regSet.rsFindRegPairNo(needReg);
- if (needReg != genRegPairMask(regPair))
+ if ((regPair == REG_PAIR_NONE) || (needReg != genRegPairMask(regPair)))
goto ANY_FREE_REG_UNSIGNED;
loRegMask = genRegMask(genRegPairLo(regPair));
if ((loRegMask & regSet.rsRegMaskCanGrab()) == 0)
@@ -15371,7 +15387,20 @@ void CodeGen::genTableSwitch(regNumber reg, unsigned jumpCnt, BasicBlock** jumpT
#endif // _TARGET_ARM_
- if (jumpCnt < minSwitchTabJumpCnt)
+ bool useJumpSequence = jumpCnt < minSwitchTabJumpCnt;
+
+#if defined(_TARGET_UNIX_) && defined(_TARGET_ARM_)
+ // Force using an inlined jumping instead switch table generation.
+ // Switch jump table is generated with incorrect values in CoreRT case,
+ // so any large switch will crash after loading to PC any such value.
+ // I think this is due to the fact that we use absolute addressing
+ // instead of relative. But in CoreRT is used as a rule relative
+ // addressing when we generate an executable.
+ // See also https://github.com/dotnet/coreclr/issues/13194
+ useJumpSequence = useJumpSequence || compiler->IsTargetAbi(CORINFO_CORERT_ABI);
+#endif // defined(_TARGET_UNIX_) && defined(_TARGET_ARM_)
+
+ if (useJumpSequence)
{
/* Does the first case label follow? */
emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
@@ -17693,7 +17722,16 @@ void CodeGen::SetupLateArgs(GenTreeCall* call)
getEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, regSrc, varNum, 0);
regTracker.rsTrackRegTrash(regSrc);
- gcLayout = compiler->lvaGetGcLayout(varNum);
+
+ if (varDsc->lvExactSize >= TARGET_POINTER_SIZE)
+ {
+ gcLayout = compiler->lvaGetGcLayout(varNum);
+ }
+ else
+ {
+ gcLayout = new (compiler, CMK_Codegen) BYTE[1];
+ gcLayout[0] = TYPE_GC_NONE;
+ }
}
}
else if (arg->gtOper == GT_MKREFANY)
@@ -18622,12 +18660,20 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
firstTgtOffs = pInfo->offsetOfDelegateFirstTarget;
#ifdef _TARGET_ARM_
+ // Ensure that we don't trash any of these registers if we have to load
+ // the helper call target into a register to invoke it.
+ regMaskTP regsUsed = 0;
+
if ((call->gtCallMoreFlags & GTF_CALL_M_SECURE_DELEGATE_INV))
{
- getEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, compiler->virtualStubParamInfo->GetReg(), regThis,
+ getEmitter()->emitIns_R_R_I(INS_add, EA_BYREF, compiler->virtualStubParamInfo->GetReg(), regThis,
pInfo->offsetOfSecureDelegateIndirectCell);
regTracker.rsTrackRegTrash(compiler->virtualStubParamInfo->GetReg());
+
+ // Ensure that the virtual stub param info register doesn't get reused before the call is taken
+ regSet.rsLockReg(compiler->virtualStubParamInfo->GetRegMask(), &regsUsed);
}
+
#endif // _TARGET_ARM_
// Grab an available register to use for the CALL indirection
@@ -18651,6 +18697,13 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr
args, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur, ilOffset, indCallReg);
+
+#ifdef _TARGET_ARM_
+ if ((call->gtCallMoreFlags & GTF_CALL_M_SECURE_DELEGATE_INV))
+ {
+ regSet.rsUnlockReg(compiler->virtualStubParamInfo->GetRegMask(), regsUsed);
+ }
+#endif // _TARGET_ARM_
}
else
@@ -18792,15 +18845,24 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
(ssize_t)stubAddr);
// The stub will write-back to this register, so don't track it
regTracker.rsTrackRegTrash(compiler->virtualStubParamInfo->GetReg());
- getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, REG_JUMP_THUNK_PARAM,
+ regNumber indReg;
+ if (compiler->IsTargetAbi(CORINFO_CORERT_ABI))
+ {
+ indReg = regSet.rsGrabReg(RBM_ALLINT & ~compiler->virtualStubParamInfo->GetRegMask());
+ }
+ else
+ {
+ indReg = REG_JUMP_THUNK_PARAM;
+ }
+ getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indReg,
compiler->virtualStubParamInfo->GetReg(), 0);
- regTracker.rsTrackRegTrash(REG_JUMP_THUNK_PARAM);
+ regTracker.rsTrackRegTrash(indReg);
callTypeStubAddr = emitter::EC_INDIR_R;
getEmitter()->emitIns_Call(emitter::EC_INDIR_R,
NULL, // methHnd
INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr
args, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
- gcInfo.gcRegByrefSetCur, ilOffset, REG_JUMP_THUNK_PARAM);
+ gcInfo.gcRegByrefSetCur, ilOffset, indReg);
#else
// emit an indirect call
@@ -18866,29 +18928,48 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
// stub dispatching is off or this is not a virtual call (could be a tailcall)
{
regNumber vptrReg;
+ regNumber vptrReg1 = REG_NA;
+ regMaskTP vptrMask1 = RBM_NONE;
unsigned vtabOffsOfIndirection;
unsigned vtabOffsAfterIndirection;
+ bool isRelative;
noway_assert(callType == CT_USER_FUNC);
+ /* Get hold of the vtable offset (note: this might be expensive) */
+
+ compiler->info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection,
+ &vtabOffsAfterIndirection, &isRelative);
+
vptrReg =
regSet.rsGrabReg(RBM_ALLINT); // Grab an available register to use for the CALL indirection
vptrMask = genRegMask(vptrReg);
+ if (isRelative)
+ {
+ vptrReg1 = regSet.rsGrabReg(RBM_ALLINT & ~vptrMask);
+ vptrMask1 = genRegMask(vptrReg1);
+ }
+
/* The register no longer holds a live pointer value */
gcInfo.gcMarkRegSetNpt(vptrMask);
+ if (isRelative)
+ {
+ gcInfo.gcMarkRegSetNpt(vptrMask1);
+ }
+
// MOV vptrReg, [REG_CALL_THIS + offs]
getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, genGetThisArgReg(call),
VPTR_OFFS);
regTracker.rsTrackRegTrash(vptrReg);
- noway_assert(vptrMask & ~call->gtCallRegUsedMask);
-
- /* Get hold of the vtable offset (note: this might be expensive) */
+ if (isRelative)
+ {
+ regTracker.rsTrackRegTrash(vptrReg1);
+ }
- compiler->info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection,
- &vtabOffsAfterIndirection);
+ noway_assert(vptrMask & ~call->gtCallRegUsedMask);
/* The register no longer holds a live pointer value */
gcInfo.gcMarkRegSetNpt(vptrMask);
@@ -18897,25 +18978,61 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
if (vtabOffsOfIndirection != CORINFO_VIRTUALCALL_NO_CHUNK)
{
+ if (isRelative)
+ {
+#if defined(_TARGET_ARM_)
+ unsigned offset = vtabOffsOfIndirection + vtabOffsAfterIndirection;
+
+ // ADD vptrReg1, REG_CALL_IND_SCRATCH, vtabOffsOfIndirection + vtabOffsAfterIndirection
+ getEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, vptrReg1, vptrReg, offset);
+#else
+ unreached();
+#endif
+ }
+
// MOV vptrReg, [REG_CALL_IND_SCRATCH + vtabOffsOfIndirection]
getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg,
vtabOffsOfIndirection);
}
+ else
+ {
+ assert(!isRelative);
+ }
/* Call through the appropriate vtable slot */
if (fTailCall)
{
- /* Load the function address: "[vptrReg+vtabOffs] -> reg_intret" */
-
- getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_TAILCALL_ADDR, vptrReg,
- vtabOffsAfterIndirection);
+ if (isRelative)
+ {
+#if defined(_TARGET_ARM_)
+ /* Load the function address: "[vptrReg1 + vptrReg] -> reg_intret" */
+ getEmitter()->emitIns_R_ARR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_TAILCALL_ADDR, vptrReg1,
+ vptrReg, 0);
+#else
+ unreached();
+#endif
+ }
+ else
+ {
+ /* Load the function address: "[vptrReg+vtabOffs] -> reg_intret" */
+ getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_TAILCALL_ADDR, vptrReg,
+ vtabOffsAfterIndirection);
+ }
}
else
{
#if CPU_LOAD_STORE_ARCH
- getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg,
- vtabOffsAfterIndirection);
+ if (isRelative)
+ {
+ getEmitter()->emitIns_R_ARR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg1, vptrReg,
+ 0);
+ }
+ else
+ {
+ getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg,
+ vtabOffsAfterIndirection);
+ }
getEmitter()->emitIns_Call(emitter::EC_INDIR_R, call->gtCallMethHnd,
INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr
@@ -18923,6 +19040,7 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
gcInfo.gcRegByrefSetCur, ilOffset,
vptrReg); // ireg
#else
+ assert(!isRelative);
getEmitter()->emitIns_Call(emitter::EC_FUNC_VIRTUAL, call->gtCallMethHnd,
INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr
args, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
@@ -19052,7 +19170,7 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
CORINFO_CONST_LOOKUP lookup;
compiler->info.compCompHnd->getAddressOfPInvokeTarget(methHnd, &lookup);
- void* addr = lookup.addr;
+ addr = lookup.addr;
assert(addr != NULL);
@@ -19115,6 +19233,10 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
emitCallType = emitter::EC_INDIR_ARD;
#endif // CPU_LOAD_STORE_ARCH
+
+ // For a indirect calls, we don't want to pass the address (used below),
+ // so set it to nullptr. (We've already used the address to load up the target register.)
+ addr = nullptr;
}
}
@@ -19425,6 +19547,7 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
break;
case IAT_PVALUE:
+ {
//------------------------------------------------------
// Non-virtual direct calls to addresses accessed by
// a single indirection.
@@ -19433,10 +19556,29 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
// Load the address into a register, load indirect and call through a register
CLANG_FORMAT_COMMENT_ANCHOR;
#if CPU_LOAD_STORE_ARCH
- indCallReg = regSet.rsGrabReg(RBM_ALLINT); // Grab an available register to use for the CALL
- // indirection
+ regMaskTP indCallMask = RBM_ALLINT;
+
+#ifdef FEATURE_READYTORUN_COMPILER
+ if (call->IsR2RRelativeIndir())
+ {
+ indCallMask &= ~RBM_R2R_INDIRECT_PARAM;
+ }
+#endif // FEATURE_READYTORUN_COMPILER
+
+ // Grab an available register to use for the CALL indirection
+ indCallReg = regSet.rsGrabReg(indCallMask);
instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indCallReg, (ssize_t)addr);
+
+#ifdef FEATURE_READYTORUN_COMPILER
+ if (call->IsR2RRelativeIndir())
+ {
+ noway_assert(regSet.rsRegMaskCanGrab() & RBM_R2R_INDIRECT_PARAM);
+ getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_R2R_INDIRECT_PARAM, indCallReg);
+ regTracker.rsTrackRegTrash(REG_R2R_INDIRECT_PARAM);
+ }
+#endif // FEATURE_READYTORUN_COMPILER
+
getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indCallReg, indCallReg, 0);
regTracker.rsTrackRegTrash(indCallReg);
@@ -19456,7 +19598,8 @@ regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed)
REG_NA, 0, 0, // xreg, xmul, disp
false, /* isJump */
emitter::emitNoGChelper(helperNum));
- break;
+ }
+ break;
case IAT_PPVALUE:
{
diff --git a/src/jit/codegenlinear.cpp b/src/jit/codegenlinear.cpp
index 59d08de9ce..657d7799c1 100644
--- a/src/jit/codegenlinear.cpp
+++ b/src/jit/codegenlinear.cpp
@@ -386,7 +386,7 @@ void CodeGen::genCodeForBBlist()
#endif // DEBUG
genCodeForTreeNode(node);
- if (node->gtHasReg() && node->gtLsraInfo.isLocalDefUse)
+ if (node->gtHasReg() && node->IsUnusedValue())
{
genConsumeReg(node);
}
@@ -735,7 +735,7 @@ void CodeGen::genSpillVar(GenTreePtr tree)
restoreRegVar = true;
}
- instruction storeIns = ins_Store(tree->TypeGet(), compiler->isSIMDTypeLocalAligned(varNum));
+ instruction storeIns = ins_Store(lclTyp, compiler->isSIMDTypeLocalAligned(varNum));
#if CPU_LONG_USES_REGPAIR
if (varTypeIsMultiReg(tree))
{
@@ -1034,6 +1034,32 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree)
unspillTree->gtFlags &= ~GTF_SPILLED;
}
+ else if (unspillTree->OperIsMultiRegOp())
+ {
+ GenTreeMultiRegOp* multiReg = unspillTree->AsMultiRegOp();
+ unsigned regCount = multiReg->GetRegCount();
+
+ // In case of split struct argument node, GTF_SPILLED flag on it indicates that
+ // one or more of its result regs are spilled. Call node needs to be
+ // queried to know which specific result regs to be unspilled.
+ for (unsigned i = 0; i < regCount; ++i)
+ {
+ unsigned flags = multiReg->GetRegSpillFlagByIdx(i);
+ if ((flags & GTF_SPILLED) != 0)
+ {
+ var_types dstType = multiReg->GetRegType(i);
+ regNumber dstReg = multiReg->GetRegNumByIdx(i);
+
+ TempDsc* t = regSet.rsUnspillInPlace(multiReg, dstReg, i);
+ getEmitter()->emitIns_R_S(ins_Load(dstType), emitActualTypeSize(dstType), dstReg, t->tdTempNum(),
+ 0);
+ compiler->tmpRlsTemp(t);
+ gcInfo.gcMarkRegPtrVal(dstReg, dstType);
+ }
+ }
+
+ unspillTree->gtFlags &= ~GTF_SPILLED;
+ }
#endif
else
{
@@ -1061,6 +1087,7 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree)
void CodeGen::genCopyRegIfNeeded(GenTree* node, regNumber needReg)
{
assert((node->gtRegNum != REG_NA) && (needReg != REG_NA));
+ assert(!node->isUsedFromSpillTemp());
if (node->gtRegNum != needReg)
{
inst_RV_RV(INS_mov, needReg, node->gtRegNum, node->TypeGet());
@@ -1656,6 +1683,22 @@ void CodeGen::genProduceReg(GenTree* tree)
}
}
}
+ else if (tree->OperIsMultiRegOp())
+ {
+ GenTreeMultiRegOp* multiReg = tree->AsMultiRegOp();
+ unsigned regCount = multiReg->GetRegCount();
+
+ for (unsigned i = 0; i < regCount; ++i)
+ {
+ unsigned flags = multiReg->GetRegSpillFlagByIdx(i);
+ if ((flags & GTF_SPILL) != 0)
+ {
+ regNumber reg = multiReg->GetRegNumByIdx(i);
+ regSet.rsSpillTree(reg, multiReg, i);
+ gcInfo.gcMarkRegSetNpt(genRegMask(reg));
+ }
+ }
+ }
#endif // _TARGET_ARM_
else
{
diff --git a/src/jit/codegenlinear.h b/src/jit/codegenlinear.h
index 9cd30404bc..40f61bce93 100644
--- a/src/jit/codegenlinear.h
+++ b/src/jit/codegenlinear.h
@@ -148,7 +148,7 @@ void genConsumeBlockOp(GenTreeBlk* blkNode, regNumber dstReg, regNumber srcReg,
void genConsumePutStructArgStk(GenTreePutArgStk* putArgStkNode, regNumber dstReg, regNumber srcReg, regNumber sizeReg);
#endif // FEATURE_PUT_STRUCT_ARG_STK
#ifdef _TARGET_ARM_
-void CodeGen::genConsumeArgSplitStruct(GenTreePutArgSplit* putArgNode);
+void genConsumeArgSplitStruct(GenTreePutArgSplit* putArgNode);
#endif
void genConsumeRegs(GenTree* tree);
@@ -167,6 +167,7 @@ void genCodeForShiftRMW(GenTreeStoreInd* storeInd);
void genCodeForCast(GenTreeOp* tree);
void genCodeForLclAddr(GenTree* tree);
+void genCodeForIndexAddr(GenTreeIndexAddr* tree);
void genCodeForIndir(GenTreeIndir* tree);
void genCodeForNegNot(GenTree* tree);
void genCodeForLclVar(GenTreeLclVar* tree);
diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp
index 57a9adcc3f..5128785c8d 100644
--- a/src/jit/codegenxarch.cpp
+++ b/src/jit/codegenxarch.cpp
@@ -982,6 +982,19 @@ void CodeGen::genCodeForMul(GenTreeOp* treeNode)
unsigned int scale = (unsigned int)(imm - 1);
getEmitter()->emitIns_R_ARX(INS_lea, size, targetReg, rmOp->gtRegNum, rmOp->gtRegNum, scale, 0);
}
+ else if (!requiresOverflowCheck && rmOp->isUsedFromReg() && (imm == genFindLowestBit(imm)) && (imm != 0))
+ {
+ // Use shift for constant multiply when legal
+ uint64_t zextImm = static_cast<uint64_t>(static_cast<size_t>(imm));
+ unsigned int shiftAmount = genLog2(zextImm);
+
+ if (targetReg != rmOp->gtRegNum)
+ {
+ // Copy reg src to dest register
+ inst_RV_RV(ins_Copy(targetType), targetReg, rmOp->gtRegNum, targetType);
+ }
+ inst_RV_SH(INS_shl, size, targetReg, shiftAmount);
+ }
else
{
// use the 3-op form with immediate
@@ -1384,16 +1397,51 @@ void CodeGen::genReturn(GenTreePtr treeNode)
// Since we are invalidating the assumption that we would slip into the epilog
// right after the "return", we need to preserve the return reg's GC state
// across the call until actual method return.
+ ReturnTypeDesc retTypeDesc;
+ unsigned regCount = 0;
+ if (compiler->compMethodReturnsMultiRegRetType())
+ {
+ if (varTypeIsLong(compiler->info.compRetNativeType))
+ {
+ retTypeDesc.InitializeLongReturnType(compiler);
+ }
+ else // we must have a struct return type
+ {
+ retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass);
+ }
+ regCount = retTypeDesc.GetReturnRegCount();
+ }
+
if (varTypeIsGC(compiler->info.compRetType))
{
gcInfo.gcMarkRegPtrVal(REG_INTRET, compiler->info.compRetType);
}
+ else if (compiler->compMethodReturnsMultiRegRetType())
+ {
+ for (unsigned i = 0; i < regCount; ++i)
+ {
+ if (varTypeIsGC(retTypeDesc.GetReturnRegType(i)))
+ {
+ gcInfo.gcMarkRegPtrVal(retTypeDesc.GetABIReturnReg(i), retTypeDesc.GetReturnRegType(i));
+ }
+ }
+ }
genProfilingLeaveCallback();
if (varTypeIsGC(compiler->info.compRetType))
{
- gcInfo.gcMarkRegSetNpt(REG_INTRET);
+ gcInfo.gcMarkRegSetNpt(genRegMask(REG_INTRET));
+ }
+ else if (compiler->compMethodReturnsMultiRegRetType())
+ {
+ for (unsigned i = 0; i < regCount; ++i)
+ {
+ if (varTypeIsGC(retTypeDesc.GetReturnRegType(i)))
+ {
+ gcInfo.gcMarkRegSetNpt(genRegMask(retTypeDesc.GetABIReturnReg(i)));
+ }
+ }
}
}
#endif
@@ -1774,6 +1822,10 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
genLeaInstruction(treeNode->AsAddrMode());
break;
+ case GT_INDEX_ADDR:
+ genCodeForIndexAddr(treeNode->AsIndexAddr());
+ break;
+
case GT_IND:
genCodeForIndir(treeNode->AsIndir());
break;
@@ -1838,7 +1890,8 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
case GT_LIST:
case GT_FIELD_LIST:
case GT_ARGPLACE:
- // Nothing to do
+ // Should always be marked contained.
+ assert(!"LIST, FIELD_LIST and ARGPLACE nodes should always be marked contained.");
break;
case GT_SWAP:
@@ -4436,8 +4489,8 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree)
if (targetReg == REG_NA)
{
// stack store
- emit->emitInsMov(ins_Store(targetType, compiler->isSIMDTypeLocalAligned(lclNum)), emitTypeSize(targetType),
- tree);
+ emit->emitInsStoreLcl(ins_Store(targetType, compiler->isSIMDTypeLocalAligned(lclNum)),
+ emitTypeSize(targetType), tree);
varDsc->lvRegNum = REG_STK;
}
else
@@ -4481,6 +4534,97 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree)
}
//------------------------------------------------------------------------
+// genCodeForIndexAddr: Produce code for a GT_INDEX_ADDR node.
+//
+// Arguments:
+// tree - the GT_INDEX_ADDR node
+//
+void CodeGen::genCodeForIndexAddr(GenTreeIndexAddr* node)
+{
+ GenTree* const base = node->Arr();
+ GenTree* const index = node->Index();
+
+ genConsumeReg(base);
+ genConsumeReg(index);
+
+ // NOTE: `genConsumeReg` marks the consumed register as not a GC pointer, as it assumes that the input registers
+ // die at the first instruction generated by the node. This is not the case for `INDEX_ADDR`, however, as the
+ // base register is multiply-used. As such, we need to mark the base register as containing a GC pointer until
+ // we are finished generating the code for this node.
+
+ gcInfo.gcMarkRegPtrVal(base->gtRegNum, base->TypeGet());
+ assert(!varTypeIsGC(index->TypeGet()));
+
+ regNumber tmpReg = REG_NA;
+
+ // Generate the bounds check if necessary.
+ if ((node->gtFlags & GTF_INX_RNGCHK) != 0)
+ {
+ // Create a GT_IND(GT_LEA)) tree for the array length access.
+ GenTreeAddrMode arrLenAddr(base->TypeGet(), base, nullptr, 0, node->gtLenOffset);
+ arrLenAddr.gtRegNum = REG_NA;
+ arrLenAddr.SetContained();
+ arrLenAddr.gtNext = (GenTree*)(-1);
+
+ GenTreeIndir arrLen = indirForm(TYP_INT, &arrLenAddr);
+
+#ifdef _TARGET_64BIT_
+ // The CLI Spec allows an array to be indexed by either an int32 or a native int. In the case that the index
+ // is a native int on a 64-bit platform, we will need to widen the array length and the compare.
+ if (index->TypeGet() == TYP_I_IMPL)
+ {
+ // Load the array length into a register.
+ tmpReg = node->GetSingleTempReg();
+ arrLen.gtRegNum = tmpReg;
+ arrLen.ClearContained();
+ getEmitter()->emitInsLoadInd(ins_Load(TYP_INT), EA_4BYTE, arrLen.gtRegNum, &arrLen);
+ }
+ else
+#endif
+ {
+ assert(varTypeIsIntegral(index->TypeGet()));
+
+ arrLen.gtRegNum = REG_NA;
+ arrLen.SetContained();
+ arrLen.gtNext = (GenTree*)(-1);
+ }
+
+ // Generate the range check.
+ getEmitter()->emitInsBinary(INS_cmp, emitTypeSize(TYP_I_IMPL), index, &arrLen);
+ genJumpToThrowHlpBlk(EJ_jae, SCK_RNGCHK_FAIL, node->gtIndRngFailBB);
+ }
+
+ // Compute the address of the array element.
+ switch (node->gtElemSize)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ getEmitter()->emitIns_R_ARX(INS_lea, emitTypeSize(node), node->gtRegNum, base->gtRegNum, index->gtRegNum,
+ node->gtElemSize, static_cast<int>(node->gtElemOffset));
+ break;
+
+ default:
+ {
+ // Multiply the index by the element size.
+ //
+ // TODO-CQ: this should really just use `imul index, index, #gtElemSize`
+ tmpReg = (tmpReg == REG_NA) ? node->GetSingleTempReg() : tmpReg;
+ CodeGen::genSetRegToIcon(tmpReg, (ssize_t)node->gtElemSize, TYP_INT);
+ inst_RV_RV(INS_imul, tmpReg, index->gtRegNum);
+ getEmitter()->emitIns_R_ARX(INS_lea, emitTypeSize(node), node->gtRegNum, base->gtRegNum, tmpReg, 1,
+ static_cast<int>(node->gtElemOffset));
+ break;
+ }
+ }
+
+ gcInfo.gcMarkRegSetNpt(base->gtGetRegMask());
+
+ genProduceReg(node);
+}
+
+//------------------------------------------------------------------------
// genCodeForIndir: Produce code for a GT_IND node.
//
// Arguments:
@@ -4512,7 +4656,7 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree)
else
{
genConsumeAddress(addr);
- emit->emitInsMov(ins_Load(targetType), emitTypeSize(tree), tree);
+ emit->emitInsLoadInd(ins_Load(targetType), emitTypeSize(tree), tree->gtRegNum, tree);
}
genProduceReg(tree);
@@ -4774,7 +4918,7 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)
}
else
{
- getEmitter()->emitInsMov(ins_Store(data->TypeGet()), emitTypeSize(tree), tree);
+ getEmitter()->emitInsStoreInd(ins_Store(data->TypeGet()), emitTypeSize(tree), tree);
}
}
}
@@ -5812,16 +5956,16 @@ void CodeGen::genLeaInstruction(GenTreeAddrMode* lea)
{
regNumber baseReg = lea->Base()->gtRegNum;
regNumber indexReg = lea->Index()->gtRegNum;
- getEmitter()->emitIns_R_ARX(INS_lea, size, lea->gtRegNum, baseReg, indexReg, lea->gtScale, lea->gtOffset);
+ getEmitter()->emitIns_R_ARX(INS_lea, size, lea->gtRegNum, baseReg, indexReg, lea->gtScale, lea->Offset());
}
else if (lea->Base())
{
- getEmitter()->emitIns_R_AR(INS_lea, size, lea->gtRegNum, lea->Base()->gtRegNum, lea->gtOffset);
+ getEmitter()->emitIns_R_AR(INS_lea, size, lea->gtRegNum, lea->Base()->gtRegNum, lea->Offset());
}
else if (lea->Index())
{
getEmitter()->emitIns_R_ARX(INS_lea, size, lea->gtRegNum, REG_NA, lea->Index()->gtRegNum, lea->gtScale,
- lea->gtOffset);
+ lea->Offset());
}
genProduceReg(lea);
@@ -6083,10 +6227,11 @@ void CodeGen::genCompareInt(GenTreePtr treeNode)
// it was spilled after producing its result in a register.
// Spill code too will not modify the flags set by op1.
GenTree* realOp1 = op1->gtSkipReloadOrCopy();
- if (realOp1->gtSetFlags())
+ if ((tree->gtFlags & GTF_USE_FLAGS) != 0)
{
// op1 must set ZF and SF flags
assert(realOp1->gtSetZSFlags());
+ assert(realOp1->gtSetFlags());
// Must be (in)equality against zero.
assert(tree->OperIs(GT_EQ, GT_NE));
@@ -6577,7 +6722,7 @@ void CodeGen::genIntToIntCast(GenTreePtr treeNode)
if (signCheckOnly)
{
// We only need to check for a negative value in sourceReg
- inst_RV_IV(INS_cmp, sourceReg, 0, srcSize);
+ inst_RV_RV(INS_test, sourceReg, sourceReg, srcType, srcSize);
genJumpToThrowHlpBlk(EJ_jl, SCK_OVERFLOW);
}
else
@@ -6640,34 +6785,7 @@ void CodeGen::genIntToIntCast(GenTreePtr treeNode)
ins = INS_mov;
}
- if (ins == INS_AND)
- {
- noway_assert(isUnsignedDst);
-
- /* Generate "and reg, MASK */
- unsigned fillPattern;
- if (dstSize == EA_1BYTE)
- {
- fillPattern = 0xff;
- }
- else if (dstSize == EA_2BYTE)
- {
- fillPattern = 0xffff;
- }
- else
- {
- fillPattern = 0xffffffff;
- }
-
- inst_RV_IV(INS_AND, targetReg, fillPattern, EA_4BYTE);
- }
-#ifdef _TARGET_AMD64_
- else if (ins == INS_movsxd)
- {
- inst_RV_RV(ins, targetReg, sourceReg, srcType, srcSize);
- }
-#endif // _TARGET_AMD64_
- else if (ins == INS_mov)
+ if (ins == INS_mov)
{
if (targetReg != sourceReg
#ifdef _TARGET_AMD64_
@@ -6680,6 +6798,12 @@ void CodeGen::genIntToIntCast(GenTreePtr treeNode)
inst_RV_RV(ins, targetReg, sourceReg, srcType, srcSize);
}
}
+#ifdef _TARGET_AMD64_
+ else if (ins == INS_movsxd)
+ {
+ inst_RV_RV(ins, targetReg, sourceReg, srcType, srcSize);
+ }
+#endif // _TARGET_AMD64_
else
{
noway_assert(ins == INS_movsx || ins == INS_movzx);
@@ -8202,7 +8326,6 @@ void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk)
var_types memType = (gcPtrs[i] == TYPE_GC_REF) ? TYP_REF : TYP_BYREF;
getEmitter()->emitIns_R_AR(ins_Load(memType), emitTypeSize(memType), REG_RCX, REG_RSI, 0);
genStoreRegToStackArg(memType, REG_RCX, i * TARGET_POINTER_SIZE);
-
#ifdef DEBUG
numGCSlotsCopied++;
#endif // DEBUG
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
index ab5f4743d7..552a6dff94 100644
--- a/src/jit/compiler.cpp
+++ b/src/jit/compiler.cpp
@@ -1003,9 +1003,14 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
//
if (structSize <= sizeof(double))
{
- // We set the "primitive" useType based upon the structSize
- // and also examine the clsHnd to see if it is an HFA of count one
- useType = getPrimitiveTypeForStruct(structSize, clsHnd);
+#if defined LEGACY_BACKEND
+ if (!IsHfa(clsHnd))
+#endif
+ {
+ // We set the "primitive" useType based upon the structSize
+ // and also examine the clsHnd to see if it is an HFA of count one
+ useType = getPrimitiveTypeForStruct(structSize, clsHnd);
+ }
}
#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
@@ -1043,8 +1048,10 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
// Structs that are HFA's are returned in multiple registers
if (IsHfa(clsHnd))
{
+#if !defined(LEGACY_BACKEND)
// HFA's of count one should have been handled by getPrimitiveTypeForStruct
assert(GetHfaCount(clsHnd) >= 2);
+#endif // !defined(LEGACY_BACKEND)
// setup wbPassType and useType indicate that this is returned by value as an HFA
// using multiple registers
@@ -3650,18 +3657,19 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
assert(opts.compGCPollType == GCPOLL_NONE);
opts.compGCPollType = GCPOLL_INLINE;
}
+
+#ifdef PROFILING_SUPPORTED
+#ifdef UNIX_AMD64_ABI
+ if (compIsProfilerHookNeeded())
+ {
+ opts.compNeedToAlignFrame = true;
+ }
+#endif // UNIX_AMD64_ABI
+#endif
}
#ifdef DEBUG
-void JitDump(const char* pcFormat, ...)
-{
- va_list lst;
- va_start(lst, pcFormat);
- vflogf(jitstdout, pcFormat, lst);
- va_end(lst);
-}
-
bool Compiler::compJitHaltMethod()
{
/* This method returns true when we use an INS_BREAKPOINT to allow us to step into the generated native code */
@@ -3848,8 +3856,7 @@ void Compiler::compInitDebuggingInfo()
fgInsertStmtAtEnd(fgFirstBB, gtNewNothingNode());
- JITDUMP("Debuggable code - Add new BB%02u to perform initialization of variables [%08X]\n", fgFirstBB->bbNum,
- dspPtr(fgFirstBB));
+ JITDUMP("Debuggable code - Add new %s to perform initialization of variables\n", fgFirstBB->dspToString());
}
/*-------------------------------------------------------------------------
@@ -5022,6 +5029,29 @@ bool Compiler::compQuirkForPPP()
// This fixes the PPP backward compat issue
varDscExposedStruct->lvExactSize += 32;
+ // Update the GC info to indicate that the padding area does
+ // not contain any GC pointers.
+ //
+ // The struct is now 64 bytes.
+ //
+ // We're on x64 so this should be 8 pointer slots.
+ assert((varDscExposedStruct->lvExactSize / TARGET_POINTER_SIZE) == 8);
+
+ BYTE* oldGCPtrs = varDscExposedStruct->lvGcLayout;
+ BYTE* newGCPtrs = (BYTE*)compGetMemA(8, CMK_LvaTable);
+
+ for (int i = 0; i < 4; i++)
+ {
+ newGCPtrs[i] = oldGCPtrs[i];
+ }
+
+ for (int i = 4; i < 8; i++)
+ {
+ newGCPtrs[i] = TYPE_GC_NONE;
+ }
+
+ varDscExposedStruct->lvGcLayout = newGCPtrs;
+
return true;
}
return false;
@@ -5800,8 +5830,9 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
compCurBB = nullptr;
lvaTable = nullptr;
- // Reset node ID counter
- compGenTreeID = 0;
+ // Reset node and block ID counter
+ compGenTreeID = 0;
+ compBasicBlockID = 0;
#endif
/* Initialize emitter */
@@ -5845,6 +5876,13 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
compInitDebuggingInfo();
}
+#ifdef DEBUG
+ if (compIsForInlining())
+ {
+ compBasicBlockID = impInlineInfo->InlinerCompiler->compBasicBlockID;
+ }
+#endif
+
const bool forceInline = !!(info.compFlags & CORINFO_FLG_FORCEINLINE);
if (!compIsForInlining() && opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT))
@@ -5977,7 +6015,8 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr,
#ifdef DEBUG
if (compIsForInlining())
{
- impInlineInfo->InlinerCompiler->compGenTreeID = compGenTreeID;
+ impInlineInfo->InlinerCompiler->compGenTreeID = compGenTreeID;
+ impInlineInfo->InlinerCompiler->compBasicBlockID = compBasicBlockID;
}
#endif
@@ -6846,6 +6885,29 @@ void Compiler::GetStructTypeOffset(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSIN
*type1 = GetEightByteType(structDesc, 1);
}
}
+
+//------------------------------------------------------------------------------------------------------
+// GetStructTypeOffset: Gets the type, size and offset of the eightbytes of a struct for System V systems.
+//
+// Arguments:
+// 'typeHnd' - type handle
+// 'type0' - out param; returns the type of the first eightbyte.
+// 'type1' - out param; returns the type of the second eightbyte.
+// 'offset0' - out param; returns the offset of the first eightbyte.
+// 'offset1' - out param; returns the offset of the second eightbyte.
+//
+void Compiler::GetStructTypeOffset(CORINFO_CLASS_HANDLE typeHnd,
+ var_types* type0,
+ var_types* type1,
+ unsigned __int8* offset0,
+ unsigned __int8* offset1)
+{
+ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
+ eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc);
+ assert(structDesc.passedInRegisters);
+ GetStructTypeOffset(structDesc, type0, type1, offset0, offset1);
+}
+
#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
/*****************************************************************************/
@@ -9281,10 +9343,6 @@ int cTreeFlagsIR(Compiler* comp, GenTree* tree)
{
chars += printf("[IND_VOLATILE]");
}
- if (tree->gtFlags & GTF_IND_REFARR_LAYOUT)
- {
- chars += printf("[IND_REFARR_LAYOUT]");
- }
if (tree->gtFlags & GTF_IND_TGTANYWHERE)
{
chars += printf("[IND_TGTANYWHERE]");
@@ -9739,7 +9797,7 @@ int cTreeFlagsIR(Compiler* comp, GenTree* tree)
#endif
if (tree->gtFlags & GTF_IND_NONFAULTING)
{
- if ((op == GT_IND) || (op == GT_STOREIND))
+ if (tree->OperIsIndirOrArrLength())
{
chars += printf("[IND_NONFAULTING]");
}
@@ -10940,7 +10998,7 @@ void cNodeIR(Compiler* comp, GenTree* tree)
GenTree* base = lea->Base();
GenTree* index = lea->Index();
unsigned scale = lea->gtScale;
- unsigned offset = lea->gtOffset;
+ int offset = lea->Offset();
chars += printf(" [");
if (base != nullptr)
@@ -10965,7 +11023,7 @@ void cNodeIR(Compiler* comp, GenTree* tree)
{
chars += printf("+");
}
- chars += printf("%u", offset);
+ chars += printf("%d", offset);
}
chars += printf("]");
break;
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index a8aad0cc89..b4077cf6f5 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -45,6 +45,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "valuenum.h"
#include "reglist.h"
#include "jittelemetry.h"
+#include "namedintrinsiclist.h"
#ifdef LATE_DISASM
#include "disasm.h"
#endif
@@ -722,6 +723,8 @@ public:
return (unsigned)(roundUp(lvExactSize, TARGET_POINTER_SIZE));
}
+ const size_t lvArgStackSize() const;
+
unsigned lvSlotNum; // original slot # (if remapped)
typeInfo lvVerTypeInfo; // type info needed for verification
@@ -2028,7 +2031,7 @@ public:
GenTree* gtNewBlkOpNode(GenTreePtr dst, GenTreePtr srcOrFillVal, unsigned size, bool isVolatile, bool isCopyBlock);
- GenTree* gtNewPutArgReg(var_types type, GenTreePtr arg);
+ GenTree* gtNewPutArgReg(var_types type, GenTreePtr arg, regNumber argReg);
protected:
void gtBlockOpInit(GenTreePtr result, GenTreePtr dst, GenTreePtr srcOrFillVal, bool isVolatile);
@@ -2054,10 +2057,7 @@ public:
GenTreeArgList* args,
IL_OFFSETX ilOffset = BAD_IL_OFFSET);
- GenTreeCall* gtNewHelperCallNode(unsigned helper,
- var_types type,
- unsigned flags = 0,
- GenTreeArgList* args = nullptr);
+ GenTreeCall* gtNewHelperCallNode(unsigned helper, var_types type, GenTreeArgList* args = nullptr);
GenTreePtr gtNewLclvNode(unsigned lnum, var_types type, IL_OFFSETX ILoffs = BAD_IL_OFFSET);
@@ -2084,6 +2084,10 @@ public:
GenTreePtr gtNewIndexRef(var_types typ, GenTreePtr arrayOp, GenTreePtr indexOp);
+ GenTreeArrLen* gtNewArrLen(var_types typ, GenTree* arrayOp, int lenOffset);
+
+ GenTree* gtNewIndir(var_types typ, GenTree* addr);
+
GenTreeArgList* gtNewArgList(GenTreePtr op);
GenTreeArgList* gtNewArgList(GenTreePtr op1, GenTreePtr op2);
GenTreeArgList* gtNewArgList(GenTreePtr op1, GenTreePtr op2, GenTreePtr op3);
@@ -2137,7 +2141,13 @@ public:
GenTreePtr gtReplaceTree(GenTreePtr stmt, GenTreePtr tree, GenTreePtr replacementTree);
- void gtUpdateSideEffects(GenTreePtr tree, unsigned oldGtFlags, unsigned newGtFlags);
+ void gtUpdateSideEffects(GenTree* stmt, GenTree* tree);
+
+ void gtUpdateTreeAncestorsSideEffects(GenTree* tree);
+
+ void gtUpdateStmtSideEffects(GenTree* stmt);
+
+ void gtResetNodeSideEffects(GenTree* tree);
// Returns "true" iff the complexity (not formally defined, but first interpretation
// is #of nodes in subtree) of "tree" is greater than "limit".
@@ -2229,6 +2239,18 @@ public:
GenTreePtr gtFoldExprSpecial(GenTreePtr tree);
GenTreePtr gtFoldExprCompare(GenTreePtr tree);
+ // Options to control behavior of gtTryRemoveBoxUpstreamEffects
+ enum BoxRemovalOptions
+ {
+ BR_REMOVE_AND_NARROW, // remove effects, minimize remaining work, return possibly narrowed source tree
+ BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE, // remove effects and minimize remaining work, return type handle tree
+ BR_REMOVE_BUT_NOT_NARROW, // remove effects, return original source tree
+ BR_DONT_REMOVE // just check if removal is possible
+ };
+
+ GenTree* gtTryRemoveBoxUpstreamEffects(GenTree* tree, BoxRemovalOptions options = BR_REMOVE_AND_NARROW);
+ GenTree* gtOptimizeEnumHasFlag(GenTree* thisOp, GenTree* flagOp);
+
//-------------------------------------------------------------------------
// Get the handle, if any.
CORINFO_CLASS_HANDLE gtGetStructHandleIfPresent(GenTreePtr tree);
@@ -2443,6 +2465,9 @@ public:
DNER_DepField, // It is a field of a dependently promoted struct
DNER_NoRegVars, // opts.compFlags & CLFLG_REGVAR is not set
DNER_MinOptsGC, // It is a GC Ref and we are compiling MinOpts
+#if !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_)
+ DNER_LongParamField, // It is a decomposed field of a long parameter.
+#endif
#ifdef JIT32_GCENCODER
DNER_PinningRef,
#endif
@@ -2761,12 +2786,11 @@ public:
#if defined(_TARGET_64BIT_)
assert(varDsc->lvSize() == 16);
- return true;
-#else // !defined(_TARGET_64BIT_)
+#endif // defined(_TARGET_64BIT_)
- // For 32-bit architectures, we make local variable SIMD12 types 16 bytes instead of just 12. lvSize()
+ // We make local variable SIMD12 types 16 bytes instead of just 12. lvSize()
// already does this calculation. However, we also need to prevent mapping types if the var is a
- // depenendently promoted struct field, which must remain its exact size within its parent struct.
+ // dependently promoted struct field, which must remain its exact size within its parent struct.
// However, we don't know this until late, so we may have already pretended the field is bigger
// before that.
if ((varDsc->lvSize() == 16) && !lvaIsFieldOfDependentlyPromotedStruct(varDsc))
@@ -2777,8 +2801,6 @@ public:
{
return false;
}
-
-#endif // !defined(_TARGET_64BIT_)
}
#endif // defined(FEATURE_SIMD)
@@ -2970,7 +2992,10 @@ protected:
int memberRef,
bool readonlyCall,
bool tailCall,
- CorInfoIntrinsics* pIntrinsicID);
+ bool isJitIntrinsic,
+ CorInfoIntrinsics* pIntrinsicID,
+ bool* isSpecialIntrinsic = nullptr);
+ NamedIntrinsic lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method);
GenTreePtr impArrayAccessIntrinsic(CORINFO_CLASS_HANDLE clsHnd,
CORINFO_SIG_INFO* sig,
int memberRef,
@@ -3135,15 +3160,9 @@ private:
((opcode >= CEE_STLOC_0) && (opcode <= CEE_STLOC_3)));
}
- GenTreeArgList* impPopList(unsigned count,
- unsigned* flagsPtr,
- CORINFO_SIG_INFO* sig,
- GenTreeArgList* prefixTree = nullptr);
+ GenTreeArgList* impPopList(unsigned count, CORINFO_SIG_INFO* sig, GenTreeArgList* prefixTree = nullptr);
- GenTreeArgList* impPopRevList(unsigned count,
- unsigned* flagsPtr,
- CORINFO_SIG_INFO* sig,
- unsigned skipReverseCount = 0);
+ GenTreeArgList* impPopRevList(unsigned count, CORINFO_SIG_INFO* sig, unsigned skipReverseCount = 0);
/*
* Get current IL offset with stack-empty info incoporated
@@ -3617,9 +3636,8 @@ public:
bool fgFuncletsCreated; // true if the funclet creation phase has been run
#endif // FEATURE_EH_FUNCLETS
- bool fgGlobalMorph; // indicates if we are during the global morphing phase
- // since fgMorphTree can be called from several places
- bool fgExpandInline; // indicates that we are creating tree for the inliner
+ bool fgGlobalMorph; // indicates if we are during the global morphing phase
+ // since fgMorphTree can be called from several places
bool impBoxTempInUse; // the temp below is valid and available
unsigned impBoxTemp; // a temporary that is used for boxing
@@ -3655,6 +3673,16 @@ public:
void fgUpdateFinallyTargetFlags();
+ void fgClearAllFinallyTargetBits();
+
+ void fgAddFinallyTargetFlags();
+
+#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+ // Sometimes we need to defer updating the BBF_FINALLY_TARGET bit. fgNeedToAddFinallyTargetBits signals
+ // when this is necessary.
+ bool fgNeedToAddFinallyTargetBits;
+#endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+
bool fgRetargetBranchesToCanonicalCallFinally(BasicBlock* block,
BasicBlock* handler,
BlockToBlockMap& continuationMap);
@@ -3770,6 +3798,16 @@ public:
void fgComputeLifeCall(VARSET_TP& life, GenTreeCall* call);
+ void fgComputeLifeTrackedLocalUse(VARSET_TP& life, LclVarDsc& varDsc, GenTreeLclVarCommon* node);
+ bool fgComputeLifeTrackedLocalDef(VARSET_TP& life,
+ VARSET_VALARG_TP keepAliveVars,
+ LclVarDsc& varDsc,
+ GenTreeLclVarCommon* node);
+ void fgComputeLifeUntrackedLocal(VARSET_TP& life,
+ VARSET_VALARG_TP keepAliveVars,
+ LclVarDsc& varDsc,
+ GenTreeLclVarCommon* lclVarNode,
+ GenTree* node);
bool fgComputeLifeLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, GenTree* lclVarNode, GenTree* node);
void fgComputeLife(VARSET_TP& life,
@@ -3786,8 +3824,6 @@ public:
bool* doAgain,
bool* pStmtInfoDirty DEBUGARG(bool* treeModf));
- bool fgTryRemoveDeadLIRStore(LIR::Range& blockRange, GenTree* node, GenTree** next);
-
// For updating liveset during traversal AFTER fgComputeLife has completed
VARSET_VALRET_TP fgGetVarBits(GenTreePtr tree);
VARSET_VALRET_TP fgUpdateLiveSet(VARSET_VALARG_TP liveSet, GenTreePtr tree);
@@ -3902,6 +3938,9 @@ public:
// Returns "true" iff lcl "lclNum" should be excluded from SSA.
inline bool fgExcludeFromSsa(unsigned lclNum);
+ // Returns "true" if a struct temp of the given type requires needs zero init in this block
+ inline bool fgStructTempNeedsExplicitZeroInit(LclVarDsc* varDsc, BasicBlock* block);
+
// The value numbers for this compilation.
ValueNumStore* vnStore;
@@ -4105,11 +4144,11 @@ public:
void vnPrint(ValueNum vn, unsigned level);
#endif
+ bool fgDominate(BasicBlock* b1, BasicBlock* b2); // Return true if b1 dominates b2
+
// Dominator computation member functions
// Not exposed outside Compiler
protected:
- bool fgDominate(BasicBlock* b1, BasicBlock* b2); // Return true if b1 dominates b2
-
bool fgReachable(BasicBlock* b1, BasicBlock* b2); // Returns true if block b1 can reach block b2
void fgComputeDoms(); // Computes the immediate dominators for each basic block in the
@@ -4472,12 +4511,6 @@ public:
static GenTreePtr fgGetFirstNode(GenTreePtr tree);
static bool fgTreeIsInStmt(GenTree* tree, GenTreeStmt* stmt);
-
- inline bool fgIsInlining()
- {
- return fgExpandInline;
- }
-
void fgTraverseRPO();
//--------------------- Walking the trees in the IR -----------------------
@@ -4662,6 +4695,8 @@ private:
void fgSetRngChkTarget(GenTreePtr tree, bool delay = true);
+ BasicBlock* fgSetRngChkTargetInner(SpecialCodeKind kind, bool delay, unsigned* stkDepth);
+
#if REARRANGE_ADDS
void fgMoveOpsLeft(GenTreePtr tree);
#endif
@@ -4738,7 +4773,11 @@ private:
void fgFixupStructReturn(GenTreePtr call);
GenTreePtr fgMorphLocalVar(GenTreePtr tree, bool forceRemorph);
+
+public:
bool fgAddrCouldBeNull(GenTreePtr addr);
+
+private:
GenTreePtr fgMorphField(GenTreePtr tree, MorphAddrContext* mac);
bool fgCanFastTailCall(GenTreeCall* call);
void fgMorphTailCall(GenTreeCall* call);
@@ -4903,6 +4942,9 @@ private:
void fgMarkAddressExposedLocals();
bool fgNodesMayInterfere(GenTree* store, GenTree* load);
+ static fgWalkPreFn fgUpdateSideEffectsPre;
+ static fgWalkPostFn fgUpdateSideEffectsPost;
+
// Returns true if the type of tree is of size at least "width", or if "tree" is not a
// local variable.
bool fgFitsInOrNotLoc(GenTreePtr tree, unsigned width);
@@ -4946,8 +4988,7 @@ protected:
LclVarDsc* optIsTrackedLocal(GenTreePtr tree);
public:
- void optRemoveRangeCheck(
- GenTreePtr tree, GenTreePtr stmt, bool updateCSEcounts, unsigned sideEffFlags = 0, bool forceRemove = false);
+ void optRemoveRangeCheck(GenTreePtr tree, GenTreePtr stmt);
bool optIsRangeCheckRemovable(GenTreePtr tree);
protected:
@@ -5313,6 +5354,14 @@ public:
LoopDsc optLoopTable[MAX_LOOP_NUM]; // loop descriptor table
unsigned char optLoopCount; // number of tracked loops
+ bool optRecordLoop(BasicBlock* head,
+ BasicBlock* first,
+ BasicBlock* top,
+ BasicBlock* entry,
+ BasicBlock* bottom,
+ BasicBlock* exit,
+ unsigned char exitCnt);
+
protected:
unsigned optCallCount; // number of calls made in the method
unsigned optIndirectCallCount; // number of virtual, interface and indirect calls made in the method
@@ -5356,14 +5405,6 @@ protected:
GenTreePtr* ppTest,
GenTreePtr* ppIncr);
- void optRecordLoop(BasicBlock* head,
- BasicBlock* first,
- BasicBlock* top,
- BasicBlock* entry,
- BasicBlock* bottom,
- BasicBlock* exit,
- unsigned char exitCnt);
-
void optFindNaturalLoops();
// Ensures that all the loops in the loop nest rooted at "loopInd" (an index into the loop table) are 'canonical' --
@@ -6666,8 +6707,16 @@ public:
regMask = RBM_R11;
}
#elif defined(_TARGET_ARM_)
- reg = REG_R4;
- regMask = RBM_R4;
+ if (isCoreRTABI)
+ {
+ reg = REG_R12;
+ regMask = RBM_R12;
+ }
+ else
+ {
+ reg = REG_R4;
+ regMask = RBM_R4;
+ }
#elif defined(_TARGET_ARM64_)
reg = REG_R11;
regMask = RBM_R11;
@@ -8273,7 +8322,14 @@ public:
var_types compRetNativeType; // Normalized return type as per target arch ABI
unsigned compILargsCount; // Number of arguments (incl. implicit but not hidden)
unsigned compArgsCount; // Number of arguments (incl. implicit and hidden)
- unsigned compRetBuffArg; // position of hidden return param var (0, 1) (BAD_VAR_NUM means not present);
+
+#if FEATURE_FASTTAILCALL
+ unsigned compArgRegCount; // Number of incoming integer argument registers used for incoming arguments
+ unsigned compFloatArgRegCount; // Number of incoming floating argument registers used for incoming arguments
+ size_t compArgStackSize; // Incoming argument stack size in bytes
+#endif // FEATURE_FASTTAILCALL
+
+ unsigned compRetBuffArg; // position of hidden return param var (0, 1) (BAD_VAR_NUM means not present);
int compTypeCtxtArg; // position of hidden param for type context for generic code (CORINFO_CALLCONV_PARAMTYPE)
unsigned compThisArg; // position of implicit this pointer param (not to be confused with lvaArg0Var)
unsigned compILlocalsCount; // Number of vars : args + locals (incl. implicit but not hidden)
@@ -8404,6 +8460,7 @@ public:
#ifdef DEBUG
static unsigned s_compMethodsCount; // to produce unique label names
unsigned compGenTreeID;
+ unsigned compBasicBlockID;
#endif
BasicBlock* compCurBB; // the current basic block in process
@@ -9305,6 +9362,37 @@ public:
return compRoot->m_arrayInfoMap;
}
+ //-----------------------------------------------------------------------------------------------------------------
+ // Compiler::TryGetArrayInfo:
+ // Given an indirection node, checks to see whether or not that indirection represents an array access, and
+ // if so returns information about the array.
+ //
+ // Arguments:
+ // indir - The `GT_IND` node.
+ // arrayInfo (out) - Information about the accessed array if this function returns true. Undefined otherwise.
+ //
+ // Returns:
+ // True if the `GT_IND` node represents an array access; false otherwise.
+ inline bool TryGetArrayInfo(GenTreeIndir* indir, ArrayInfo* arrayInfo)
+ {
+ if ((indir->gtFlags & GTF_IND_ARR_INDEX) == 0)
+ {
+ return false;
+ }
+
+ if (indir->gtOp1->OperIs(GT_INDEX_ADDR))
+ {
+ GenTreeIndexAddr* const indexAddr = indir->gtOp1->AsIndexAddr();
+ *arrayInfo = ArrayInfo(indexAddr->gtElemType, indexAddr->gtElemSize, indexAddr->gtElemOffset,
+ indexAddr->gtStructElemClass);
+ return true;
+ }
+
+ bool found = GetArrayInfoMap()->Lookup(indir, arrayInfo);
+ assert(found);
+ return true;
+ }
+
NodeToUnsignedMap* m_memorySsaMap[MemoryKindCount];
// In some cases, we want to assign intermediate SSA #'s to memory states, and know what nodes create those memory
@@ -9361,11 +9449,19 @@ public:
static var_types GetTypeFromClassificationAndSizes(SystemVClassificationType classType, int size);
static var_types GetEightByteType(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc,
unsigned slotNum);
+
static void GetStructTypeOffset(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc,
var_types* type0,
var_types* type1,
unsigned __int8* offset0,
unsigned __int8* offset1);
+
+ void GetStructTypeOffset(CORINFO_CLASS_HANDLE typeHnd,
+ var_types* type0,
+ var_types* type1,
+ unsigned __int8* offset0,
+ unsigned __int8* offset1);
+
void fgMorphSystemVStructArgs(GenTreeCall* call, bool hasStructArgument);
#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp
index 127c56ed94..4a90760ce2 100644
--- a/src/jit/compiler.hpp
+++ b/src/jit/compiler.hpp
@@ -1121,8 +1121,21 @@ inline GenTreePtr Compiler::gtNewIconEmbFldHndNode(CORINFO_FIELD_HANDLE fldHnd,
/*****************************************************************************/
-inline GenTreeCall* Compiler::gtNewHelperCallNode(unsigned helper, var_types type, unsigned flags, GenTreeArgList* args)
+//------------------------------------------------------------------------------
+// gtNewHelperCallNode : Helper to create a call helper node.
+//
+//
+// Arguments:
+// helper - Call helper
+// type - Type of the node
+// args - Call args
+//
+// Return Value:
+// New CT_HELPER node
+
+inline GenTreeCall* Compiler::gtNewHelperCallNode(unsigned helper, var_types type, GenTreeArgList* args)
{
+ unsigned flags = s_helperCallProperties.NoThrow((CorInfoHelpFunc)helper) ? 0 : GTF_EXCEPT;
GenTreeCall* result = gtNewCallNode(CT_HELPER, eeFindHelper(helper), type, args);
result->gtFlags |= flags;
@@ -1228,6 +1241,43 @@ inline GenTreePtr Compiler::gtNewIndexRef(var_types typ, GenTreePtr arrayOp, Gen
return gtIndx;
}
+//------------------------------------------------------------------------------
+// gtNewArrLen : Helper to create an array length node.
+//
+//
+// Arguments:
+// typ - Type of the node
+// arrayOp - Array node
+// lenOffset - Offset of the length field
+//
+// Return Value:
+// New GT_ARR_LENGTH node
+
+inline GenTreeArrLen* Compiler::gtNewArrLen(var_types typ, GenTree* arrayOp, int lenOffset)
+{
+ GenTreeArrLen* arrLen = new (this, GT_ARR_LENGTH) GenTreeArrLen(typ, arrayOp, lenOffset);
+ static_assert_no_msg(GTF_ARRLEN_NONFAULTING == GTF_IND_NONFAULTING);
+ arrLen->SetIndirExceptionFlags(this);
+ return arrLen;
+}
+
+//------------------------------------------------------------------------------
+// gtNewIndir : Helper to create an indirection node.
+//
+// Arguments:
+// typ - Type of the node
+// addr - Address of the indirection
+//
+// Return Value:
+// New GT_IND node
+
+inline GenTree* Compiler::gtNewIndir(var_types typ, GenTree* addr)
+{
+ GenTree* indir = gtNewOperNode(GT_IND, typ, addr);
+ indir->SetIndirExceptionFlags(this);
+ return indir;
+}
+
/*****************************************************************************
*
* Create (and check for) a "nothing" node, i.e. a node that doesn't produce
@@ -1512,8 +1562,13 @@ inline void GenTree::ChangeOper(genTreeOps oper, ValueNumberUpdate vnUpdate)
{
assert(!OperIsConst(oper)); // use ChangeOperLeaf() instead
+ unsigned mask = GTF_COMMON_MASK;
+ if (this->OperIsIndirOrArrLength() && OperIsIndirOrArrLength(oper))
+ {
+ mask |= GTF_IND_NONFAULTING;
+ }
SetOper(oper, vnUpdate);
- gtFlags &= GTF_COMMON_MASK;
+ gtFlags &= mask;
// Do "oper"-specific initializations...
switch (oper)
@@ -1529,8 +1584,13 @@ inline void GenTree::ChangeOper(genTreeOps oper, ValueNumberUpdate vnUpdate)
inline void GenTree::ChangeOperUnchecked(genTreeOps oper)
{
+ unsigned mask = GTF_COMMON_MASK;
+ if (this->OperIsIndirOrArrLength() && OperIsIndirOrArrLength(oper))
+ {
+ mask |= GTF_IND_NONFAULTING;
+ }
SetOperRaw(oper); // Trust the caller and don't use SetOper()
- gtFlags &= GTF_COMMON_MASK;
+ gtFlags &= mask;
}
/*****************************************************************************
@@ -3020,6 +3080,35 @@ inline unsigned Compiler::fgThrowHlpBlkStkLevel(BasicBlock* block)
*/
inline void Compiler::fgConvertBBToThrowBB(BasicBlock* block)
{
+ // If we're converting a BBJ_CALLFINALLY block to a BBJ_THROW block,
+ // then mark the subsequent BBJ_ALWAYS block as unreferenced.
+ if (block->isBBCallAlwaysPair())
+ {
+ BasicBlock* leaveBlk = block->bbNext;
+ noway_assert(leaveBlk->bbJumpKind == BBJ_ALWAYS);
+
+ leaveBlk->bbFlags &= ~BBF_DONT_REMOVE;
+ leaveBlk->bbRefs = 0;
+ leaveBlk->bbPreds = nullptr;
+
+#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+ // This function (fgConvertBBToThrowBB) can be called before the predecessor lists are created (e.g., in
+ // fgMorph). The fgClearFinallyTargetBit() function to update the BBF_FINALLY_TARGET bit depends on these
+ // predecessor lists. If there are no predecessor lists, we immediately clear all BBF_FINALLY_TARGET bits
+ // (to allow subsequent dead code elimination to delete such blocks without asserts), and set a flag to
+ // recompute them later, before they are required.
+ if (fgComputePredsDone)
+ {
+ fgClearFinallyTargetBit(leaveBlk->bbJumpDest);
+ }
+ else
+ {
+ fgClearAllFinallyTargetBits();
+ fgNeedToAddFinallyTargetBits = true;
+ }
+#endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+ }
+
block->bbJumpKind = BBJ_THROW;
block->bbSetRunRarely(); // any block with a throw is rare
}
@@ -4622,6 +4711,28 @@ inline void Compiler::CLR_API_Leave(API_ICorJitInfo_Names ename)
#endif // MEASURE_CLRAPI_CALLS
+//------------------------------------------------------------------------------
+// fgStructTempNeedsExplicitZeroInit : Check whether temp struct needs
+// explicit zero initialization in this basic block.
+//
+// Arguments:
+// varDsc - struct local var description
+// block - basic block to check
+//
+// Returns:
+// true if the struct temp needs explicit zero-initialization in this basic block;
+// false otherwise
+//
+// Notes:
+// Structs with GC pointer fields are fully zero-initialized in the prolog if compInitMem is true.
+// Therefore, we don't need to insert zero-initialization if this block is not in a loop.
+
+bool Compiler::fgStructTempNeedsExplicitZeroInit(LclVarDsc* varDsc, BasicBlock* block)
+{
+ bool containsGCPtr = (varDsc->lvStructGcCount > 0);
+ return (!containsGCPtr || !info.compInitMem || ((block->bbFlags & BBF_BACKWARD_JUMP) != 0));
+}
+
/*****************************************************************************/
bool Compiler::fgExcludeFromSsa(unsigned lclNum)
{
@@ -5000,20 +5111,15 @@ void GenTree::VisitBinOpOperands(TVisitor visitor)
*
* Note that compGetMem is an arena allocator that returns memory that is
* not zero-initialized and can contain data from a prior allocation lifetime.
- * it also requires that 'sz' be aligned to a multiple of sizeof(int)
*/
inline void* __cdecl operator new(size_t sz, Compiler* context, CompMemKind cmk)
{
- sz = AlignUp(sz, sizeof(int));
- assert(sz != 0 && (sz & (sizeof(int) - 1)) == 0);
return context->compGetMem(sz, cmk);
}
inline void* __cdecl operator new[](size_t sz, Compiler* context, CompMemKind cmk)
{
- sz = AlignUp(sz, sizeof(int));
- assert(sz != 0 && (sz & (sizeof(int) - 1)) == 0);
return context->compGetMem(sz, cmk);
}
diff --git a/src/jit/copyprop.cpp b/src/jit/copyprop.cpp
index 773976e33e..75fc5c48ef 100644
--- a/src/jit/copyprop.cpp
+++ b/src/jit/copyprop.cpp
@@ -110,13 +110,17 @@ int Compiler::optCopyProp_LclVarScore(LclVarDsc* lclVarDsc, LclVarDsc* copyVarDs
return score + ((preferOp2) ? 1 : -1);
}
-/**************************************************************************************
- *
- * Perform copy propagation on a given tree as we walk the graph and if it is a local
- * variable, then look up all currently live definitions and check if any of those
- * definitions share the same value number. If so, then we can make the replacement.
- *
- */
+//------------------------------------------------------------------------------
+// optCopyProp : Perform copy propagation on a given tree as we walk the graph and if it is a local
+// variable, then look up all currently live definitions and check if any of those
+// definitions share the same value number. If so, then we can make the replacement.
+//
+// Arguments:
+// block - Block the tree belongs to
+// stmt - Statement the tree belongs to
+// tree - The tree to perform copy propagation on
+// curSsaName - The map from lclNum to its recently live definitions as a stack
+
void Compiler::optCopyProp(BasicBlock* block, GenTreePtr stmt, GenTreePtr tree, LclNumToGenTreePtrStack* curSsaName)
{
// TODO-Review: EH successor/predecessor iteration seems broken.
@@ -259,6 +263,7 @@ void Compiler::optCopyProp(BasicBlock* block, GenTreePtr stmt, GenTreePtr tree,
lvaTable[newLclNum].incRefCnts(block->getBBWeight(this), this);
tree->gtLclVarCommon.SetLclNum(newLclNum);
tree->AsLclVarCommon()->SetSsaNum(newSsaNum);
+ gtUpdateSideEffects(stmt, tree);
#ifdef DEBUG
if (verbose)
{
@@ -280,13 +285,15 @@ bool Compiler::optIsSsaLocal(GenTreePtr tree)
return tree->IsLocal() && !fgExcludeFromSsa(tree->AsLclVarCommon()->GetLclNum());
}
-/**************************************************************************************
- *
- * Perform copy propagation using currently live definitions on the current block's
- * variables. Also as new definitions are encountered update the "curSsaName" which
- * tracks the currently live definitions.
- *
- */
+//------------------------------------------------------------------------------
+// optBlockCopyProp : Perform copy propagation using currently live definitions on the current block's
+// variables. Also as new definitions are encountered update the "curSsaName" which
+// tracks the currently live definitions.
+//
+// Arguments:
+// block - Block the tree belongs to
+// curSsaName - The map from lclNum to its recently live definitions as a stack
+
void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curSsaName)
{
JITDUMP("Copy Assertion for BB%02u\n", block->bbNum);
@@ -302,6 +309,7 @@ void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* curS
for (GenTreePtr tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext)
{
compUpdateLife</*ForCodeGen*/ false>(tree);
+
optCopyProp(block, stmt, tree, curSsaName);
// TODO-Review: Merge this loop with the following loop to correctly update the
diff --git a/src/jit/decomposelongs.cpp b/src/jit/decomposelongs.cpp
index 96d3b0bb71..174bba2a56 100644
--- a/src/jit/decomposelongs.cpp
+++ b/src/jit/decomposelongs.cpp
@@ -607,7 +607,8 @@ GenTree* DecomposeLongs::DecomposeCast(LIR::Use& use)
// check provided by codegen.
//
- loResult = loSrcOp;
+ const bool signExtend = (cast->gtFlags & GTF_UNSIGNED) == 0;
+ loResult = EnsureIntSized(loSrcOp, signExtend);
hiResult = cast;
hiResult->gtType = TYP_INT;
@@ -657,7 +658,9 @@ GenTree* DecomposeLongs::DecomposeCast(LIR::Use& use)
}
else if (varTypeIsUnsigned(srcType))
{
- loResult = cast->gtGetOp1();
+ const bool signExtend = (cast->gtFlags & GTF_UNSIGNED) == 0;
+ loResult = EnsureIntSized(cast->gtGetOp1(), signExtend);
+
hiResult = m_compiler->gtNewZeroConNode(TYP_INT);
Range().InsertAfter(cast, hiResult);
@@ -989,10 +992,13 @@ GenTree* DecomposeLongs::DecomposeArith(LIR::Use& use)
if ((oper == GT_ADD) || (oper == GT_SUB))
{
+ loResult->gtFlags |= GTF_SET_FLAGS;
+ hiResult->gtFlags |= GTF_USE_FLAGS;
+
if (loResult->gtOverflow())
{
- hiResult->gtFlags |= GTF_OVERFLOW;
- loResult->gtFlags &= ~GTF_OVERFLOW;
+ hiResult->gtFlags |= GTF_OVERFLOW | GTF_EXCEPT;
+ loResult->gtFlags &= ~(GTF_OVERFLOW | GTF_EXCEPT);
}
if (loResult->gtFlags & GTF_UNSIGNED)
{
@@ -1423,7 +1429,7 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use)
GenTreeArgList* argList = m_compiler->gtNewArgList(loOp1, hiOp1, shiftByOp);
- GenTree* call = m_compiler->gtNewHelperCallNode(helper, TYP_LONG, 0, argList);
+ GenTree* call = m_compiler->gtNewHelperCallNode(helper, TYP_LONG, argList);
call->gtFlags |= shift->gtFlags & GTF_ALL_EFFECT;
if (shift->IsUnusedValue())
@@ -1918,6 +1924,43 @@ GenTree* DecomposeLongs::RepresentOpAsLocalVar(GenTree* op, GenTree* user, GenTr
}
//------------------------------------------------------------------------
+// DecomposeLongs::EnsureIntSized:
+// Checks to see if the given node produces an int-sized value and
+// performs the appropriate widening if it does not.
+//
+// Arguments:
+// node - The node that may need to be widened.
+// signExtend - True if the value should be sign-extended; false if it
+// should be zero-extended.
+//
+// Return Value:
+// The node that produces the widened value.
+GenTree* DecomposeLongs::EnsureIntSized(GenTree* node, bool signExtend)
+{
+ assert(node != nullptr);
+ if (!varTypeIsSmall(node))
+ {
+ assert(genTypeSize(node) == genTypeSize(TYP_INT));
+ return node;
+ }
+
+ if (node->OperIs(GT_LCL_VAR) && !m_compiler->lvaTable[node->AsLclVarCommon()->gtLclNum].lvNormalizeOnLoad())
+ {
+ node->gtType = TYP_INT;
+ return node;
+ }
+
+ GenTree* const cast = m_compiler->gtNewCastNode(TYP_INT, node, node->TypeGet());
+ if (!signExtend)
+ {
+ cast->gtFlags |= GTF_UNSIGNED;
+ }
+
+ Range().InsertAfter(node, cast);
+ return cast;
+}
+
+//------------------------------------------------------------------------
// GetHiOper: Convert arithmetic operator to "high half" operator of decomposed node.
//
// Arguments:
diff --git a/src/jit/decomposelongs.h b/src/jit/decomposelongs.h
index ff4f4ac880..7a0d6ff5ba 100644
--- a/src/jit/decomposelongs.h
+++ b/src/jit/decomposelongs.h
@@ -61,6 +61,7 @@ private:
// Helper functions
GenTree* FinalizeDecomposition(LIR::Use& use, GenTree* loResult, GenTree* hiResult, GenTree* insertResultAfter);
GenTree* RepresentOpAsLocalVar(GenTree* op, GenTree* user, GenTree** edge);
+ GenTree* EnsureIntSized(GenTree* node, bool signExtend);
GenTree* StoreNodeToVar(LIR::Use& use);
static genTreeOps GetHiOper(genTreeOps oper);
diff --git a/src/jit/earlyprop.cpp b/src/jit/earlyprop.cpp
index b2f3050f42..d4d0e3ee41 100644
--- a/src/jit/earlyprop.cpp
+++ b/src/jit/earlyprop.cpp
@@ -196,11 +196,12 @@ void Compiler::optEarlyProp()
{
if (optEarlyPropRewriteTree(tree))
{
+ gtUpdateSideEffects(stmt, tree);
isRewritten = true;
}
}
- // Morph the stmt and update the evaluation order if the stmt has been rewritten.
+ // Update the evaluation order and the statement info if the stmt has been rewritten.
if (isRewritten)
{
gtSetStmtInfo(stmt);
@@ -611,6 +612,7 @@ void Compiler::optFoldNullCheck(GenTreePtr tree)
// Set this flag to prevent reordering
nullCheckTree->gtFlags |= GTF_ORDER_SIDEEFF;
+ nullCheckTree->gtFlags |= GTF_IND_NONFAULTING;
defRHS->gtFlags &= ~(GTF_EXCEPT | GTF_DONT_CSE);
defRHS->gtFlags |=
diff --git a/src/jit/ee_il_dll.cpp b/src/jit/ee_il_dll.cpp
index 553a9b8f13..b32d59fc51 100644
--- a/src/jit/ee_il_dll.cpp
+++ b/src/jit/ee_il_dll.cpp
@@ -58,6 +58,20 @@ extern "C" void __stdcall jitStartup(ICorJitHost* jitHost)
{
if (g_jitInitialized)
{
+ if (jitHost != g_jitHost)
+ {
+ // We normally don't expect jitStartup() to be invoked more than once.
+ // (We check whether it has been called once due to an abundance of caution.)
+ // However, during SuperPMI playback of MCH file, we need to JIT many different methods.
+ // Each one carries its own environment configuration state.
+ // So, we need the JIT to reload the JitConfig state for each change in the environment state of the
+ // replayed compilations.
+ // We do this by calling jitStartup with a different ICorJitHost,
+ // and have the JIT re-initialize its JitConfig state when this happens.
+ JitConfig.destroy(g_jitHost);
+ JitConfig.initialize(jitHost);
+ g_jitHost = jitHost;
+ }
return;
}
diff --git a/src/jit/emit.cpp b/src/jit/emit.cpp
index bad0528bd6..f579aee5ba 100644
--- a/src/jit/emit.cpp
+++ b/src/jit/emit.cpp
@@ -3221,8 +3221,11 @@ void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose)
{
printf("<END>");
}
- printf(", BB=%08XH (BB%02u)", dspPtr(igPh->igPhData->igPhBB),
- (igPh->igPhData->igPhBB != nullptr) ? igPh->igPhData->igPhBB->bbNum : 0);
+
+ if (igPh->igPhData->igPhBB != nullptr)
+ {
+ printf(", %s", igPh->igPhData->igPhBB->dspToString());
+ }
emitDispIGflags(igPh->igFlags);
diff --git a/src/jit/emit.h b/src/jit/emit.h
index cfad5c60d7..a9dc076958 100644
--- a/src/jit/emit.h
+++ b/src/jit/emit.h
@@ -1718,7 +1718,9 @@ private:
CORINFO_FIELD_HANDLE emitFltOrDblConst(GenTreeDblCon* tree, emitAttr attr = EA_UNKNOWN);
regNumber emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src);
regNumber emitInsTernary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src1, GenTree* src2);
- void emitInsMov(instruction ins, emitAttr attr, GenTree* node);
+ void emitInsLoadInd(instruction ins, emitAttr attr, regNumber dstReg, GenTreeIndir* mem);
+ void emitInsStoreInd(instruction ins, emitAttr attr, GenTreeStoreInd* mem);
+ void emitInsStoreLcl(instruction ins, emitAttr attr, GenTreeLclVarCommon* varNode);
insFormat emitMapFmtForIns(insFormat fmt, instruction ins);
insFormat emitMapFmtAtoM(insFormat fmt);
void emitHandleMemOp(GenTreeIndir* indir, instrDesc* id, insFormat fmt, instruction ins);
diff --git a/src/jit/emitarm.cpp b/src/jit/emitarm.cpp
index a0c7196ecb..c21f7aeb3e 100644
--- a/src/jit/emitarm.cpp
+++ b/src/jit/emitarm.cpp
@@ -2459,6 +2459,16 @@ void emitter::emitIns_R_R_I(instruction ins,
fmt = IF_T2_M0;
sf = INS_FLAGS_NOT_SET;
}
+ else if (insDoesNotSetFlags(flags) && (reg1 != REG_SP) && (reg1 != REG_PC))
+ {
+ // movw,movt reg1, imm
+ codeGen->instGen_Set_Reg_To_Imm(attr, reg1, (ins == INS_sub ? -1 : 1) * imm);
+
+ // ins reg1, reg2
+ emitIns_R_R(INS_add, attr, reg1, reg2);
+
+ return;
+ }
else
{
assert(!"Instruction cannot be encoded");
@@ -7619,7 +7629,7 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR
if (addr->OperGet() == GT_LEA)
{
- offset += (int)addr->AsAddrMode()->gtOffset;
+ offset += addr->AsAddrMode()->Offset();
if (addr->AsAddrMode()->gtScale > 0)
{
assert(isPow2(addr->AsAddrMode()->gtScale));
diff --git a/src/jit/emitarm64.cpp b/src/jit/emitarm64.cpp
index 9943dc293e..6c9d61ca25 100644
--- a/src/jit/emitarm64.cpp
+++ b/src/jit/emitarm64.cpp
@@ -11082,7 +11082,7 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR
if (addr->OperGet() == GT_LEA)
{
- offset = (int)addr->AsAddrMode()->gtOffset;
+ offset = addr->AsAddrMode()->Offset();
if (addr->AsAddrMode()->gtScale > 0)
{
assert(isPow2(addr->AsAddrMode()->gtScale));
diff --git a/src/jit/emitxarch.cpp b/src/jit/emitxarch.cpp
index fe20e9e297..a24a778b2c 100644
--- a/src/jit/emitxarch.cpp
+++ b/src/jit/emitxarch.cpp
@@ -1544,14 +1544,15 @@ inline UNATIVE_OFFSET emitter::emitInsSizeRR(instruction ins, regNumber reg1, re
// If Byte 4 (which is 0xFF00) is zero, that's where the RM encoding goes.
// Otherwise, it will be placed after the 4 byte encoding, making the total 5 bytes.
// This would probably be better expressed as a different format or something?
- if ((insCodeRM(ins) & 0xFF00) != 0)
+ code_t code = insCodeRM(ins);
+
+ if ((code & 0xFF00) != 0)
{
sz = 5;
}
else
{
- code_t code = insCodeRM(ins);
- sz = emitInsSize(insEncodeRMreg(ins, code));
+ sz = emitInsSize(insEncodeRMreg(ins, code));
}
// Most 16-bit operand instructions will need a prefix
@@ -1564,10 +1565,13 @@ inline UNATIVE_OFFSET emitter::emitInsSizeRR(instruction ins, regNumber reg1, re
sz += emitGetVexPrefixAdjustedSize(ins, size, insCodeRM(ins));
// REX prefix
- if ((TakesRexWPrefix(ins, size) && ((ins != INS_xor) || (reg1 != reg2))) || IsExtendedReg(reg1, attr) ||
- IsExtendedReg(reg2, attr))
+ if (!hasRexPrefix(code))
{
- sz += emitGetRexPrefixSize(ins);
+ if ((TakesRexWPrefix(ins, size) && ((ins != INS_xor) || (reg1 != reg2))) || IsExtendedReg(reg1, attr) ||
+ IsExtendedReg(reg2, attr))
+ {
+ sz += emitGetRexPrefixSize(ins);
+ }
}
return sz;
@@ -1902,7 +1906,6 @@ UNATIVE_OFFSET emitter::emitInsSizeAM(instrDesc* id, code_t code)
assert((attrSize == EA_4BYTE) || (attrSize == EA_PTRSIZE) // Only for x64
|| (attrSize == EA_16BYTE) // only for x64
|| (ins == INS_movzx) || (ins == INS_movsx));
-
size = 3;
}
else
@@ -1935,7 +1938,8 @@ UNATIVE_OFFSET emitter::emitInsSizeAM(instrDesc* id, code_t code)
// REX.W prefix
size += emitGetRexPrefixSize(ins);
}
- else if (IsExtendedReg(reg, EA_PTRSIZE) || IsExtendedReg(rgx, EA_PTRSIZE) || IsExtendedReg(id->idReg1(), attrSize))
+ else if (IsExtendedReg(reg, EA_PTRSIZE) || IsExtendedReg(rgx, EA_PTRSIZE) ||
+ ((ins != INS_call) && IsExtendedReg(id->idReg1(), attrSize)))
{
// Should have a REX byte
size += emitGetRexPrefixSize(ins);
@@ -1961,6 +1965,14 @@ UNATIVE_OFFSET emitter::emitInsSizeAM(instrDesc* id, code_t code)
return size;
}
+ // If this is just "call reg", we're done.
+ if (id->idIsCallRegPtr())
+ {
+ assert(ins == INS_call);
+ assert(dsp == 0);
+ return size;
+ }
+
// If the base register is ESP (or R12 on 64-bit systems), a SIB byte must be used.
if (baseRegisterRequiresSibByte(reg))
{
@@ -2599,7 +2611,7 @@ void emitter::emitHandleMemOp(GenTreeIndir* indir, instrDesc* id, insFormat fmt,
id->idInsFmt(emitMapFmtForIns(fmt, ins));
// disp must have already been set in the instrDesc constructor.
- assert(emitGetInsAmdAny(id) == ssize_t(indir->Offset())); // make sure "disp" is stored properly
+ assert(emitGetInsAmdAny(id) == indir->Offset()); // make sure "disp" is stored properly
}
}
@@ -2639,132 +2651,151 @@ void emitter::spillIntArgRegsToShadowSlots()
}
}
-// this is very similar to emitInsBinary and probably could be folded in to same
-// except the requirements on the incoming parameter are different,
-// ex: the memory op in storeind case must NOT be contained
-void emitter::emitInsMov(instruction ins, emitAttr attr, GenTree* node)
+//------------------------------------------------------------------------
+// emitInsLoadInd: Emits a "mov reg, [mem]" (or a variant such as "movzx" or "movss")
+// instruction for a GT_IND node.
+//
+// Arguments:
+// ins - the instruction to emit
+// attr - the instruction operand size
+// dstReg - the destination register
+// mem - the GT_IND node
+//
+void emitter::emitInsLoadInd(instruction ins, emitAttr attr, regNumber dstReg, GenTreeIndir* mem)
{
- UNATIVE_OFFSET sz;
- instrDesc* id;
+ assert(mem->OperIs(GT_IND));
- switch (node->OperGet())
+ GenTree* addr = mem->Addr();
+
+ if (addr->OperGet() == GT_CLS_VAR_ADDR)
{
- case GT_IND:
- {
- GenTreeIndir* mem = node->AsIndir();
- GenTreePtr addr = mem->Addr();
+ emitIns_R_C(ins, attr, dstReg, addr->gtClsVar.gtClsVarHnd, 0);
+ return;
+ }
- if (addr->OperGet() == GT_CLS_VAR_ADDR)
- {
- emitIns_R_C(ins, attr, mem->gtRegNum, addr->gtClsVar.gtClsVarHnd, 0);
- return;
- }
- else if (addr->OperGet() == GT_LCL_VAR_ADDR)
- {
- GenTreeLclVarCommon* varNode = addr->AsLclVarCommon();
- emitIns_R_S(ins, attr, mem->gtRegNum, varNode->GetLclNum(), 0);
- codeGen->genUpdateLife(varNode);
- return;
- }
- else
- {
- assert(addr->OperIsAddrMode() || (addr->IsCnsIntOrI() && addr->isContained()) || !addr->isContained());
- size_t offset = mem->Offset();
- id = emitNewInstrAmd(attr, offset);
- id->idIns(ins);
- id->idReg1(mem->gtRegNum);
- emitHandleMemOp(mem, id, IF_RWR_ARD, ins);
- sz = emitInsSizeAM(id, insCodeRM(ins));
- id->idCodeSize(sz);
- }
- }
- break;
+ if (addr->OperGet() == GT_LCL_VAR_ADDR)
+ {
+ GenTreeLclVarCommon* varNode = addr->AsLclVarCommon();
+ emitIns_R_S(ins, attr, dstReg, varNode->GetLclNum(), 0);
+ codeGen->genUpdateLife(varNode);
+ return;
+ }
- case GT_STOREIND:
- {
- GenTreeStoreInd* mem = node->AsStoreInd();
- GenTreePtr addr = mem->Addr();
- size_t offset = mem->Offset();
- GenTree* data = mem->Data();
+ assert(addr->OperIsAddrMode() || (addr->IsCnsIntOrI() && addr->isContained()) || !addr->isContained());
+ ssize_t offset = mem->Offset();
+ instrDesc* id = emitNewInstrAmd(attr, offset);
+ id->idIns(ins);
+ id->idReg1(dstReg);
+ emitHandleMemOp(mem, id, IF_RWR_ARD, ins);
+ UNATIVE_OFFSET sz = emitInsSizeAM(id, insCodeRM(ins));
+ id->idCodeSize(sz);
+ dispIns(id);
+ emitCurIGsize += sz;
+}
- if (addr->OperGet() == GT_CLS_VAR_ADDR)
- {
- if (data->isContainedIntOrIImmed())
- {
- emitIns_C_I(ins, attr, addr->gtClsVar.gtClsVarHnd, 0, (int)data->AsIntConCommon()->IconValue());
- }
- else
- {
- assert(!data->isContained());
- emitIns_C_R(ins, attr, addr->gtClsVar.gtClsVarHnd, data->gtRegNum, 0);
- }
- return;
- }
- else if (addr->OperGet() == GT_LCL_VAR_ADDR)
- {
- GenTreeLclVarCommon* varNode = addr->AsLclVarCommon();
- if (data->isContainedIntOrIImmed())
- {
- emitIns_S_I(ins, attr, varNode->GetLclNum(), 0, (int)data->AsIntConCommon()->IconValue());
- }
- else
- {
- assert(!data->isContained());
- emitIns_S_R(ins, attr, data->gtRegNum, varNode->GetLclNum(), 0);
- }
- codeGen->genUpdateLife(varNode);
- return;
- }
- else if (data->isContainedIntOrIImmed())
- {
- int icon = (int)data->AsIntConCommon()->IconValue();
- id = emitNewInstrAmdCns(attr, offset, icon);
- id->idIns(ins);
- emitHandleMemOp(mem, id, IF_AWR_CNS, ins);
- sz = emitInsSizeAM(id, insCodeMI(ins), icon);
- id->idCodeSize(sz);
- }
- else
- {
- assert(!data->isContained());
- id = emitNewInstrAmd(attr, offset);
- id->idIns(ins);
- emitHandleMemOp(mem, id, IF_AWR_RRD, ins);
- id->idReg1(data->gtRegNum);
- sz = emitInsSizeAM(id, insCodeMR(ins));
- id->idCodeSize(sz);
- }
- }
- break;
+//------------------------------------------------------------------------
+// emitInsStoreInd: Emits a "mov [mem], reg/imm" (or a variant such as "movss")
+// instruction for a GT_STOREIND node.
+//
+// Arguments:
+// ins - the instruction to emit
+// attr - the instruction operand size
+// mem - the GT_STOREIND node
+//
+void emitter::emitInsStoreInd(instruction ins, emitAttr attr, GenTreeStoreInd* mem)
+{
+ assert(mem->OperIs(GT_STOREIND));
+
+ GenTree* addr = mem->Addr();
+ GenTree* data = mem->Data();
- case GT_STORE_LCL_VAR:
+ if (addr->OperGet() == GT_CLS_VAR_ADDR)
+ {
+ if (data->isContainedIntOrIImmed())
+ {
+ emitIns_C_I(ins, attr, addr->gtClsVar.gtClsVarHnd, 0, (int)data->AsIntConCommon()->IconValue());
+ }
+ else
{
- GenTreeLclVarCommon* varNode = node->AsLclVarCommon();
- GenTree* data = varNode->gtOp.gtOp1;
- codeGen->inst_set_SV_var(varNode);
- assert(varNode->gtRegNum == REG_NA); // stack store
+ assert(!data->isContained());
+ emitIns_C_R(ins, attr, addr->gtClsVar.gtClsVarHnd, data->gtRegNum, 0);
+ }
+ return;
+ }
- if (data->isContainedIntOrIImmed())
- {
- emitIns_S_I(ins, attr, varNode->GetLclNum(), 0, (int)data->AsIntConCommon()->IconValue());
- }
- else
- {
- assert(!data->isContained());
- emitIns_S_R(ins, attr, data->gtRegNum, varNode->GetLclNum(), 0);
- }
- codeGen->genUpdateLife(varNode);
+ if (addr->OperGet() == GT_LCL_VAR_ADDR)
+ {
+ GenTreeLclVarCommon* varNode = addr->AsLclVarCommon();
+ if (data->isContainedIntOrIImmed())
+ {
+ emitIns_S_I(ins, attr, varNode->GetLclNum(), 0, (int)data->AsIntConCommon()->IconValue());
}
- return;
+ else
+ {
+ assert(!data->isContained());
+ emitIns_S_R(ins, attr, data->gtRegNum, varNode->GetLclNum(), 0);
+ }
+ codeGen->genUpdateLife(varNode);
+ return;
+ }
- default:
- unreached();
+ ssize_t offset = mem->Offset();
+ UNATIVE_OFFSET sz;
+ instrDesc* id;
+
+ if (data->isContainedIntOrIImmed())
+ {
+ int icon = (int)data->AsIntConCommon()->IconValue();
+ id = emitNewInstrAmdCns(attr, offset, icon);
+ id->idIns(ins);
+ emitHandleMemOp(mem, id, IF_AWR_CNS, ins);
+ sz = emitInsSizeAM(id, insCodeMI(ins), icon);
+ id->idCodeSize(sz);
+ }
+ else
+ {
+ assert(!data->isContained());
+ id = emitNewInstrAmd(attr, offset);
+ id->idIns(ins);
+ emitHandleMemOp(mem, id, IF_AWR_RRD, ins);
+ id->idReg1(data->gtRegNum);
+ sz = emitInsSizeAM(id, insCodeMR(ins));
+ id->idCodeSize(sz);
}
dispIns(id);
emitCurIGsize += sz;
}
+//------------------------------------------------------------------------
+// emitInsStoreLcl: Emits a "mov [mem], reg/imm" (or a variant such as "movss")
+// instruction for a GT_STORE_LCL_VAR node.
+//
+// Arguments:
+// ins - the instruction to emit
+// attr - the instruction operand size
+// varNode - the GT_STORE_LCL_VAR node
+//
+void emitter::emitInsStoreLcl(instruction ins, emitAttr attr, GenTreeLclVarCommon* varNode)
+{
+ assert(varNode->OperIs(GT_STORE_LCL_VAR));
+ assert(varNode->gtRegNum == REG_NA); // stack store
+
+ GenTree* data = varNode->gtGetOp1();
+ codeGen->inst_set_SV_var(varNode);
+
+ if (data->isContainedIntOrIImmed())
+ {
+ emitIns_S_I(ins, attr, varNode->GetLclNum(), 0, (int)data->AsIntConCommon()->IconValue());
+ }
+ else
+ {
+ assert(!data->isContained());
+ emitIns_S_R(ins, attr, data->gtRegNum, varNode->GetLclNum(), 0);
+ }
+ codeGen->genUpdateLife(varNode);
+}
+
CORINFO_FIELD_HANDLE emitter::emitLiteralConst(ssize_t cnsValIn, emitAttr attr /*= EA_8BYTE*/)
{
NYI("emitLiteralConst");
@@ -3082,8 +3113,8 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
}
else // [mem], reg OR reg, [mem]
{
- size_t offset = mem->Offset();
- id = emitNewInstrAmd(attr, offset);
+ ssize_t offset = mem->Offset();
+ id = emitNewInstrAmd(attr, offset);
id->idIns(ins);
GenTree* regTree = (src == mem) ? dst : src;
@@ -3199,7 +3230,7 @@ void emitter::emitInsRMW(instruction ins, emitAttr attr, GenTreeStoreInd* storeI
instrDesc* id = nullptr;
UNATIVE_OFFSET sz;
- size_t offset = 0;
+ ssize_t offset = 0;
if (addr->OperGet() != GT_CLS_VAR_ADDR)
{
offset = storeInd->Offset();
@@ -3260,7 +3291,7 @@ void emitter::emitInsRMW(instruction ins, emitAttr attr, GenTreeStoreInd* storeI
assert(addr->OperGet() == GT_LCL_VAR || addr->OperGet() == GT_LCL_VAR_ADDR || addr->OperGet() == GT_CLS_VAR_ADDR ||
addr->OperGet() == GT_LEA || addr->OperGet() == GT_CNS_INT);
- size_t offset = 0;
+ ssize_t offset = 0;
if (addr->OperGet() != GT_CLS_VAR_ADDR)
{
offset = storeInd->Offset();
@@ -3498,6 +3529,10 @@ void emitter::emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t
{
sz = 5;
}
+ else if (size == EA_1BYTE && reg == REG_EAX && !instrIs3opImul(ins))
+ {
+ sz = 2;
+ }
else
{
sz = 3;
@@ -4168,13 +4203,15 @@ void emitter::emitIns_R_L(instruction ins, emitAttr attr, BasicBlock* dst, regNu
emitTotalIGjmps++;
#endif
- UNATIVE_OFFSET sz = emitInsSizeAM(id, insCodeRM(ins));
- id->idCodeSize(sz);
-
// Set the relocation flags - these give hint to zap to perform
// relocation of the specified 32bit address.
+ //
+ // Note the relocation flags influence the size estimate.
id->idSetRelocFlags(attr);
+ UNATIVE_OFFSET sz = emitInsSizeAM(id, insCodeRM(ins));
+ id->idCodeSize(sz);
+
dispIns(id);
emitCurIGsize += sz;
}
diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp
index dfcf703d28..83f3a78bd5 100644
--- a/src/jit/flowgraph.cpp
+++ b/src/jit/flowgraph.cpp
@@ -17,6 +17,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#endif
#include "allocacheck.h" // for alloca
+#ifndef LEGACY_BACKEND
+#include "lower.h" // for LowerRange()
+#endif
/*****************************************************************************/
@@ -90,9 +93,8 @@ void Compiler::fgInit()
genReturnBB = nullptr;
/* We haven't reached the global morphing phase */
- fgGlobalMorph = false;
- fgExpandInline = false;
- fgModified = false;
+ fgGlobalMorph = false;
+ fgModified = false;
#ifdef DEBUG
fgSafeBasicBlockCreation = true;
@@ -286,7 +288,7 @@ void Compiler::fgInstrumentMethod()
// In such cases we still want to add the method entry callback node
GenTreeArgList* args = gtNewArgList(gtNewIconEmbMethHndNode(info.compMethodHnd));
- GenTreePtr call = gtNewHelperCallNode(CORINFO_HELP_BBT_FCN_ENTER, TYP_VOID, 0, args);
+ GenTreePtr call = gtNewHelperCallNode(CORINFO_HELP_BBT_FCN_ENTER, TYP_VOID, args);
stmt = gtNewStmt(call);
}
@@ -354,7 +356,7 @@ void Compiler::fgInstrumentMethod()
}
GenTreeArgList* args = gtNewArgList(arg);
- GenTreePtr call = gtNewHelperCallNode(CORINFO_HELP_BBT_FCN_ENTER, TYP_VOID, 0, args);
+ GenTreePtr call = gtNewHelperCallNode(CORINFO_HELP_BBT_FCN_ENTER, TYP_VOID, args);
GenTreePtr handle =
gtNewIconEmbHndNode((void*)&bbProfileBufferStart->ExecutionCount, nullptr, GTF_ICON_BBC_PTR);
@@ -4741,6 +4743,39 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, BYTE*
}
break;
+ case CEE_CALLI:
+
+ // CEE_CALLI should not be inlined if the call indirect target has a calling convention other than
+ // CORINFO_CALLCONV_DEFAULT. In the case where we have a no-marshal CALLI P/Invoke we end up calling
+ // the IL stub. We don't NGEN these stubs, so we'll have to JIT an IL stub for a trivial func.
+ // It's almost certainly a better choice to leave out the inline candidate so we can generate an inlined
+ // call frame.
+
+ // Consider skipping this bail-out for force inlines.
+ if (makeInlineObservations)
+ {
+ if (codeAddr > codeEndp - sizeof(DWORD))
+ {
+ goto TOO_FAR;
+ }
+
+ CORINFO_SIG_INFO calliSig;
+ eeGetSig(getU4LittleEndian(codeAddr), info.compScopeHnd, impTokenLookupContextHandle, &calliSig);
+
+ if (calliSig.getCallConv() != CORINFO_CALLCONV_DEFAULT)
+ {
+ compInlineResult->Note(InlineObservation::CALLEE_UNSUPPORTED_OPCODE);
+
+ // Fail fast if we're inlining
+ if (isInlining)
+ {
+ assert(compInlineResult->IsFailure());
+ return;
+ }
+ }
+ }
+ break;
+
case CEE_JMP:
retBlocks++;
@@ -4762,20 +4797,9 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, BYTE*
__fallthrough;
- case CEE_CALLI:
case CEE_LOCALLOC:
case CEE_MKREFANY:
case CEE_RETHROW:
- // CEE_CALLI should not be inlined because the JIT cannot generate an inlined call frame. If the call
- // target
- // is a no-marshal CALLI P/Invoke we end up calling the IL stub. We don't NGEN these stubs, so we'll
- // have to
- // JIT an IL stub for a trivial func. It's almost certainly a better choice to leave out the inline
- // candidate so we can generate an inlined call frame. It might be nice to call getCallInfo to figure
- // out
- // what kind of call we have here.
-
- // Consider making this only for not force inline.
if (makeInlineObservations)
{
// Arguably this should be NoteFatal, but the legacy behavior is
@@ -7029,12 +7053,10 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo
argList = gtNewArgList(opModuleIDArg);
}
- if (!s_helperCallProperties.NoThrow(helper))
- {
- callFlags |= GTF_EXCEPT;
- }
+ GenTreeCall* result = gtNewHelperCallNode(helper, type, argList);
+ result->gtFlags |= callFlags;
- return gtNewHelperCallNode(helper, type, callFlags, argList);
+ return result;
}
GenTreeCall* Compiler::fgGetSharedCCtor(CORINFO_CLASS_HANDLE cls)
@@ -7054,13 +7076,39 @@ GenTreeCall* Compiler::fgGetSharedCCtor(CORINFO_CLASS_HANDLE cls)
return fgGetStaticsCCtorHelper(cls, info.compCompHnd->getSharedCCtorHelper(cls));
}
+//------------------------------------------------------------------------------
+// fgAddrCouldBeNull : Check whether the address tree can represent null.
//
-// Returns true unless the address expression could
-// never represent a NULL
//
+// Arguments:
+// addr - Address to check
+//
+// Return Value:
+// True if address could be null; false otherwise
+
bool Compiler::fgAddrCouldBeNull(GenTreePtr addr)
{
- if (addr->gtOper == GT_ADDR)
+ if ((addr->gtOper == GT_CNS_INT) && addr->IsIconHandle())
+ {
+ return false;
+ }
+ else if (addr->gtOper == GT_LCL_VAR)
+ {
+ unsigned varNum = addr->AsLclVarCommon()->GetLclNum();
+
+ if (lvaIsImplicitByRefLocal(varNum))
+ {
+ return false;
+ }
+
+ LclVarDsc* varDsc = &lvaTable[varNum];
+
+ if (varDsc->lvStackByref)
+ {
+ return false;
+ }
+ }
+ else if (addr->gtOper == GT_ADDR)
{
if (addr->gtOp.gtOp1->gtOper == GT_CNS_INT)
{
@@ -7072,10 +7120,7 @@ bool Compiler::fgAddrCouldBeNull(GenTreePtr addr)
return true;
}
}
- else if (addr->gtOp.gtOp1->OperIsLocalAddr())
- {
- return false;
- }
+
return false; // we can't have a null address
}
else if (addr->gtOper == GT_ADD)
@@ -7226,7 +7271,7 @@ GenTreePtr Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call,
helperArgs = gtNewArgList(thisPointer, targetObjPointers, ctxTree);
entryPoint = genericLookup;
}
- call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, TYP_VOID, GTF_EXCEPT, helperArgs);
+ call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, TYP_VOID, helperArgs);
call->setEntryPoint(entryPoint);
}
}
@@ -7237,7 +7282,7 @@ GenTreePtr Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call,
GenTreePtr targetObjPointers = call->gtCallArgs->Current();
GenTreeArgList* helperArgs = gtNewArgList(thisPointer, targetObjPointers);
- call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, TYP_VOID, GTF_EXCEPT, helperArgs);
+ call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, TYP_VOID, helperArgs);
CORINFO_LOOKUP entryPoint;
info.compCompHnd->getReadyToRunDelegateCtorHelper(ldftnToken, clsHnd, &entryPoint);
@@ -7623,7 +7668,7 @@ GenTreePtr Compiler::fgGetCritSectOfStaticMethod()
tree = gtNewLclvNode(info.compTypeCtxtArg, TYP_I_IMPL);
// Call helper CORINFO_HELP_GETCLASSFROMMETHODPARAM to get the class handle
// from the method handle.
- tree = gtNewHelperCallNode(CORINFO_HELP_GETCLASSFROMMETHODPARAM, TYP_I_IMPL, 0, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_GETCLASSFROMMETHODPARAM, TYP_I_IMPL, gtNewArgList(tree));
break;
}
@@ -7637,7 +7682,7 @@ GenTreePtr Compiler::fgGetCritSectOfStaticMethod()
noway_assert(tree); // tree should now contain the CORINFO_CLASS_HANDLE for the exact class.
// Given the class handle, get the pointer to the Monitor.
- tree = gtNewHelperCallNode(CORINFO_HELP_GETSYNCFROMCLASSHANDLE, TYP_I_IMPL, 0, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_GETSYNCFROMCLASSHANDLE, TYP_I_IMPL, gtNewArgList(tree));
}
noway_assert(tree);
@@ -7861,8 +7906,8 @@ void Compiler::fgAddSyncMethodEnterExit()
#ifdef DEBUG
if (verbose)
{
- printf("\nSynchronized method - Add 'acquired' initialization in first block BB%02u [%08p]\n", fgFirstBB,
- dspPtr(fgFirstBB));
+ printf("\nSynchronized method - Add 'acquired' initialization in first block %s\n",
+ fgFirstBB->dspToString());
gtDispTree(initNode);
printf("\n");
}
@@ -7919,21 +7964,21 @@ GenTree* Compiler::fgCreateMonitorTree(unsigned lvaMonAcquired, unsigned lvaThis
if (info.compIsStatic)
{
tree = fgGetCritSectOfStaticMethod();
- tree = gtNewHelperCallNode(enter ? CORINFO_HELP_MON_ENTER_STATIC : CORINFO_HELP_MON_EXIT_STATIC, TYP_VOID, 0,
+ tree = gtNewHelperCallNode(enter ? CORINFO_HELP_MON_ENTER_STATIC : CORINFO_HELP_MON_EXIT_STATIC, TYP_VOID,
gtNewArgList(tree, varAddrNode));
}
else
{
tree = gtNewLclvNode(lvaThisVar, TYP_REF);
- tree = gtNewHelperCallNode(enter ? CORINFO_HELP_MON_ENTER : CORINFO_HELP_MON_EXIT, TYP_VOID, 0,
+ tree = gtNewHelperCallNode(enter ? CORINFO_HELP_MON_ENTER : CORINFO_HELP_MON_EXIT, TYP_VOID,
gtNewArgList(tree, varAddrNode));
}
#ifdef DEBUG
if (verbose)
{
- printf("\nSynchronized method - Add monitor %s call to block BB%02u [%08p]\n", enter ? "enter" : "exit", block,
- dspPtr(block));
+ printf("\nSynchronized method - Add monitor %s call to block %s\n", enter ? "enter" : "exit",
+ block->dspToString());
gtDispTree(tree);
printf("\n");
}
@@ -8046,7 +8091,7 @@ void Compiler::fgAddReversePInvokeEnterExit()
tree = gtNewOperNode(GT_ADDR, TYP_I_IMPL, gtNewLclvNode(lvaReversePInvokeFrameVar, TYP_BLK));
- tree = gtNewHelperCallNode(CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER, TYP_VOID, 0, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER, TYP_VOID, gtNewArgList(tree));
fgEnsureFirstBBisScratch();
@@ -8055,7 +8100,8 @@ void Compiler::fgAddReversePInvokeEnterExit()
#ifdef DEBUG
if (verbose)
{
- printf("\nReverse PInvoke method - Add reverse pinvoke enter in first basic block [%08p]\n", dspPtr(fgFirstBB));
+ printf("\nReverse PInvoke method - Add reverse pinvoke enter in first basic block %s\n",
+ fgFirstBB->dspToString());
gtDispTree(tree);
printf("\n");
}
@@ -8065,17 +8111,17 @@ void Compiler::fgAddReversePInvokeEnterExit()
tree = gtNewOperNode(GT_ADDR, TYP_I_IMPL, gtNewLclvNode(lvaReversePInvokeFrameVar, TYP_BLK));
- tree = gtNewHelperCallNode(CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT, TYP_VOID, 0, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT, TYP_VOID, gtNewArgList(tree));
assert(genReturnBB != nullptr);
- fgInsertStmtAtEnd(genReturnBB, tree);
+ fgInsertStmtNearEnd(genReturnBB, tree);
#ifdef DEBUG
if (verbose)
{
- printf("\nReverse PInvoke method - Add reverse pinvoke exit in return basic block [%08p]\n",
- dspPtr(genReturnBB));
+ printf("\nReverse PInvoke method - Add reverse pinvoke exit in return basic block %s\n",
+ genReturnBB->dspToString());
gtDispTree(tree);
printf("\n");
}
@@ -8106,10 +8152,517 @@ bool Compiler::fgMoreThanOneReturnBlock()
return false;
}
+namespace
+{
+// Define a helper class for merging return blocks (which we do when the input has
+// more than the limit for this configuration).
+//
+// Notes: sets fgReturnCount, genReturnBB, and genReturnLocal.
+class MergedReturns
+{
+public:
+#ifdef JIT32_GCENCODER
+
+ // X86 GC encoding has a hard limit of SET_EPILOGCNT_MAX epilogs.
+ const static unsigned ReturnCountHardLimit = SET_EPILOGCNT_MAX;
+#else // JIT32_GCENCODER
+
+ // We currently apply a hard limit of '4' to all other targets (see
+ // the other uses of SET_EPILOGCNT_MAX), though it would be good
+ // to revisit that decision based on CQ analysis.
+ const static unsigned ReturnCountHardLimit = 4;
+#endif // JIT32_GCENCODER
+
+private:
+ Compiler* comp;
+
+ // As we discover returns, we'll record them in `returnBlocks`, until
+ // the limit is reached, at which point we'll keep track of the merged
+ // return blocks in `returnBlocks`.
+ BasicBlock* returnBlocks[ReturnCountHardLimit];
+
+ // Each constant value returned gets its own merged return block that
+ // returns that constant (up to the limit on number of returns); in
+ // `returnConstants` we track the constant values returned by these
+ // merged constant return blocks.
+ ssize_t returnConstants[ReturnCountHardLimit];
+
+ // Indicators of where in the lexical block list we'd like to place
+ // each constant return block.
+ BasicBlock* insertionPoints[ReturnCountHardLimit];
+
+ // Number of return blocks allowed
+ PhasedVar<unsigned> maxReturns;
+
+ // Flag to keep track of when we've hit the limit of returns and are
+ // actively merging returns together.
+ bool mergingReturns = false;
+
+public:
+ MergedReturns(Compiler* comp) : comp(comp)
+ {
+ comp->fgReturnCount = 0;
+ }
+
+ void SetMaxReturns(unsigned value)
+ {
+ maxReturns = value;
+ maxReturns.MarkAsReadOnly();
+ }
+
+ //------------------------------------------------------------------------
+ // Record: Make note of a return block in the input program.
+ //
+ // Arguments:
+ // returnBlock - Block in the input that has jump kind BBJ_RETURN
+ //
+ // Notes:
+ // Updates fgReturnCount appropriately, and generates a merged return
+ // block if necessary. If a constant merged return block is used,
+ // `returnBlock` is rewritten to jump to it. If a non-constant return
+ // block is used, `genReturnBB` is set to that block, and `genReturnLocal`
+ // is set to the lclvar that it returns; morph will need to rewrite
+ // `returnBlock` to set the local and jump to the return block in such
+ // cases, which it will do after some key transformations like rewriting
+ // tail calls and calls that return to hidden buffers. In either of these
+ // cases, `fgReturnCount` and the merged return block's profile information
+ // will be updated to reflect or anticipate the rewrite of `returnBlock`.
+ //
+ void Record(BasicBlock* returnBlock)
+ {
+ // Add this return to our tally
+ unsigned oldReturnCount = comp->fgReturnCount++;
+
+ if (!mergingReturns)
+ {
+ if (oldReturnCount < maxReturns)
+ {
+ // No need to merge just yet; simply record this return.
+ returnBlocks[oldReturnCount] = returnBlock;
+ return;
+ }
+
+ // We'e reached our threshold
+ mergingReturns = true;
+
+ // Merge any returns we've already identified
+ for (unsigned i = 0, searchLimit = 0; i < oldReturnCount; ++i)
+ {
+ BasicBlock* mergedReturnBlock = Merge(returnBlocks[i], searchLimit);
+ if (returnBlocks[searchLimit] == mergedReturnBlock)
+ {
+ // We've added a new block to the searchable set
+ ++searchLimit;
+ }
+ }
+ }
+
+ // We have too many returns, so merge this one in.
+ // Search limit is new return count minus one (to exclude this block).
+ unsigned searchLimit = comp->fgReturnCount - 1;
+ Merge(returnBlock, searchLimit);
+ }
+
+ //------------------------------------------------------------------------
+ // EagerCreate: Force creation of a non-constant merged return block `genReturnBB`.
+ //
+ // Return Value:
+ // The newly-created block which returns `genReturnLocal`.
+ //
+ BasicBlock* EagerCreate()
+ {
+ mergingReturns = true;
+ return Merge(nullptr, 0);
+ }
+
+ //------------------------------------------------------------------------
+ // PlaceReturns: Move any generated const return blocks to an appropriate
+ // spot in the lexical block list.
+ //
+ // Notes:
+ // The goal is to set things up favorably for a reasonable layout without
+ // putting too much burden on fgReorderBlocks; in particular, since that
+ // method doesn't (currently) shuffle non-profile, non-rare code to create
+ // fall-through and reduce gotos, this method places each const return
+ // block immediately after its last predecessor, so that the flow from
+ // there to it can become fallthrough without requiring any motion to be
+ // performed by fgReorderBlocks.
+ //
+ void PlaceReturns()
+ {
+ if (!mergingReturns)
+ {
+ // No returns generated => no returns to place.
+ return;
+ }
+
+ for (unsigned index = 0; index < comp->fgReturnCount; ++index)
+ {
+ BasicBlock* returnBlock = returnBlocks[index];
+ BasicBlock* genReturnBlock = comp->genReturnBB;
+ if (returnBlock == genReturnBlock)
+ {
+ continue;
+ }
+
+ BasicBlock* insertionPoint = insertionPoints[index];
+ assert(insertionPoint != nullptr);
+
+ comp->fgUnlinkBlock(returnBlock);
+ comp->fgMoveBlocksAfter(returnBlock, returnBlock, insertionPoint);
+ // Treat the merged return block as belonging to the same EH region
+ // as the insertion point block, to make sure we don't break up
+ // EH regions; since returning a constant won't throw, this won't
+ // affect program behavior.
+ comp->fgExtendEHRegionAfter(insertionPoint);
+ }
+ }
+
+private:
+ //------------------------------------------------------------------------
+ // CreateReturnBB: Create a basic block to serve as a merged return point, stored to
+ // `returnBlocks` at the given index, and optionally returning the given constant.
+ //
+ // Arguments:
+ // index - Index into `returnBlocks` to store the new block into.
+ // returnConst - Constant that the new block should return; may be nullptr to
+ // indicate that the new merged return is for the non-constant case, in which
+ // case, if the method's return type is non-void, `comp->genReturnLocal` will
+ // be initialized to a new local of the appropriate type, and the new block will
+ // return it.
+ //
+ // Return Value:
+ // The new merged return block.
+ //
+ BasicBlock* CreateReturnBB(unsigned index, GenTreeIntConCommon* returnConst = nullptr)
+ {
+ BasicBlock* newReturnBB = comp->fgNewBBinRegion(BBJ_RETURN);
+ newReturnBB->bbRefs = 1; // bbRefs gets update later, for now it should be 1
+ comp->fgReturnCount++;
+
+ newReturnBB->bbFlags |= BBF_INTERNAL;
+
+ noway_assert(newReturnBB->bbNext == nullptr);
+
+#ifdef DEBUG
+ if (comp->verbose)
+ {
+ printf("\n newReturnBB [BB%02u] created\n", newReturnBB->bbNum);
+ }
+#endif
+
+ // We have profile weight, the weight is zero, and the block is run rarely,
+ // until we prove otherwise by merging other returns into this one.
+ newReturnBB->bbFlags |= (BBF_PROF_WEIGHT | BBF_RUN_RARELY);
+ newReturnBB->bbWeight = 0;
+
+ GenTreePtr returnExpr;
+
+ if (returnConst != nullptr)
+ {
+ returnExpr = comp->gtNewOperNode(GT_RETURN, returnConst->gtType, returnConst);
+ returnConstants[index] = returnConst->IconValue();
+ }
+ else if (comp->compMethodHasRetVal())
+ {
+ // There is a return value, so create a temp for it. Real returns will store the value in there and
+ // it'll be reloaded by the single return.
+ unsigned returnLocalNum = comp->lvaGrabTemp(true DEBUGARG("Single return block return value"));
+ comp->genReturnLocal = returnLocalNum;
+ LclVarDsc& returnLocalDsc = comp->lvaTable[returnLocalNum];
+
+ if (comp->compMethodReturnsNativeScalarType())
+ {
+ returnLocalDsc.lvType = genActualType(comp->info.compRetNativeType);
+ }
+ else if (comp->compMethodReturnsRetBufAddr())
+ {
+ returnLocalDsc.lvType = TYP_BYREF;
+ }
+ else if (comp->compMethodReturnsMultiRegRetType())
+ {
+ returnLocalDsc.lvType = TYP_STRUCT;
+ comp->lvaSetStruct(returnLocalNum, comp->info.compMethodInfo->args.retTypeClass, true);
+ returnLocalDsc.lvIsMultiRegRet = true;
+ }
+ else
+ {
+ assert(!"unreached");
+ }
+
+ if (varTypeIsFloating(returnLocalDsc.lvType))
+ {
+ comp->compFloatingPointUsed = true;
+ }
+
+ if (!varTypeIsFloating(comp->info.compRetType))
+ {
+ returnLocalDsc.setPrefReg(REG_INTRET, comp);
+ }
+#ifdef REG_FLOATRET
+ else
+ {
+ returnLocalDsc.setPrefReg(REG_FLOATRET, comp);
+ }
+#endif
+
+#ifdef DEBUG
+ // This temporary should not be converted to a double in stress mode,
+ // because we introduce assigns to it after the stress conversion
+ returnLocalDsc.lvKeepType = 1;
+#endif
+
+ GenTreePtr retTemp = comp->gtNewLclvNode(returnLocalNum, returnLocalDsc.TypeGet());
+
+ // make sure copy prop ignores this node (make sure it always does a reload from the temp).
+ retTemp->gtFlags |= GTF_DONT_CSE;
+ returnExpr = comp->gtNewOperNode(GT_RETURN, retTemp->gtType, retTemp);
+ }
+ else
+ {
+ // return void
+ noway_assert(comp->info.compRetType == TYP_VOID || varTypeIsStruct(comp->info.compRetType));
+ comp->genReturnLocal = BAD_VAR_NUM;
+
+ returnExpr = new (comp, GT_RETURN) GenTreeOp(GT_RETURN, TYP_VOID);
+ }
+
+ // Add 'return' expression to the return block
+ comp->fgInsertStmtAtEnd(newReturnBB, returnExpr);
+ // Flag that this 'return' was generated by return merging so that subsequent
+ // return block morhping will know to leave it alone.
+ returnExpr->gtFlags |= GTF_RET_MERGED;
+
+#ifdef DEBUG
+ if (comp->verbose)
+ {
+ printf("\nmergeReturns statement tree ");
+ Compiler::printTreeID(returnExpr);
+ printf(" added to genReturnBB %s\n", newReturnBB->dspToString());
+ comp->gtDispTree(returnExpr);
+ printf("\n");
+ }
+#endif
+ assert(index < maxReturns);
+ returnBlocks[index] = newReturnBB;
+ return newReturnBB;
+ }
+
+ //------------------------------------------------------------------------
+ // Merge: Find or create an appropriate merged return block for the given input block.
+ //
+ // Arguments:
+ // returnBlock - Return block from the input program to find a merged return for.
+ // May be nullptr to indicate that new block suitable for non-constant
+ // returns should be generated but no existing block modified.
+ // searchLimit - Blocks in `returnBlocks` up to but not including index `searchLimit`
+ // will be checked to see if we already have an appropriate merged return
+ // block for this case. If a new block must be created, it will be stored
+ // to `returnBlocks` at index `searchLimit`.
+ //
+ // Return Value:
+ // Merged return block suitable for handling this return value. May be newly-created
+ // or pre-existing.
+ //
+ // Notes:
+ // If a constant-valued merged return block is used, `returnBlock` will be rewritten to
+ // jump to the merged return block and its `GT_RETURN` statement will be removed. If
+ // a non-constant-valued merged return block is used, `genReturnBB` and `genReturnLocal`
+ // will be set so that Morph can perform that rewrite, which it will do after some key
+ // transformations like rewriting tail calls and calls that return to hidden buffers.
+ // In either of these cases, `fgReturnCount` and the merged return block's profile
+ // information will be updated to reflect or anticipate the rewrite of `returnBlock`.
+ //
+ BasicBlock* Merge(BasicBlock* returnBlock, unsigned searchLimit)
+ {
+ assert(mergingReturns);
+
+ BasicBlock* mergedReturnBlock = nullptr;
+ if ((returnBlock != nullptr) && (maxReturns > 1))
+ {
+ // Check to see if this is a constant return so that we can search
+ // for and/or create a constant return block for it.
+
+ GenTreeIntConCommon* retConst = GetReturnConst(returnBlock);
+ if (retConst != nullptr)
+ {
+ // We have a constant. Now find or create a corresponding return block.
+
+ unsigned index;
+ BasicBlock* constReturnBlock = FindConstReturnBlock(retConst, searchLimit, &index);
+
+ if (constReturnBlock == nullptr)
+ {
+ // We didn't find a const return block. See if we have space left
+ // to make one.
+
+ // We have already allocated `searchLimit` slots.
+ unsigned slotsReserved = searchLimit;
+ if (comp->genReturnBB == nullptr)
+ {
+ // We haven't made a non-const return yet, so we have to reserve
+ // a slot for one.
+ ++slotsReserved;
+ }
+
+ if (slotsReserved < maxReturns)
+ {
+ // We have enough space to allocate a slot for this constant.
+ constReturnBlock = CreateReturnBB(searchLimit, retConst);
+ }
+ }
+
+ if (constReturnBlock != nullptr)
+ {
+ // Found a constant merged return block.
+ mergedReturnBlock = constReturnBlock;
+
+ // Change BBJ_RETURN to BBJ_ALWAYS targeting const return block.
+ assert((comp->info.compFlags & CORINFO_FLG_SYNCH) == 0);
+ returnBlock->bbJumpKind = BBJ_ALWAYS;
+ returnBlock->bbJumpDest = constReturnBlock;
+
+ // Remove GT_RETURN since constReturnBlock returns the constant.
+ assert(returnBlock->lastStmt()->gtStmtExpr->OperIs(GT_RETURN));
+ assert(returnBlock->lastStmt()->gtStmtExpr->gtGetOp1()->IsIntegralConst());
+ comp->fgRemoveStmt(returnBlock, returnBlock->lastStmt());
+
+ // Using 'returnBlock' as the insertion point for 'mergedReturnBlock'
+ // will give it a chance to use fallthrough rather than BBJ_ALWAYS.
+ // Resetting this after each merge ensures that any branches to the
+ // merged return block are lexically forward.
+
+ insertionPoints[index] = returnBlock;
+ }
+ }
+ }
+
+ if (mergedReturnBlock == nullptr)
+ {
+ // No constant return block for this return; use the general one.
+ mergedReturnBlock = comp->genReturnBB;
+ if (mergedReturnBlock == nullptr)
+ {
+ // No general merged return for this function yet; create one.
+ // There had better still be room left in the array.
+ assert(searchLimit < maxReturns);
+ mergedReturnBlock = CreateReturnBB(searchLimit);
+ comp->genReturnBB = mergedReturnBlock;
+ // Downstream code expects the `genReturnBB` to always remain
+ // once created, so that it can redirect flow edges to it.
+ mergedReturnBlock->bbFlags |= BBF_DONT_REMOVE;
+ }
+ }
+
+ if (returnBlock != nullptr)
+ {
+ // Propagate profile weight and related annotations to the merged block.
+ // Return weight should never exceed entry weight, so cap it to avoid nonsensical
+ // hot returns in synthetic profile settings.
+ mergedReturnBlock->bbWeight =
+ min(mergedReturnBlock->bbWeight + returnBlock->bbWeight, comp->fgFirstBB->bbWeight);
+ if (!returnBlock->hasProfileWeight())
+ {
+ mergedReturnBlock->bbFlags &= ~BBF_PROF_WEIGHT;
+ }
+ if (mergedReturnBlock->bbWeight > 0)
+ {
+ mergedReturnBlock->bbFlags &= ~BBF_RUN_RARELY;
+ }
+
+ // Update fgReturnCount to reflect or anticipate that `returnBlock` will no longer
+ // be a return point.
+ comp->fgReturnCount--;
+ }
+
+ return mergedReturnBlock;
+ }
+
+ //------------------------------------------------------------------------
+ // GetReturnConst: If the given block returns an integral constant, return the
+ // GenTreeIntConCommon that represents the constant.
+ //
+ // Arguments:
+ // returnBlock - Block whose return value is to be inspected.
+ //
+ // Return Value:
+ // GenTreeIntCommon that is the argument of `returnBlock`'s `GT_RETURN` if
+ // such exists; nullptr otherwise.
+ //
+ static GenTreeIntConCommon* GetReturnConst(BasicBlock* returnBlock)
+ {
+ GenTreeStmt* lastStmt = returnBlock->lastStmt();
+ if (lastStmt == nullptr)
+ {
+ return nullptr;
+ }
+
+ GenTreePtr lastExpr = lastStmt->gtStmtExpr;
+ if (!lastExpr->OperIs(GT_RETURN))
+ {
+ return nullptr;
+ }
+
+ GenTreePtr retExpr = lastExpr->gtGetOp1();
+ if ((retExpr == nullptr) || !retExpr->IsIntegralConst())
+ {
+ return nullptr;
+ }
+
+ return retExpr->AsIntConCommon();
+ }
+
+ //------------------------------------------------------------------------
+ // FindConstReturnBlock: Scan the already-created merged return blocks, up to `searchLimit`,
+ // and return the one corresponding to the given const expression if it exists.
+ //
+ // Arguments:
+ // constExpr - GenTreeIntCommon representing the constant return value we're
+ // searching for.
+ // searchLimit - Check `returnBlocks`/`returnConstants` up to but not including
+ // this index.
+ // index - [out] Index of return block in the `returnBlocks` array, if found;
+ // searchLimit otherwise.
+ //
+ // Return Value:
+ // A block that returns the same constant, if one is found; otherwise nullptr.
+ //
+ BasicBlock* FindConstReturnBlock(GenTreeIntConCommon* constExpr, unsigned searchLimit, unsigned* index)
+ {
+ ssize_t constVal = constExpr->IconValue();
+
+ for (unsigned i = 0; i < searchLimit; ++i)
+ {
+ // Need to check both for matching const val and for genReturnBB
+ // because genReturnBB is used for non-constant returns and its
+ // corresponding entry in the returnConstants array is garbage.
+ if (returnConstants[i] == constVal)
+ {
+ BasicBlock* returnBlock = returnBlocks[i];
+
+ if (returnBlock == comp->genReturnBB)
+ {
+ // This is the block used for non-constant returns, so
+ // its returnConstants entry is just garbage; don't be
+ // fooled.
+ continue;
+ }
+
+ *index = i;
+ return returnBlock;
+ }
+ }
+
+ *index = searchLimit;
+ return nullptr;
+ }
+};
+}
+
/*****************************************************************************
- *
- * Add any internal blocks/trees we may need
- */
+*
+* Add any internal blocks/trees we may need
+*/
void Compiler::fgAddInternal()
{
@@ -8126,17 +8679,17 @@ void Compiler::fgAddInternal()
#endif // !LEGACY_BACKEND
/*
- <BUGNUM> VSW441487 </BUGNUM>
+ <BUGNUM> VSW441487 </BUGNUM>
- The "this" pointer is implicitly used in the following cases:
- 1. Locking of synchronized methods
- 2. Dictionary access of shared generics code
- 3. If a method has "catch(FooException<T>)", the EH code accesses "this" to determine T.
- 4. Initializing the type from generic methods which require precise cctor semantics
- 5. Verifier does special handling of "this" in the .ctor
+ The "this" pointer is implicitly used in the following cases:
+ 1. Locking of synchronized methods
+ 2. Dictionary access of shared generics code
+ 3. If a method has "catch(FooException<T>)", the EH code accesses "this" to determine T.
+ 4. Initializing the type from generic methods which require precise cctor semantics
+ 5. Verifier does special handling of "this" in the .ctor
- However, we might overwrite it with a "starg 0".
- In this case, we will redirect all "ldarg(a)/starg(a) 0" to a temp lvaTable[lvaArg0Var]
+ However, we might overwrite it with a "starg 0".
+ In this case, we will redirect all "ldarg(a)/starg(a) 0" to a temp lvaTable[lvaArg0Var]
*/
if (!info.compIsStatic)
@@ -8150,7 +8703,7 @@ void Compiler::fgAddInternal()
#ifndef JIT32_GCENCODER
lva0CopiedForGenericsCtxt = ((info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_THIS) != 0);
#else // JIT32_GCENCODER
- lva0CopiedForGenericsCtxt = false;
+ lva0CopiedForGenericsCtxt = false;
#endif // JIT32_GCENCODER
noway_assert(lva0CopiedForGenericsCtxt || !lvaTable[info.compThisArg].lvAddrExposed);
noway_assert(!lvaTable[info.compThisArg].lvHasILStoreOp);
@@ -8178,7 +8731,7 @@ void Compiler::fgAddInternal()
#ifdef DEBUG
if (verbose)
{
- printf("\nCopy \"this\" to lvaArg0Var in first basic block [%08p]\n", dspPtr(fgFirstBB));
+ printf("\nCopy \"this\" to lvaArg0Var in first basic block %s\n", fgFirstBB->dspToString());
gtDispTree(tree);
printf("\n");
}
@@ -8195,11 +8748,20 @@ void Compiler::fgAddInternal()
lvaTable[lvaSecurityObject].lvType = TYP_REF;
}
- /* Assume we will generate a single shared return sequence */
+ // Merge return points if required or beneficial
+ MergedReturns merger(this);
- ULONG returnWeight = 0;
- bool oneReturn;
- bool allProfWeight;
+#if FEATURE_EH_FUNCLETS
+ // Add the synchronized method enter/exit calls and try/finally protection. Note
+ // that this must happen before the one BBJ_RETURN block is created below, so the
+ // BBJ_RETURN block gets placed at the top-level, not within an EH region. (Otherwise,
+ // we'd have to be really careful when creating the synchronized method try/finally
+ // not to include the BBJ_RETURN block.)
+ if ((info.compFlags & CORINFO_FLG_SYNCH) != 0)
+ {
+ fgAddSyncMethodEnterExit();
+ }
+#endif // FEATURE_EH_FUNCLETS
//
// We will generate just one epilog (return block)
@@ -8208,6 +8770,7 @@ void Compiler::fgAddInternal()
// or for methods calling into unmanaged code
// or for synchronized methods.
//
+ BasicBlock* lastBlockBeforeGenReturns = fgLastBB;
if (compIsProfilerHookNeeded() || (info.compCallUnmanaged != 0) || opts.IsReversePInvoke() ||
((info.compFlags & CORINFO_FLG_SYNCH) != 0))
{
@@ -8215,198 +8778,52 @@ void Compiler::fgAddInternal()
// We will transform the BBJ_RETURN blocks
// into jumps to the one return block
//
- oneReturn = true;
- allProfWeight = false;
- }
- else
- {
- //
- // We are allowed to have multiple individual exits
- // However we can still decide to have a single return
- //
- oneReturn = false;
- allProfWeight = true;
+ merger.SetMaxReturns(1);
- // Count the BBJ_RETURN blocks and set the returnWeight to the
- // sum of all these blocks.
- //
- fgReturnCount = 0;
- for (BasicBlock* block = fgFirstBB; block; block = block->bbNext)
- {
- if (block->bbJumpKind == BBJ_RETURN)
- {
- //
- // returnCount is the count of BBJ_RETURN blocks in this method
- //
- fgReturnCount++;
- //
- // If all BBJ_RETURN blocks have a valid profiled weights
- // then allProfWeight will be true, else it is false
- //
- if (!block->hasProfileWeight())
- {
- allProfWeight = false;
- }
- //
- // returnWeight is the sum of the weights of all BBJ_RETURN blocks
- returnWeight += block->bbWeight;
- }
- }
-
- //
- // If we only have one (or zero) return blocks then
- // we do not need a special one return block
- //
- if (fgReturnCount > 1)
+ // Eagerly create the genReturnBB since the lowering of these constructs
+ // will expect to find it.
+ BasicBlock* mergedReturn = merger.EagerCreate();
+ assert(mergedReturn == genReturnBB);
+ // Assume weight equal to entry weight for this BB.
+ mergedReturn->bbFlags &= ~BBF_PROF_WEIGHT;
+ mergedReturn->bbWeight = fgFirstBB->bbWeight;
+ if (mergedReturn->bbWeight > 0)
{
- //
- // should we generate a single return block?
- //
- if (fgReturnCount > 4)
- {
- // Our epilog encoding only supports up to 4 epilogs
- // TODO-CQ: support >4 return points for ARM/AMD64, which presumably support any number of epilogs?
- //
- oneReturn = true;
- }
- else if (compCodeOpt() == SMALL_CODE)
- {
- // For the Small_Code case we always generate a
- // single return block when we have multiple
- // return points
- //
- oneReturn = true;
- }
+ mergedReturn->bbFlags &= ~BBF_RUN_RARELY;
}
}
-
-#if FEATURE_EH_FUNCLETS
- // Add the synchronized method enter/exit calls and try/finally protection. Note
- // that this must happen before the one BBJ_RETURN block is created below, so the
- // BBJ_RETURN block gets placed at the top-level, not within an EH region. (Otherwise,
- // we'd have to be really careful when creating the synchronized method try/finally
- // not to include the BBJ_RETURN block.)
- if ((info.compFlags & CORINFO_FLG_SYNCH) != 0)
- {
- fgAddSyncMethodEnterExit();
- }
-#endif // FEATURE_EH_FUNCLETS
-
- if (oneReturn)
+ else
{
- genReturnBB = fgNewBBinRegion(BBJ_RETURN);
- genReturnBB->bbRefs = 1; // bbRefs gets update later, for now it should be 1
- fgReturnCount++;
-
- if (allProfWeight)
- {
- //
- // if we have profile data for all BBJ_RETURN blocks
- // then we can set BBF_PROF_WEIGHT for genReturnBB
- //
- genReturnBB->bbFlags |= BBF_PROF_WEIGHT;
- }
- else
- {
- //
- // We can't rely upon the calculated returnWeight unless
- // all of the BBJ_RETURN blocks had valid profile weights
- // So we will use the weight of the first block instead
- //
- returnWeight = fgFirstBB->bbWeight;
- }
-
//
- // Set the weight of the oneReturn block
+ // We are allowed to have multiple individual exits
+ // However we can still decide to have a single return
//
- genReturnBB->bbWeight = min(returnWeight, BB_MAX_WEIGHT);
-
- if (returnWeight == 0)
+ if (compCodeOpt() == SMALL_CODE)
{
+ // For the Small_Code case we always generate a
+ // single return block when we have multiple
+ // return points
//
- // If necessary set the Run Rarely flag
- //
- genReturnBB->bbFlags |= BBF_RUN_RARELY;
+ merger.SetMaxReturns(1);
}
else
{
- // Make sure that the RunRarely flag is clear
- // because fgNewBBinRegion will set it to true
- //
- genReturnBB->bbFlags &= ~BBF_RUN_RARELY;
- }
-
- genReturnBB->bbFlags |= (BBF_INTERNAL | BBF_DONT_REMOVE);
-
- noway_assert(genReturnBB->bbNext == nullptr);
-
-#ifdef DEBUG
- if (verbose)
- {
- printf("\n genReturnBB [BB%02u] created\n", genReturnBB->bbNum);
+ merger.SetMaxReturns(MergedReturns::ReturnCountHardLimit);
}
-#endif
- }
- else
- {
- //
- // We don't have a oneReturn block for this method
- //
- genReturnBB = nullptr;
}
- // If there is a return value, then create a temp for it. Real returns will store the value in there and
- // it'll be reloaded by the single return.
- if (genReturnBB && compMethodHasRetVal())
- {
- genReturnLocal = lvaGrabTemp(true DEBUGARG("Single return block return value"));
-
- if (compMethodReturnsNativeScalarType())
- {
- lvaTable[genReturnLocal].lvType = genActualType(info.compRetNativeType);
- }
- else if (compMethodReturnsRetBufAddr())
- {
- lvaTable[genReturnLocal].lvType = TYP_BYREF;
- }
- else if (compMethodReturnsMultiRegRetType())
- {
- lvaTable[genReturnLocal].lvType = TYP_STRUCT;
- lvaSetStruct(genReturnLocal, info.compMethodInfo->args.retTypeClass, true);
- lvaTable[genReturnLocal].lvIsMultiRegRet = true;
- }
- else
- {
- assert(!"unreached");
- }
+ // Visit the BBJ_RETURN blocks and merge as necessary.
- if (varTypeIsFloating(lvaTable[genReturnLocal].lvType))
- {
- this->compFloatingPointUsed = true;
- }
-
- if (!varTypeIsFloating(info.compRetType))
- {
- lvaTable[genReturnLocal].setPrefReg(REG_INTRET, this);
- }
-#ifdef REG_FLOATRET
- else
+ for (BasicBlock* block = fgFirstBB; block != lastBlockBeforeGenReturns->bbNext; block = block->bbNext)
+ {
+ if ((block->bbJumpKind == BBJ_RETURN) && ((block->bbFlags & BBF_HAS_JMP) == 0))
{
- lvaTable[genReturnLocal].setPrefReg(REG_FLOATRET, this);
+ merger.Record(block);
}
-#endif
-
-#ifdef DEBUG
- // This temporary should not be converted to a double in stress mode,
- // because we introduce assigns to it after the stress conversion
- lvaTable[genReturnLocal].lvKeepType = 1;
-#endif
- }
- else
- {
- genReturnLocal = BAD_VAR_NUM;
}
+ merger.PlaceReturns();
+
if (info.compCallUnmanaged != 0)
{
// The P/Invoke helpers only require a frame variable, so only allocate the
@@ -8486,7 +8903,7 @@ void Compiler::fgAddInternal()
tree = gtNewIconEmbMethHndNode(info.compMethodHnd);
- tree = gtNewHelperCallNode(info.compCompHnd->getSecurityPrologHelper(info.compMethodHnd), TYP_VOID, 0,
+ tree = gtNewHelperCallNode(info.compCompHnd->getSecurityPrologHelper(info.compMethodHnd), TYP_VOID,
gtNewArgList(tree, gtNewOperNode(GT_ADDR, TYP_BYREF,
gtNewLclvNode(lvaSecurityObject, TYP_REF))));
@@ -8502,7 +8919,7 @@ void Compiler::fgAddInternal()
printf("\ntiSecurityCalloutNeeded - Add call JIT_Security_Prolog(%08p) statement ",
dspPtr(info.compMethodHnd));
printTreeID(tree);
- printf(" in first basic block [%08p]\n", dspPtr(fgFirstBB));
+ printf(" in first basic block %s\n", fgFirstBB->dspToString());
gtDispTree(tree);
printf("\n");
}
@@ -8523,7 +8940,7 @@ void Compiler::fgAddInternal()
{
tree = fgGetCritSectOfStaticMethod();
- tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER_STATIC, TYP_VOID, 0, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER_STATIC, TYP_VOID, gtNewArgList(tree));
}
else
{
@@ -8531,7 +8948,7 @@ void Compiler::fgAddInternal()
tree = gtNewLclvNode(info.compThisArg, TYP_REF);
- tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER, TYP_VOID, 0, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER, TYP_VOID, gtNewArgList(tree));
}
/* Create a new basic block and stick the call in it */
@@ -8543,7 +8960,8 @@ void Compiler::fgAddInternal()
#ifdef DEBUG
if (verbose)
{
- printf("\nSynchronized method - Add enterCrit statement in first basic block [%08p]\n", dspPtr(fgFirstBB));
+ printf("\nSynchronized method - Add enterCrit statement in first basic block %s\n",
+ fgFirstBB->dspToString());
gtDispTree(tree);
printf("\n");
}
@@ -8551,8 +8969,7 @@ void Compiler::fgAddInternal()
/* We must be generating a single exit point for this to work */
- noway_assert(oneReturn);
- noway_assert(genReturnBB);
+ noway_assert(genReturnBB != nullptr);
/* Create the expression "exitCrit(this)" or "exitCrit(handle)" */
@@ -8560,16 +8977,16 @@ void Compiler::fgAddInternal()
{
tree = fgGetCritSectOfStaticMethod();
- tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT_STATIC, TYP_VOID, 0, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT_STATIC, TYP_VOID, gtNewArgList(tree));
}
else
{
tree = gtNewLclvNode(info.compThisArg, TYP_REF);
- tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT, TYP_VOID, 0, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT, TYP_VOID, gtNewArgList(tree));
}
- fgInsertStmtAtEnd(genReturnBB, tree);
+ fgInsertStmtNearEnd(genReturnBB, tree);
#ifdef DEBUG
if (verbose)
@@ -8597,7 +9014,7 @@ void Compiler::fgAddInternal()
tree = gtNewIconEmbMethHndNode(info.compMethodHnd);
- tree = gtNewHelperCallNode(CORINFO_HELP_VERIFICATION_RUNTIME_CHECK, TYP_VOID, 0, gtNewArgList(tree));
+ tree = gtNewHelperCallNode(CORINFO_HELP_VERIFICATION_RUNTIME_CHECK, TYP_VOID, gtNewArgList(tree));
/* Create a new basic block and stick the call in it */
@@ -8608,9 +9025,8 @@ void Compiler::fgAddInternal()
#ifdef DEBUG
if (verbose)
{
- printf("\ntiRuntimeCalloutNeeded - Call verificationRuntimeCheck(%08p) statement in first basic block "
- "[%08p]\n",
- dspPtr(info.compMethodHnd), dspPtr(fgFirstBB));
+ printf("\ntiRuntimeCalloutNeeded - Call verificationRuntimeCheck(%08p) statement in first basic block %s\n",
+ dspPtr(info.compMethodHnd), fgFirstBB->dspToString());
gtDispTree(tree);
printf("\n");
}
@@ -8622,53 +9038,6 @@ void Compiler::fgAddInternal()
fgAddReversePInvokeEnterExit();
}
- //
- // Add 'return' expression to the return block if we made it as "oneReturn" before.
- //
- if (oneReturn)
- {
- GenTreePtr tree;
-
- //
- // Make the 'return' expression.
- //
-
- // make sure to reload the return value as part of the return (it is saved by the "real return").
- if (genReturnLocal != BAD_VAR_NUM)
- {
- noway_assert(compMethodHasRetVal());
-
- GenTreePtr retTemp = gtNewLclvNode(genReturnLocal, lvaTable[genReturnLocal].TypeGet());
-
- // make sure copy prop ignores this node (make sure it always does a reload from the temp).
- retTemp->gtFlags |= GTF_DONT_CSE;
- tree = gtNewOperNode(GT_RETURN, retTemp->gtType, retTemp);
- }
- else
- {
- noway_assert(info.compRetType == TYP_VOID || varTypeIsStruct(info.compRetType));
- // return void
- tree = new (this, GT_RETURN) GenTreeOp(GT_RETURN, TYP_VOID);
- }
-
- /* Add 'return' expression to the return block */
-
- noway_assert(genReturnBB);
-
- fgInsertStmtAtEnd(genReturnBB, tree);
-
-#ifdef DEBUG
- if (verbose)
- {
- printf("\noneReturn statement tree ");
- printTreeID(tree);
- printf(" added to genReturnBB [%08p]\n", dspPtr(genReturnBB));
- gtDispTree(tree);
- printf("\n");
- }
-#endif
- }
-
#ifdef DEBUG
if (verbose)
{
@@ -9104,7 +9473,8 @@ void Compiler::fgSimpleLowering()
for (GenTreePtr tree = stmt->gtStmtList; tree; tree = tree->gtNext)
{
#else
- LIR::Range& range = LIR::AsRange(block);
+
+ LIR::Range& range = LIR::AsRange(block);
for (GenTree* tree : range)
{
{
@@ -9316,8 +9686,15 @@ VARSET_VALRET_TP Compiler::fgGetVarBits(GenTreePtr tree)
{
VarSetOps::AddElemD(this, varBits, varDsc->lvVarIndex);
}
- else if (varDsc->lvType == TYP_STRUCT && varDsc->lvPromoted)
+ // We have to check type of root tree, not Local Var descriptor because
+ // for legacy backend we promote TYP_STRUCT to TYP_INT if it is an unused or
+ // independently promoted non-argument struct local.
+ // For more details see Compiler::raAssignVars() method.
+ else if (tree->gtType == TYP_STRUCT && varDsc->lvPromoted)
{
+#ifndef LEGACY_BACKEND
+ assert(varDsc->lvType == TYP_STRUCT);
+#endif
for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
{
noway_assert(lvaTable[i].lvIsStructField);
@@ -12836,6 +13213,10 @@ void Compiler::fgComputeEdgeWeights()
if (fgFirstBBisScratch())
{
fgFirstBB->setBBProfileWeight(fgCalledCount);
+ if (fgFirstBB->bbWeight == 0)
+ {
+ fgFirstBB->bbFlags |= BBF_RUN_RARELY;
+ }
}
#if DEBUG
@@ -13473,6 +13854,10 @@ bool Compiler::fgOptimizeEmptyBlock(BasicBlock* block)
if (block->IsLIR())
{
LIR::AsRange(block).InsertAtEnd(nop);
+#ifndef LEGACY_BACKEND
+ LIR::ReadOnlyRange range(nop, nop);
+ m_pLowering->LowerRange(block, range);
+#endif
}
else
{
@@ -13792,6 +14177,10 @@ bool Compiler::fgOptimizeSwitchBranches(BasicBlock* block)
if (block->IsLIR())
{
blockRange->InsertAfter(switchVal, zeroConstNode, condNode);
+#ifndef LEGACY_BACKEND
+ LIR::ReadOnlyRange range(zeroConstNode, switchTree);
+ m_pLowering->LowerRange(block, range);
+#endif // !LEGACY_BACKEND
}
else
{
@@ -14955,7 +15344,15 @@ void Compiler::fgReorderBlocks()
/* (bPrev is known to be a normal block at this point) */
if (!isRare)
{
- reorderBlock = false;
+ if ((bDest == block->bbNext) && (block->bbJumpKind == BBJ_RETURN) && (bPrev->bbJumpKind == BBJ_ALWAYS))
+ {
+ // This is a common case with expressions like "return Expr1 && Expr2" -- move the return
+ // to establish fall-through.
+ }
+ else
+ {
+ reorderBlock = false;
+ }
}
else
{
@@ -17865,9 +18262,8 @@ BasicBlock* Compiler::fgAddCodeRef(BasicBlock* srcBlk, unsigned refData, Special
break;
}
- printf("\nfgAddCodeRef -"
- " Add BB in %s%s, new block BB%02u [%08p], stkDepth is %d\n",
- msgWhere, msg, add->acdDstBlk->bbNum, dspPtr(add->acdDstBlk), stkDepth);
+ printf("\nfgAddCodeRef - Add BB in %s%s, new block %s, stkDepth is %d\n", msgWhere, msg,
+ add->acdDstBlk->dspToString(), stkDepth);
}
#endif // DEBUG
@@ -17925,7 +18321,7 @@ BasicBlock* Compiler::fgAddCodeRef(BasicBlock* srcBlk, unsigned refData, Special
noway_assert(helper != CORINFO_HELP_UNDEF);
// Add the appropriate helper call.
- tree = gtNewHelperCallNode(helper, TYP_VOID, GTF_EXCEPT);
+ tree = gtNewHelperCallNode(helper, TYP_VOID);
// There are no args here but fgMorphArgs has side effects
// such as setting the outgoing arg area (which is necessary
@@ -18335,6 +18731,13 @@ void Compiler::fgSetTreeSeqHelper(GenTreePtr tree, bool isLIR)
noway_assert(!"DYN_BLK nodes should be sequenced as a special case");
break;
+ case GT_INDEX_ADDR:
+ // Evaluate the index first, then the array address
+ assert((tree->gtFlags & GTF_REVERSE_OPS) != 0);
+ fgSetTreeSeqHelper(tree->AsIndexAddr()->Index(), isLIR);
+ fgSetTreeSeqHelper(tree->AsIndexAddr()->Arr(), isLIR);
+ break;
+
default:
#ifdef DEBUG
gtDispTree(tree);
@@ -18718,15 +19121,14 @@ void Compiler::fgOrderBlockOps(GenTreePtr tree,
assert(srcPtrOrVal->OperIsIndir());
srcPtrOrVal = srcPtrOrVal->AsIndir()->Addr();
}
- GenTreePtr sizeNode = (destBlk->gtOper == GT_DYN_BLK) ? destBlk->AsDynBlk()->gtDynamicSize : nullptr;
- noway_assert((sizeNode != nullptr) || ((destBlk->gtFlags & GTF_REVERSE_OPS) == 0));
+
assert(destAddr != nullptr);
assert(srcPtrOrVal != nullptr);
GenTreePtr ops[3] = {
destAddr, // Dest address
srcPtrOrVal, // Val / Src address
- sizeNode // Size of block
+ nullptr // Size of block
};
regMaskTP regs[3] = {reg0, reg1, reg2};
@@ -18741,7 +19143,16 @@ void Compiler::fgOrderBlockOps(GenTreePtr tree,
{2, 1, 0} // true | GTF_REVERSE_OPS
};
- int orderNum = ((destBlk->gtFlags & GTF_REVERSE_OPS) != 0) * 1 + ((tree->gtFlags & GTF_REVERSE_OPS) != 0) * 2;
+ int orderNum = ((tree->gtFlags & GTF_REVERSE_OPS) == 0) ? 0 : 2;
+ if (destBlk->OperIs(GT_DYN_BLK))
+ {
+ GenTreeDynBlk* const dynBlk = destBlk->AsDynBlk();
+ if (dynBlk->gtEvalSizeFirst)
+ {
+ orderNum++;
+ }
+ ops[2] = dynBlk->gtDynamicSize;
+ }
assert(orderNum < 4);
@@ -19650,7 +20061,7 @@ void Compiler::fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth /* = 0 *
blockNumWidth = max(blockNumWidth, 2);
int blockNumPadding = maxBlockNumWidth - blockNumWidth;
- printf("BB%02u%*s [%08p] %2u", block->bbNum, blockNumPadding, "", dspPtr(block), block->bbRefs);
+ printf("%s %2u", block->dspToString(blockNumPadding), block->bbRefs);
//
// Display EH 'try' region index
@@ -19984,11 +20395,6 @@ void Compiler::fgDispBasicBlocks(BasicBlock* firstBlock, BasicBlock* lastBlock,
{
BasicBlock* block;
- int padWidth = 0;
-#ifdef _TARGET_AMD64_
- padWidth = 8;
-#endif // _TARGET_AMD64_
-
// If any block has IBC data, we add an "IBC weight" column just before the 'IL range' column. This column is as
// wide as necessary to accommodate all the various IBC weights. It's at least 4 characters wide, to accommodate
// the "IBC" title and leading space.
@@ -20014,8 +20420,7 @@ void Compiler::fgDispBasicBlocks(BasicBlock* firstBlock, BasicBlock* lastBlock,
unsigned bbNumMax = compIsForInlining() ? impInlineInfo->InlinerCompiler->fgBBNumMax : fgBBNumMax;
int maxBlockNumWidth = CountDigits(bbNumMax);
maxBlockNumWidth = max(maxBlockNumWidth, 2);
-
- padWidth += maxBlockNumWidth - 2; // Account for functions with a large number of blocks.
+ int padWidth = maxBlockNumWidth - 2; // Account for functions with a large number of blocks.
// clang-format off
@@ -20024,7 +20429,7 @@ void Compiler::fgDispBasicBlocks(BasicBlock* firstBlock, BasicBlock* lastBlock,
padWidth, "------------",
ibcColWidth, "------------",
maxBlockNumWidth, "----");
- printf("BBnum %*sdescAddr ref try hnd %s weight %*s%s [IL range] [jump]%*s [EH region] [flags]\n",
+ printf("BBnum %*sBBid ref try hnd %s weight %*s%s [IL range] [jump]%*s [EH region] [flags]\n",
padWidth, "",
fgCheapPredsValid ? "cheap preds" :
(fgComputePredsDone ? "preds "
@@ -20675,6 +21080,10 @@ void Compiler::fgDebugCheckFlags(GenTreePtr tree)
chkFlags |= GTF_ORDER_SIDEEFF;
break;
+ case GT_MEMORYBARRIER:
+ chkFlags |= GTF_GLOB_REF | GTF_ASG;
+ break;
+
default:
break;
}
@@ -20813,16 +21222,14 @@ void Compiler::fgDebugCheckFlags(GenTreePtr tree)
*/
}
- if (kind & GTK_ASGOP)
+ if (tree->OperRequiresAsgFlag())
{
chkFlags |= GTF_ASG;
}
- /* Note that it is OK for treeFlags not to have a GTF_EXCEPT,
- AssertionProp's non-Null may have cleared it */
- if (tree->OperMayThrow())
+ if (tree->OperMayThrow(this))
{
- chkFlags |= (treeFlags & GTF_EXCEPT);
+ chkFlags |= GTF_EXCEPT;
}
if (oper == GT_ADDR && (op1->OperIsLocal() || op1->gtOper == GT_CLS_VAR ||
@@ -20838,6 +21245,11 @@ void Compiler::fgDebugCheckFlags(GenTreePtr tree)
else
{
+ if (tree->OperMayThrow(this))
+ {
+ chkFlags |= GTF_EXCEPT;
+ }
+
switch (tree->OperGet())
{
case GT_CALL:
@@ -20850,24 +21262,6 @@ void Compiler::fgDebugCheckFlags(GenTreePtr tree)
chkFlags |= GTF_CALL;
- if ((treeFlags & GTF_EXCEPT) && !(chkFlags & GTF_EXCEPT))
- {
- switch (eeGetHelperNum(call->gtCallMethHnd))
- {
- // Is this a helper call that can throw an exception ?
- case CORINFO_HELP_LDIV:
- case CORINFO_HELP_LMOD:
- case CORINFO_HELP_METHOD_ACCESS_CHECK:
- case CORINFO_HELP_FIELD_ACCESS_CHECK:
- case CORINFO_HELP_CLASS_ACCESS_CHECK:
- case CORINFO_HELP_DELEGATE_SECURITY_CHECK:
- chkFlags |= GTF_EXCEPT;
- break;
- default:
- break;
- }
- }
-
if (call->gtCallObjp)
{
fgDebugCheckFlags(call->gtCallObjp);
@@ -20949,6 +21343,7 @@ void Compiler::fgDebugCheckFlags(GenTreePtr tree)
break;
case GT_ARR_OFFSET:
+
fgDebugCheckFlags(tree->gtArrOffs.gtOffset);
chkFlags |= (tree->gtArrOffs.gtOffset->gtFlags & GTF_ALL_EFFECT);
fgDebugCheckFlags(tree->gtArrOffs.gtIndex);
@@ -20957,7 +21352,55 @@ void Compiler::fgDebugCheckFlags(GenTreePtr tree)
chkFlags |= (tree->gtArrOffs.gtArrObj->gtFlags & GTF_ALL_EFFECT);
break;
+ case GT_ARR_BOUNDS_CHECK:
+#ifdef FEATURE_SIMD
+ case GT_SIMD_CHK:
+#endif // FEATURE_SIMD
+
+ GenTreeBoundsChk* bndsChk;
+ bndsChk = tree->AsBoundsChk();
+ fgDebugCheckFlags(bndsChk->gtIndex);
+ chkFlags |= (bndsChk->gtIndex->gtFlags & GTF_ALL_EFFECT);
+ fgDebugCheckFlags(bndsChk->gtArrLen);
+ chkFlags |= (bndsChk->gtArrLen->gtFlags & GTF_ALL_EFFECT);
+ break;
+
+ case GT_CMPXCHG:
+
+ chkFlags |= (GTF_GLOB_REF | GTF_ASG);
+ GenTreeCmpXchg* cmpXchg;
+ cmpXchg = tree->AsCmpXchg();
+ fgDebugCheckFlags(cmpXchg->gtOpLocation);
+ chkFlags |= (cmpXchg->gtOpLocation->gtFlags & GTF_ALL_EFFECT);
+ fgDebugCheckFlags(cmpXchg->gtOpValue);
+ chkFlags |= (cmpXchg->gtOpValue->gtFlags & GTF_ALL_EFFECT);
+ fgDebugCheckFlags(cmpXchg->gtOpComparand);
+ chkFlags |= (cmpXchg->gtOpComparand->gtFlags & GTF_ALL_EFFECT);
+ break;
+
+ case GT_STORE_DYN_BLK:
+ case GT_DYN_BLK:
+
+ GenTreeDynBlk* dynBlk;
+ dynBlk = tree->AsDynBlk();
+ fgDebugCheckFlags(dynBlk->gtDynamicSize);
+ chkFlags |= (dynBlk->gtDynamicSize->gtFlags & GTF_ALL_EFFECT);
+ fgDebugCheckFlags(dynBlk->Addr());
+ chkFlags |= (dynBlk->Addr()->gtFlags & GTF_ALL_EFFECT);
+ if (tree->OperGet() == GT_STORE_DYN_BLK)
+ {
+ fgDebugCheckFlags(dynBlk->Data());
+ chkFlags |= (dynBlk->Data()->gtFlags & GTF_ALL_EFFECT);
+ }
+ break;
+
default:
+
+#ifdef DEBUG
+ gtDispTree(tree);
+#endif
+
+ assert(!"Unknown operator for fgDebugCheckFlags");
break;
}
}
@@ -20997,19 +21440,11 @@ void Compiler::fgDebugCheckFlagsHelper(GenTreePtr tree, unsigned treeFlags, unsi
}
else if (treeFlags & ~chkFlags)
{
-#if 0
- // TODO-Cleanup:
- /* The tree has extra flags set. However, this will happen if we
- replace a subtree with something, but don't clear the flags up
- the tree. Can't flag this unless we start clearing flags above.
-
- Note: we need this working for GTF_CALL and CSEs, so I'm enabling
- it for calls.
- */
- if (tree->OperGet() != GT_CALL && (treeFlags & GTF_CALL) && !(chkFlags & GTF_CALL))
+ // TODO: We are currently only checking extra GTF_EXCEPT and GTF_ASG flags.
+ if ((treeFlags & ~chkFlags & ~GTF_GLOB_REF & ~GTF_ORDER_SIDEEFF & ~GTF_CALL) != 0)
{
// Print the tree so we can see it in the log.
- printf("Extra GTF_CALL flags on parent tree [%X]: ", tree);
+ printf("Extra flags on parent tree [%X]: ", tree);
GenTree::gtDispFlags(treeFlags & ~chkFlags, GTF_DEBUG_NONE);
printf("\n");
gtDispTree(tree);
@@ -21017,12 +21452,11 @@ void Compiler::fgDebugCheckFlagsHelper(GenTreePtr tree, unsigned treeFlags, unsi
noway_assert(!"Extra flags on tree");
// Print the tree again so we can see it right after we hook up the debugger.
- printf("Extra GTF_CALL flags on parent tree [%X]: ", tree);
+ printf("Extra flags on parent tree [%X]: ", tree);
GenTree::gtDispFlags(treeFlags & ~chkFlags, GTF_DEBUG_NONE);
printf("\n");
gtDispTree(tree);
- }
-#endif // 0
+ }
}
}
@@ -21779,8 +22213,8 @@ Compiler::fgWalkResult Compiler::fgUpdateInlineReturnExpressionPlaceHolder(GenTr
if ((parentTree != nullptr) && (parentTree->gtOper == GT_CALL))
{
- GenTreeCall* call = parentTree->AsCall();
- bool tryLateDevirt = call->IsVirtual() && (call->gtCallObjp == tree);
+ GenTreeCall* call = parentTree->AsCall();
+ bool tryLateDevirt = call->IsVirtual() && (call->gtCallObjp == tree) && (call->gtCallType == CT_USER_FUNC);
#ifdef DEBUG
tryLateDevirt = tryLateDevirt && (JitConfig.JitEnableLateDevirtualization() == 1);
@@ -22602,13 +23036,10 @@ GenTreePtr Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
impAssignTempGen(tmpNum, argNode, structHnd, (unsigned)CHECK_SPILL_NONE, &afterStmt, callILOffset,
block);
- // If we know the argument's value can't be
- // changed within the method body, try and improve
- // the type of the temp.
- if (argIsSingleDef && (argType == TYP_REF))
- {
- lvaUpdateClass(tmpNum, argNode);
- }
+ // We used to refine the temp type here based on
+ // the actual arg, but we now do this up front, when
+ // creating the temp, over in impInlineFetchArg.
+ CLANG_FORMAT_COMMENT_ANCHOR;
#ifdef DEBUG
if (verbose)
@@ -22664,6 +23095,12 @@ GenTreePtr Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
}
#endif // DEBUG
}
+ else if (argNode->IsBoxedValue())
+ {
+ // Try to clean up any unnecessary boxing side effects
+ // since the box itself will be ignored.
+ gtTryRemoveBoxUpstreamEffects(argNode);
+ }
}
}
}
@@ -22742,14 +23179,17 @@ GenTreePtr Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo)
CORINFO_CLASS_HANDLE structType =
lclVarInfo[lclNum + inlineInfo->argCnt].lclVerTypeInfo.GetClassHandle();
- tree = gtNewBlkOpNode(gtNewLclvNode(tmpNum, lclTyp), // Dest
- gtNewIconNode(0), // Value
- info.compCompHnd->getClassSize(structType), // Size
- false, // isVolatile
- false); // not copyBlock
-
- newStmt = gtNewStmt(tree, callILOffset);
- afterStmt = fgInsertStmtAfter(block, afterStmt, newStmt);
+ if (fgStructTempNeedsExplicitZeroInit(lvaTable + tmpNum, block))
+ {
+ tree = gtNewBlkOpNode(gtNewLclvNode(tmpNum, lclTyp), // Dest
+ gtNewIconNode(0), // Value
+ info.compCompHnd->getClassSize(structType), // Size
+ false, // isVolatile
+ false); // not copyBlock
+
+ newStmt = gtNewStmt(tree, callILOffset);
+ afterStmt = fgInsertStmtAfter(block, afterStmt, newStmt);
+ }
}
#ifdef DEBUG
@@ -24393,13 +24833,49 @@ void Compiler::fgUpdateFinallyTargetFlags()
JITDUMP("In fgUpdateFinallyTargetFlags, updating finally target flag bits\n");
+ fgClearAllFinallyTargetBits();
+ fgAddFinallyTargetFlags();
+
+#endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+}
+
+//------------------------------------------------------------------------
+// fgClearAllFinallyTargetBits: Clear all BBF_FINALLY_TARGET bits; these will need to be
+// recomputed later.
+//
+void Compiler::fgClearAllFinallyTargetBits()
+{
+#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+
+ JITDUMP("*************** In fgClearAllFinallyTargetBits()\n");
+
+ // Note that we clear the flags even if there are no EH clauses (compHndBBtabCount == 0)
+ // in case bits are left over from EH clauses being deleted.
+
// Walk all blocks, and reset the target bits.
for (BasicBlock* block = fgFirstBB; block != nullptr; block = block->bbNext)
{
block->bbFlags &= ~BBF_FINALLY_TARGET;
}
- // Walk all blocks again, and set the target bits.
+#endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+}
+
+//------------------------------------------------------------------------
+// fgAddFinallyTargetFlags: Add BBF_FINALLY_TARGET bits to all finally targets.
+//
+void Compiler::fgAddFinallyTargetFlags()
+{
+#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+
+ JITDUMP("*************** In fgAddFinallyTargetFlags()\n");
+
+ if (compHndBBtabCount == 0)
+ {
+ JITDUMP("No EH in this method, no flags to set.\n");
+ return;
+ }
+
for (BasicBlock* block = fgFirstBB; block != nullptr; block = block->bbNext)
{
if (block->isBBCallAlwaysPair())
diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp
index 9503206874..8aa255f520 100644
--- a/src/jit/gentree.cpp
+++ b/src/jit/gentree.cpp
@@ -299,6 +299,7 @@ void GenTree::InitNodeSize()
GenTree::s_gtNodeSizes[GT_FTN_ADDR] = TREE_NODE_SZ_LARGE;
GenTree::s_gtNodeSizes[GT_BOX] = TREE_NODE_SZ_LARGE;
GenTree::s_gtNodeSizes[GT_INDEX] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_INDEX_ADDR] = TREE_NODE_SZ_LARGE;
GenTree::s_gtNodeSizes[GT_ARR_BOUNDS_CHECK] = TREE_NODE_SZ_LARGE;
#ifdef FEATURE_SIMD
GenTree::s_gtNodeSizes[GT_SIMD_CHK] = TREE_NODE_SZ_LARGE;
@@ -610,42 +611,6 @@ void Compiler::fgWalkAllTreesPre(fgWalkPreFn* visitor, void* pCallBackData)
}
}
-// ------------------------------------------------------------------------------------------
-// gtClearReg: Sets the register to the "no register assignment" value, depending upon
-// the type of the node, and whether it fits any of the special cases for register pairs
-// or multi-reg call nodes.
-//
-// Arguments:
-// compiler - compiler instance
-//
-// Return Value:
-// None
-void GenTree::gtClearReg(Compiler* compiler)
-{
-#if CPU_LONG_USES_REGPAIR
- if (isRegPairType(TypeGet()) ||
- // (IsLocal() && isRegPairType(compiler->lvaTable[gtLclVarCommon.gtLclNum].TypeGet())) ||
- (OperGet() == GT_MUL && (gtFlags & GTF_MUL_64RSLT)))
- {
- gtRegPair = REG_PAIR_NONE;
- }
- else
-#endif // CPU_LONG_USES_REGPAIR
- {
- gtRegNum = REG_NA;
- }
-
- // Also clear multi-reg state if this is a call node
- if (IsCall())
- {
- this->AsCall()->ClearOtherRegs();
- }
- else if (IsCopyOrReload())
- {
- this->AsCopyOrReload()->ClearOtherRegs();
- }
-}
-
//-----------------------------------------------------------
// CopyReg: Copy the _gtRegNum/_gtRegPair/gtRegTag fields.
//
@@ -704,56 +669,52 @@ bool GenTree::gtHasReg() const
{
assert(_gtRegNum != REG_NA);
INDEBUG(assert(gtRegTag == GT_REGTAG_REGPAIR));
- hasReg = (gtRegPair != REG_PAIR_NONE);
+ return (gtRegPair != REG_PAIR_NONE);
}
- else
+ assert(_gtRegNum != REG_PAIR_NONE);
+ INDEBUG(assert(gtRegTag == GT_REGTAG_REG));
#endif
+ if (IsMultiRegCall())
{
- assert(_gtRegNum != REG_PAIR_NONE);
- INDEBUG(assert(gtRegTag == GT_REGTAG_REG));
+ // Have to cast away const-ness because GetReturnTypeDesc() is a non-const method
+ GenTree* tree = const_cast<GenTree*>(this);
+ GenTreeCall* call = tree->AsCall();
+ unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount();
+ hasReg = false;
- if (IsMultiRegCall())
+ // A Multi-reg call node is said to have regs, if it has
+ // reg assigned to each of its result registers.
+ for (unsigned i = 0; i < regCount; ++i)
{
- // Has to cast away const-ness because GetReturnTypeDesc() is a non-const method
- GenTree* tree = const_cast<GenTree*>(this);
- GenTreeCall* call = tree->AsCall();
- unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount();
- hasReg = false;
-
- // A Multi-reg call node is said to have regs, if it has
- // reg assigned to each of its result registers.
- for (unsigned i = 0; i < regCount; ++i)
+ hasReg = (call->GetRegNumByIdx(i) != REG_NA);
+ if (!hasReg)
{
- hasReg = (call->GetRegNumByIdx(i) != REG_NA);
- if (!hasReg)
- {
- break;
- }
+ break;
}
}
- else if (IsCopyOrReloadOfMultiRegCall())
- {
- GenTree* tree = const_cast<GenTree*>(this);
- GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload();
- GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall();
- unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount();
- hasReg = false;
+ }
+ else if (IsCopyOrReloadOfMultiRegCall())
+ {
+ GenTree* tree = const_cast<GenTree*>(this);
+ GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload();
+ GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall();
+ unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount();
+ hasReg = false;
- // A Multi-reg copy or reload node is said to have regs,
- // if it has valid regs in any of the positions.
- for (unsigned i = 0; i < regCount; ++i)
+ // A Multi-reg copy or reload node is said to have regs,
+ // if it has valid regs in any of the positions.
+ for (unsigned i = 0; i < regCount; ++i)
+ {
+ hasReg = (copyOrReload->GetRegNumByIdx(i) != REG_NA);
+ if (hasReg)
{
- hasReg = (copyOrReload->GetRegNumByIdx(i) != REG_NA);
- if (hasReg)
- {
- break;
- }
+ break;
}
}
- else
- {
- hasReg = (gtRegNum != REG_NA);
- }
+ }
+ else
+ {
+ hasReg = (gtRegNum != REG_NA);
}
return hasReg;
@@ -885,6 +846,55 @@ bool GenTreeCall::IsPure(Compiler* compiler) const
compiler->s_helperCallProperties.IsPure(compiler->eeGetHelperNum(gtCallMethHnd));
}
+//-------------------------------------------------------------------------
+// HasSideEffects:
+// Returns true if this call has any side effects. All non-helpers are considered to have side-effects. Only helpers
+// that do not mutate the heap, do not run constructors, may not throw, and are either a) pure or b) non-finalizing
+// allocation functions are considered side-effect-free.
+//
+// Arguments:
+// compiler - the compiler instance
+// ignoreExceptions - when `true`, ignores exception side effects
+// ignoreCctors - when `true`, ignores class constructor side effects
+//
+// Return Value:
+// true if this call has any side-effects; false otherwise.
+bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool ignoreCctors) const
+{
+ // Generally all GT_CALL nodes are considered to have side-effects, but we may have extra information about helper
+ // calls that can prove them side-effect-free.
+ if (gtCallType != CT_HELPER)
+ {
+ return true;
+ }
+
+ CorInfoHelpFunc helper = compiler->eeGetHelperNum(gtCallMethHnd);
+ HelperCallProperties& helperProperties = compiler->s_helperCallProperties;
+
+ // We definitely care about the side effects if MutatesHeap is true
+ if (helperProperties.MutatesHeap(helper))
+ {
+ return true;
+ }
+
+ // Unless we have been instructed to ignore cctors (CSE, for example, ignores cctors), consider them side effects.
+ if (!ignoreCctors && helperProperties.MayRunCctor(helper))
+ {
+ return true;
+ }
+
+ // If we also care about exceptions then check if the helper can throw
+ if (!ignoreExceptions && !helperProperties.NoThrow(helper))
+ {
+ return true;
+ }
+
+ // If this is not a Pure helper call or an allocator (that will not need to run a finalizer)
+ // then this call has side effects.
+ return !helperProperties.IsPure(helper) &&
+ (!helperProperties.IsAllocator(helper) || helperProperties.MayFinalize(helper));
+}
+
#ifndef LEGACY_BACKEND
//-------------------------------------------------------------------------
@@ -1281,7 +1291,7 @@ AGAIN:
{
return false;
}
- if (op1->gtAddrMode.gtOffset != op2->gtAddrMode.gtOffset)
+ if (op1->gtAddrMode.Offset() != op2->gtAddrMode.Offset())
{
return false;
}
@@ -1292,6 +1302,12 @@ AGAIN:
return false;
}
break;
+ case GT_INDEX_ADDR:
+ if (op1->AsIndexAddr()->gtElemSize != op2->AsIndexAddr()->gtElemSize)
+ {
+ return false;
+ }
+ break;
// For the ones below no extra argument matters for comparison.
case GT_QMARK:
@@ -1867,6 +1883,9 @@ AGAIN:
case GT_INDEX:
hash += tree->gtIndex.gtIndElemSize;
break;
+ case GT_INDEX_ADDR:
+ hash += tree->AsIndexAddr()->gtElemSize;
+ break;
case GT_ALLOCOBJ:
hash = genTreeHashAdd(hash, static_cast<unsigned>(
reinterpret_cast<uintptr_t>(tree->gtAllocObj.gtAllocObjClsHnd)));
@@ -1907,7 +1926,7 @@ AGAIN:
hash += tree->gtIntrinsic.gtIntrinsicId;
break;
case GT_LEA:
- hash += (tree->gtAddrMode.gtOffset << 3) + tree->gtAddrMode.gtScale;
+ hash += static_cast<unsigned>(tree->gtAddrMode.Offset() << 3) + tree->gtAddrMode.gtScale;
break;
case GT_BLK:
@@ -1929,6 +1948,7 @@ AGAIN:
case GT_ARR_INDEX:
case GT_QMARK:
case GT_INDEX:
+ case GT_INDEX_ADDR:
break;
#ifdef FEATURE_SIMD
@@ -3375,7 +3395,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
case CORINFO_INTRINSIC_Ceiling:
case CORINFO_INTRINSIC_Floor:
case CORINFO_INTRINSIC_Object_GetType:
- // Giving intrinsics a large fixed exectuion cost is because we'd like to CSE
+ // Giving intrinsics a large fixed execution cost is because we'd like to CSE
// them, even if they are implemented by calls. This is different from modeling
// user calls since we never CSE user calls.
costEx = 36;
@@ -4767,6 +4787,23 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
}
break;
+ case GT_INDEX_ADDR:
+ costEx = 6; // cmp reg,reg; jae throw; mov reg, [addrmode] (not taken)
+ costSz = 9; // jump to cold section
+
+ level = gtSetEvalOrder(tree->AsIndexAddr()->Index());
+ costEx += tree->AsIndexAddr()->Index()->gtCostEx;
+ costSz += tree->AsIndexAddr()->Index()->gtCostSz;
+
+ lvl2 = gtSetEvalOrder(tree->AsIndexAddr()->Arr());
+ if (level < lvl2)
+ {
+ level = lvl2;
+ }
+ costEx += tree->AsIndexAddr()->Arr()->gtCostEx;
+ costSz += tree->AsIndexAddr()->Arr()->gtCostSz;
+ break;
+
default:
#ifdef DEBUG
if (verbose)
@@ -5410,6 +5447,19 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use)
case GT_FIELD_LIST:
return TryGetUseList(def, use);
+#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
+ case GT_PUTARG_SPLIT:
+ if (this->AsUnOp()->gtOp1->gtOper == GT_FIELD_LIST)
+ {
+ return TryGetUseList(def, use);
+ }
+ if (def == this->AsUnOp()->gtOp1)
+ {
+ *use = &this->AsUnOp()->gtOp1;
+ return true;
+ }
+ return false;
+#endif // !LEGACY_BACKEND && _TARGET_ARM_
#ifdef FEATURE_SIMD
case GT_SIMD:
@@ -5698,12 +5748,28 @@ GenTreePtr GenTree::gtGetParent(GenTreePtr** parentChildPtrPtr) const
return parent;
}
-/*****************************************************************************
- *
- * Returns true if the given operator may cause an exception.
- */
+//------------------------------------------------------------------------------
+// OperMayThrow : Check whether the operation requires GTF_ASG flag regardless
+// of the children's flags.
+//
+
+bool GenTree::OperRequiresAsgFlag()
+{
+ return ((OperKind() & GTK_ASGOP) || (gtOper == GT_XADD) || (gtOper == GT_XCHG) || (gtOper == GT_LOCKADD) ||
+ (gtOper == GT_CMPXCHG) || (gtOper == GT_MEMORYBARRIER));
+}
+
+//------------------------------------------------------------------------------
+// OperMayThrow : Check whether the operation may throw.
+//
+//
+// Arguments:
+// comp - Compiler instance
+//
+// Return Value:
+// True if the given operator may cause an exception
-bool GenTree::OperMayThrow()
+bool GenTree::OperMayThrow(Compiler* comp)
{
GenTreePtr op;
@@ -5730,30 +5796,6 @@ bool GenTree::OperMayThrow()
}
return true;
- case GT_IND:
- op = gtOp.gtOp1;
-
- /* Indirections of handles are known to be safe */
- if (op->gtOper == GT_CNS_INT)
- {
- if (op->IsIconHandle())
- {
- /* No exception is thrown on this indirection */
- return false;
- }
- }
- if (this->gtFlags & GTF_IND_NONFAULTING)
- {
- return false;
- }
- // Non-Null AssertionProp will remove the GTF_EXCEPT flag and mark the GT_IND with GTF_ORDER_SIDEEFF flag
- if ((this->gtFlags & GTF_ALL_EFFECT) == GTF_ORDER_SIDEEFF)
- {
- return false;
- }
-
- return true;
-
case GT_INTRINSIC:
// If this is an intrinsic that represents the object.GetType(), it can throw an NullReferenceException.
// Report it as may throw.
@@ -5769,23 +5811,34 @@ bool GenTree::OperMayThrow()
break;
+ case GT_CALL:
+
+ CorInfoHelpFunc helper;
+ helper = comp->eeGetHelperNum(this->AsCall()->gtCallMethHnd);
+ return ((helper == CORINFO_HELP_UNDEF) || !comp->s_helperCallProperties.NoThrow(helper));
+
+ case GT_IND:
case GT_BLK:
case GT_OBJ:
case GT_DYN_BLK:
case GT_STORE_BLK:
- return !Compiler::fgIsIndirOfAddrOfLocal(this);
+ case GT_NULLCHECK:
+ return (((this->gtFlags & GTF_IND_NONFAULTING) == 0) && comp->fgAddrCouldBeNull(this->AsIndir()->Addr()));
+
+ case GT_ARR_LENGTH:
+ return (((this->gtFlags & GTF_IND_NONFAULTING) == 0) && comp->fgAddrCouldBeNull(gtOp.gtOp1));
case GT_ARR_BOUNDS_CHECK:
case GT_ARR_ELEM:
case GT_ARR_INDEX:
+ case GT_ARR_OFFSET:
case GT_CATCH_ARG:
- case GT_ARR_LENGTH:
case GT_LCLHEAP:
case GT_CKFINITE:
- case GT_NULLCHECK:
#ifdef FEATURE_SIMD
case GT_SIMD_CHK:
#endif // FEATURE_SIMD
+ case GT_INDEX_ADDR:
return true;
default:
break;
@@ -6600,9 +6653,13 @@ GenTree* Compiler::gtNewObjNode(CORINFO_CLASS_HANDLE structHnd, GenTree* addr)
if ((addr->gtFlags & GTF_GLOB_REF) == 0)
{
GenTreeLclVarCommon* lclNode = addr->IsLocalAddrExpr();
- if ((lclNode != nullptr) && !lvaIsImplicitByRefLocal(lclNode->gtLclNum))
+ if (lclNode != nullptr)
{
- newBlkOrObjNode->gtFlags &= ~GTF_GLOB_REF;
+ newBlkOrObjNode->gtFlags |= GTF_IND_NONFAULTING;
+ if (!lvaIsImplicitByRefLocal(lclNode->gtLclNum))
+ {
+ newBlkOrObjNode->gtFlags &= ~GTF_GLOB_REF;
+ }
}
}
return newBlkOrObjNode;
@@ -6993,8 +7050,9 @@ GenTree* Compiler::gtNewBlkOpNode(
// gtNewPutArgReg: Creates a new PutArgReg node.
//
// Arguments:
-// type - The actual type of the argument
-// arg - The argument node
+// type - The actual type of the argument
+// arg - The argument node
+// argReg - The register that the argument will be passed in
//
// Return Value:
// Returns the newly created PutArgReg node.
@@ -7002,22 +7060,18 @@ GenTree* Compiler::gtNewBlkOpNode(
// Notes:
// The node is generated as GenTreeMultiRegOp on armel, as GenTreeOp on all the other archs
//
-GenTreePtr Compiler::gtNewPutArgReg(var_types type, GenTreePtr arg)
+GenTreePtr Compiler::gtNewPutArgReg(var_types type, GenTreePtr arg, regNumber argReg)
{
assert(arg != nullptr);
GenTreePtr node = nullptr;
-#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
+#if !defined(LEGACY_BACKEND) && defined(ARM_SOFTFP)
// A PUTARG_REG could be a MultiRegOp on armel since we could move a double register to two int registers.
- if (opts.compUseSoftFP)
- {
- node = new (this, GT_PUTARG_REG) GenTreeMultiRegOp(GT_PUTARG_REG, type, arg, nullptr);
- }
- else
+ node = new (this, GT_PUTARG_REG) GenTreeMultiRegOp(GT_PUTARG_REG, type, arg, nullptr);
+#else
+ node = gtNewOperNode(GT_PUTARG_REG, type, arg);
#endif
- {
- node = gtNewOperNode(GT_PUTARG_REG, type, arg);
- }
+ node->gtRegNum = argReg;
return node;
}
@@ -7392,6 +7446,19 @@ GenTreePtr Compiler::gtCloneExpr(
}
break;
+ case GT_INDEX_ADDR:
+ {
+ GenTreeIndexAddr* asIndAddr = tree->AsIndexAddr();
+
+ copy = new (this, GT_INDEX_ADDR)
+ GenTreeIndexAddr(asIndAddr->Arr(), asIndAddr->Index(), asIndAddr->gtElemType,
+ asIndAddr->gtStructElemClass, asIndAddr->gtElemSize, asIndAddr->gtLenOffset,
+ asIndAddr->gtElemOffset);
+ copy->AsIndexAddr()->gtIndRngFailBB = asIndAddr->gtIndRngFailBB;
+ copy->AsIndexAddr()->gtStkDepth = asIndAddr->gtStkDepth;
+ }
+ break;
+
case GT_ALLOCOBJ:
{
GenTreeAllocObj* asAllocObj = tree->AsAllocObj();
@@ -7401,8 +7468,7 @@ GenTreePtr Compiler::gtCloneExpr(
break;
case GT_ARR_LENGTH:
- copy = new (this, GT_ARR_LENGTH)
- GenTreeArrLen(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtArrLen.ArrLenOffset());
+ copy = gtNewArrLen(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtArrLen.ArrLenOffset());
break;
case GT_ARR_INDEX:
@@ -7441,7 +7507,8 @@ GenTreePtr Compiler::gtCloneExpr(
case GT_BOX:
copy = new (this, GT_BOX)
- GenTreeBox(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtBox.gtAsgStmtWhenInlinedBoxValue);
+ GenTreeBox(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtBox.gtAsgStmtWhenInlinedBoxValue,
+ tree->gtBox.gtCopyStmtWhenInlinedBoxValue);
break;
case GT_INTRINSIC:
@@ -7456,9 +7523,9 @@ GenTreePtr Compiler::gtCloneExpr(
case GT_LEA:
{
GenTreeAddrMode* addrModeOp = tree->AsAddrMode();
- copy =
- new (this, GT_LEA) GenTreeAddrMode(addrModeOp->TypeGet(), addrModeOp->Base(), addrModeOp->Index(),
- addrModeOp->gtScale, addrModeOp->gtOffset);
+ copy = new (this, GT_LEA)
+ GenTreeAddrMode(addrModeOp->TypeGet(), addrModeOp->Base(), addrModeOp->Index(), addrModeOp->gtScale,
+ static_cast<unsigned>(addrModeOp->Offset()));
}
break;
@@ -7549,14 +7616,14 @@ GenTreePtr Compiler::gtCloneExpr(
case GT_IND:
case GT_OBJ:
case GT_STORE_OBJ:
- if (tree->gtFlags & GTF_IND_ARR_INDEX)
+ {
+ ArrayInfo arrInfo;
+ if (!tree->AsIndir()->gtOp1->OperIs(GT_INDEX_ADDR) && TryGetArrayInfo(tree->AsIndir(), &arrInfo))
{
- ArrayInfo arrInfo;
- bool b = GetArrayInfoMap()->Lookup(tree, &arrInfo);
- assert(b);
GetArrayInfoMap()->Set(copy, arrInfo);
}
- break;
+ }
+ break;
default:
break;
@@ -7737,6 +7804,8 @@ GenTreePtr Compiler::gtCloneExpr(
gtCloneExpr(tree->gtBoundsChk.gtIndex, addFlags, deepVarNum, deepVarVal),
gtCloneExpr(tree->gtBoundsChk.gtArrLen, addFlags, deepVarNum, deepVarVal),
tree->gtBoundsChk.gtThrowKind);
+ copy->gtBoundsChk.gtIndRngFailBB = tree->gtBoundsChk.gtIndRngFailBB;
+ copy->gtBoundsChk.gtStkDepth = tree->gtBoundsChk.gtStkDepth;
break;
case GT_STORE_DYN_BLK:
@@ -7794,6 +7863,9 @@ DONE:
copy->gtOp.gtOp1->gtFlags |= GTF_RELOP_QMARK;
}
+ // Clear the copy's GTF_ASG and GTF_EXCEPT bits; these will instead be taken from the source.
+ copy->gtFlags &= ~(GTF_ASG | GTF_EXCEPT);
+
copy->gtFlags |= addFlags;
}
@@ -7830,6 +7902,7 @@ DONE:
//
// Notes:
// The caller must ensure that the original statement has been sequenced,
+// and the side effect flags are updated on the statement nodes,
// but this method will sequence 'replacementTree', and insert it into the
// proper place in the statement sequence.
@@ -7907,51 +7980,145 @@ GenTreePtr Compiler::gtReplaceTree(GenTreePtr stmt, GenTreePtr tree, GenTreePtr
treeLastNode->gtNext = treeNextNode;
treeNextNode->gtPrev = treeLastNode;
}
-
- // Propagate side-effect flags of "replacementTree" to its parents if needed.
- gtUpdateSideEffects(treeParent, tree->gtFlags, replacementTree->gtFlags);
}
return replacementTree;
}
//------------------------------------------------------------------------
-// gtUpdateSideEffects: Update the side effects for ancestors.
+// gtUpdateSideEffects: Update the side effects of a tree and its ancestors
//
// Arguments:
-// treeParent - The immediate parent node.
-// oldGtFlags - The stale gtFlags.
-// newGtFlags - The new gtFlags.
-//
+// stmt - The tree's statement
+// tree - Tree to update the side effects for
//
-// Assumptions:
-// Linear order of the stmt has been established.
+// Note: If tree's order hasn't been established, the method updates side effect
+// flags on all statement's nodes.
+
+void Compiler::gtUpdateSideEffects(GenTree* stmt, GenTree* tree)
+{
+ if (fgStmtListThreaded)
+ {
+ gtUpdateTreeAncestorsSideEffects(tree);
+ }
+ else
+ {
+ gtUpdateStmtSideEffects(stmt);
+ }
+}
+
+//------------------------------------------------------------------------
+// gtUpdateTreeAncestorsSideEffects: Update the side effects of a tree and its ancestors
+// when statement order has been established.
//
-// Notes:
-// The routine is used for updating the stale side effect flags for ancestor
-// nodes starting from treeParent up to the top-level stmt expr.
+// Arguments:
+// tree - Tree to update the side effects for
-void Compiler::gtUpdateSideEffects(GenTreePtr treeParent, unsigned oldGtFlags, unsigned newGtFlags)
+void Compiler::gtUpdateTreeAncestorsSideEffects(GenTree* tree)
{
assert(fgStmtListThreaded);
+ while (tree != nullptr)
+ {
+ gtResetNodeSideEffects(tree);
+ unsigned nChildren = tree->NumChildren();
+ for (unsigned childNum = 0; childNum < nChildren; childNum++)
+ {
+ tree->gtFlags |= (tree->GetChild(childNum)->gtFlags & GTF_ALL_EFFECT);
+ }
+ tree = tree->gtGetParent(nullptr);
+ }
+}
- oldGtFlags = oldGtFlags & GTF_ALL_EFFECT;
- newGtFlags = newGtFlags & GTF_ALL_EFFECT;
+//------------------------------------------------------------------------
+// gtUpdateStmtSideEffects: Update the side effects for statement tree nodes.
+//
+// Arguments:
+// stmt - The statement to update side effects on
+
+void Compiler::gtUpdateStmtSideEffects(GenTree* stmt)
+{
+ fgWalkTree(&stmt->gtStmt.gtStmtExpr, fgUpdateSideEffectsPre, fgUpdateSideEffectsPost);
+}
+
+//------------------------------------------------------------------------
+// gtResetNodeSideEffects: Update the side effects based on the node operation.
+//
+// Arguments:
+// tree - Tree to update the side effects on
+//
+// Notes:
+// This method currently only updates GTF_EXCEPT and GTF_ASG flags. The other side effect
+// flags may remain unnecessarily (conservatively) set.
+// The caller of this method is expected to update the flags based on the children's flags.
- if (oldGtFlags != newGtFlags)
+void Compiler::gtResetNodeSideEffects(GenTree* tree)
+{
+ if (tree->OperMayThrow(this))
{
- while (treeParent)
+ tree->gtFlags |= GTF_EXCEPT;
+ }
+ else
+ {
+ tree->gtFlags &= ~GTF_EXCEPT;
+ if (tree->OperIsIndirOrArrLength())
{
- treeParent->gtFlags &= ~oldGtFlags;
- treeParent->gtFlags |= newGtFlags;
- treeParent = treeParent->gtGetParent(nullptr);
+ tree->gtFlags |= GTF_IND_NONFAULTING;
}
}
+
+ if (tree->OperRequiresAsgFlag())
+ {
+ tree->gtFlags |= GTF_ASG;
+ }
+ else
+ {
+ tree->gtFlags &= ~GTF_ASG;
+ }
+}
+
+//------------------------------------------------------------------------
+// fgUpdateSideEffectsPre: Update the side effects based on the tree operation.
+//
+// Arguments:
+// pTree - Pointer to the tree to update the side effects
+// fgWalkPre - Walk data
+//
+// Notes:
+// This method currently only updates GTF_EXCEPT and GTF_ASG flags. The other side effect
+// flags may remain unnecessarily (conservatively) set.
+
+Compiler::fgWalkResult Compiler::fgUpdateSideEffectsPre(GenTree** pTree, fgWalkData* fgWalkPre)
+{
+ fgWalkPre->compiler->gtResetNodeSideEffects(*pTree);
+
+ return WALK_CONTINUE;
+}
+
+//------------------------------------------------------------------------
+// fgUpdateSideEffectsPost: Update the side effects of the parent based on the tree's flags.
+//
+// Arguments:
+// pTree - Pointer to the tree
+// fgWalkPost - Walk data
+//
+// Notes:
+// The routine is used for updating the stale side effect flags for ancestor
+// nodes starting from treeParent up to the top-level stmt expr.
+
+Compiler::fgWalkResult Compiler::fgUpdateSideEffectsPost(GenTree** pTree, fgWalkData* fgWalkPost)
+{
+ GenTree* tree = *pTree;
+ GenTree* parent = fgWalkPost->parent;
+ if (parent != nullptr)
+ {
+ parent->gtFlags |= (tree->gtFlags & GTF_ALL_EFFECT);
+ }
+ return WALK_CONTINUE;
}
/*****************************************************************************
*
- * Comapres two trees and returns true when both trees are the same.
+ * Compares two trees and returns true when both trees are the same.
* Instead of fully comparing the two trees this method can just return false.
* Thus callers should not assume that the trees are different when false is returned.
* Only when true is returned can the caller perform code optimizations.
@@ -9348,7 +9515,7 @@ void Compiler::gtDispNodeName(GenTree* tree)
{
bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "(i*%d)+", lea->gtScale);
}
- bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "%d)", lea->gtOffset);
+ bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "%d)", lea->Offset());
}
else if (tree->gtOper == GT_ARR_BOUNDS_CHECK)
{
@@ -9532,8 +9699,8 @@ void Compiler::gtDispNode(GenTreePtr tree, IndentStack* indentStack, __in __in_z
case GT_STORE_DYN_BLK:
case GT_IND:
- // We prefer printing R, V or U
- if ((tree->gtFlags & (GTF_IND_REFARR_LAYOUT | GTF_IND_VOLATILE | GTF_IND_UNALIGNED)) == 0)
+ // We prefer printing V or U
+ if ((tree->gtFlags & (GTF_IND_VOLATILE | GTF_IND_UNALIGNED)) == 0)
{
if (tree->gtFlags & GTF_IND_TGTANYWHERE)
{
@@ -9557,10 +9724,11 @@ void Compiler::gtDispNode(GenTreePtr tree, IndentStack* indentStack, __in __in_z
__fallthrough;
case GT_INDEX:
+ case GT_INDEX_ADDR:
if ((tree->gtFlags & (GTF_IND_VOLATILE | GTF_IND_UNALIGNED)) == 0) // We prefer printing V or U over R
{
- if (tree->gtFlags & GTF_IND_REFARR_LAYOUT)
+ if (tree->gtFlags & GTF_INX_REFARR_LAYOUT)
{
printf("R");
--msgLength;
@@ -9948,9 +10116,9 @@ void Compiler::gtDispRegVal(GenTree* tree)
#endif
#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
- if (tree->IsMultiReg())
+ if (tree->OperIsMultiRegOp() && tree->AsMultiRegOp()->gtOtherReg != REG_NA)
{
- printf(",%s", compRegVarName((regNumber)tree->AsMultiRegOp()->gtOtherReg));
+ printf(",%s", compRegVarName(tree->AsMultiRegOp()->gtOtherReg));
}
#endif
@@ -11944,10 +12112,7 @@ GenTreePtr Compiler::gtFoldExprCompare(GenTreePtr tree)
if (fgGlobalMorph)
{
- if (!fgIsInlining())
- {
- fgMorphTreeDone(cons);
- }
+ fgMorphTreeDone(cons);
}
else
{
@@ -12019,47 +12184,79 @@ GenTreePtr Compiler::gtFoldExprSpecial(GenTreePtr tree)
switch (oper)
{
-
case GT_EQ:
case GT_NE:
+ case GT_GT:
// Optimize boxed value classes; these are always false. This IL is
// generated when a generic value is tested against null:
// <T> ... foo(T x) { ... if ((object)x == null) ...
if (val == 0 && op->IsBoxedValue())
{
- // Change the assignment node so we don't generate any code for it.
+ JITDUMP("\nAttempting to optimize BOX(valueType) %s null [%06u]\n", GenTree::OpName(oper),
+ dspTreeID(tree));
- GenTreePtr asgStmt = op->gtBox.gtAsgStmtWhenInlinedBoxValue;
- assert(asgStmt->gtOper == GT_STMT);
- GenTreePtr asg = asgStmt->gtStmt.gtStmtExpr;
- assert(asg->gtOper == GT_ASG);
-#ifdef DEBUG
- if (verbose)
+ // We don't expect GT_GT with signed compares, and we
+ // can't predict the result if we do see it, since the
+ // boxed object addr could have its high bit set.
+ if ((oper == GT_GT) && !tree->IsUnsigned())
{
- printf("Bashing ");
- printTreeID(asg);
- printf(" to NOP as part of dead box operation\n");
- gtDispTree(tree);
+ JITDUMP(" bailing; unexpected signed compare via GT_GT\n");
}
-#endif
- asg->gtBashToNOP();
-
- op = gtNewIconNode(oper == GT_NE);
- if (fgGlobalMorph)
+ else
{
- if (!fgIsInlining())
+ // The tree under the box must be side effect free
+ // since we will drop it if we optimize.
+ assert(!gtTreeHasSideEffects(op->gtBox.gtOp.gtOp1, GTF_SIDE_EFFECT));
+
+ // See if we can optimize away the box and related statements.
+ GenTree* boxSourceTree = gtTryRemoveBoxUpstreamEffects(op);
+ bool didOptimize = (boxSourceTree != nullptr);
+
+ // If optimization succeeded, remove the box.
+ if (didOptimize)
{
- fgMorphTreeDone(op);
+ // Set up the result of the compare.
+ int compareResult = 0;
+ if (oper == GT_GT)
+ {
+ // GT_GT(null, box) == false
+ // GT_GT(box, null) == true
+ compareResult = (op1 == op);
+ }
+ else if (oper == GT_EQ)
+ {
+ // GT_EQ(box, null) == false
+ // GT_EQ(null, box) == false
+ compareResult = 0;
+ }
+ else
+ {
+ assert(oper == GT_NE);
+ // GT_NE(box, null) == true
+ // GT_NE(null, box) == true
+ compareResult = 1;
+ }
+
+ JITDUMP("\nSuccess: replacing BOX(valueType) %s null with %d\n", GenTree::OpName(oper),
+ compareResult);
+
+ op = gtNewIconNode(compareResult);
+
+ if (fgGlobalMorph)
+ {
+ fgMorphTreeDone(op);
+ }
+ else
+ {
+ op->gtNext = tree->gtNext;
+ op->gtPrev = tree->gtPrev;
+ }
+
+ return op;
}
}
- else
- {
- op->gtNext = tree->gtNext;
- op->gtPrev = tree->gtPrev;
- }
- fgSetStmtSeq(asgStmt);
- return op;
}
+
break;
case GT_ADD:
@@ -12250,6 +12447,369 @@ DONE_FOLD:
return op;
}
+//------------------------------------------------------------------------
+// gtTryRemoveBoxUpstreamEffects: given an unused value type box,
+// try and remove the upstream allocation and unnecessary parts of
+// the copy.
+//
+// Arguments:
+// op - the box node to optimize
+// options - controls whether and how trees are modified
+// (see notes)
+//
+// Return Value:
+// A tree representing the original value to box, if removal
+// is successful/possible (but see note). nullptr if removal fails.
+//
+// Notes:
+// Value typed box gets special treatment because it has associated
+// side effects that can be removed if the box result is not used.
+//
+// By default (options == BR_REMOVE_AND_NARROW) this method will
+// try and remove unnecessary trees and will try and reduce remaning
+// operations to the minimal set, possibly narrowing the width of
+// loads from the box source if it is a struct.
+//
+// To perform a trial removal, pass BR_DONT_REMOVE. This can be
+// useful to determine if this optimization should only be
+// performed if some other conditions hold true.
+//
+// To remove but not alter the access to the box source, pass
+// BR_REMOVE_BUT_NOT_NARROW.
+//
+// To remove and return the tree for the type handle used for
+// the boxed newobj, pass BR_REMOVE_BUT_NOT_NARROW_WANT_TYPE_HANDLE.
+// This can be useful when the only part of the box that is "live"
+// is its type.
+//
+// If removal fails, is is possible that a subsequent pass may be
+// able to optimize. Blocking side effects may now be minimized
+// (null or bounds checks might have been removed) or might be
+// better known (inline return placeholder updated with the actual
+// return expression). So the box is perhaps best left as is to
+// help trigger this re-examination.
+
+GenTree* Compiler::gtTryRemoveBoxUpstreamEffects(GenTree* op, BoxRemovalOptions options)
+{
+ JITDUMP("gtTryRemoveBoxUpstreamEffects called for [%06u]\n", dspTreeID(op));
+ assert(op->IsBoxedValue());
+
+ // grab related parts for the optimization
+ GenTree* asgStmt = op->gtBox.gtAsgStmtWhenInlinedBoxValue;
+ assert(asgStmt->gtOper == GT_STMT);
+ GenTree* copyStmt = op->gtBox.gtCopyStmtWhenInlinedBoxValue;
+ assert(copyStmt->gtOper == GT_STMT);
+
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf("\n%s to remove side effects of BOX (valuetype)\n",
+ options == BR_DONT_REMOVE ? "Checking if it is possible" : "Attempting");
+ gtDispTree(op);
+ printf("\nWith assign\n");
+ gtDispTree(asgStmt);
+ printf("\nAnd copy\n");
+ gtDispTree(copyStmt);
+ }
+#endif
+
+ // If we don't recognize the form of the assign, bail.
+ GenTree* asg = asgStmt->gtStmt.gtStmtExpr;
+ if (asg->gtOper != GT_ASG)
+ {
+ JITDUMP(" bailing; unexpected assignment op %s\n", GenTree::OpName(asg->gtOper));
+ return nullptr;
+ }
+
+ // If we're eventually going to return the type handle, remember it now.
+ GenTree* boxTypeHandle = nullptr;
+ if (options == BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE)
+ {
+ // Note we might see GenTreeAllocObj here, if impImportAndPushBox
+ // starts using it instead of a bare helper call.
+ GenTree* asgSrc = asg->gtOp.gtOp2;
+ assert(asgSrc->IsCall());
+ GenTreeCall* newobjCall = asgSrc->AsCall();
+ GenTreeArgList* newobjArgs = newobjCall->gtCallArgs->AsArgList();
+ boxTypeHandle = newobjArgs->Current();
+ assert(boxTypeHandle != nullptr);
+ }
+
+ // If we don't recognize the form of the copy, bail.
+ GenTree* copy = copyStmt->gtStmt.gtStmtExpr;
+ if (copy->gtOper != GT_ASG)
+ {
+ // GT_RET_EXPR is a tolerable temporary failure.
+ // The jit will revisit this optimization after
+ // inlining is done.
+ if (copy->gtOper == GT_RET_EXPR)
+ {
+ JITDUMP(" bailing; must wait for replacement of copy %s\n", GenTree::OpName(copy->gtOper));
+ }
+ else
+ {
+ // Anything else is a missed case we should
+ // figure out how to handle. One known case
+ // is GT_COMMAs enclosing the GT_ASG we are
+ // looking for.
+ JITDUMP(" bailing; unexpected copy op %s\n", GenTree::OpName(copy->gtOper));
+ }
+ return nullptr;
+ }
+
+ // If the copy is a struct copy, make sure we know how to isolate
+ // any source side effects.
+ GenTree* copySrc = copy->gtOp.gtOp2;
+
+ // If the copy source is from a pending inline, wait for it to resolve.
+ if (copySrc->gtOper == GT_RET_EXPR)
+ {
+ JITDUMP(" bailing; must wait for replacement of copy source %s\n", GenTree::OpName(copySrc->gtOper));
+ return nullptr;
+ }
+
+ bool hasSrcSideEffect = false;
+ bool isStructCopy = false;
+
+ if (gtTreeHasSideEffects(copySrc, GTF_SIDE_EFFECT))
+ {
+ hasSrcSideEffect = true;
+
+ if (copySrc->gtType == TYP_STRUCT)
+ {
+ isStructCopy = true;
+
+ if ((copySrc->gtOper != GT_OBJ) && (copySrc->gtOper != GT_IND) && (copySrc->gtOper != GT_FIELD))
+ {
+ // We don't know how to handle other cases, yet.
+ JITDUMP(" bailing; unexpected copy source struct op with side effect %s\n",
+ GenTree::OpName(copySrc->gtOper));
+ return nullptr;
+ }
+ }
+ }
+
+ // If this was a trial removal, we're done.
+ if (options == BR_DONT_REMOVE)
+ {
+ return copySrc;
+ }
+
+ // Otherwise, proceed with the optimization.
+ //
+ // Change the assignment expression to a NOP.
+ JITDUMP("\nBashing NEWOBJ [%06u] to NOP\n", dspTreeID(asg));
+ asg->gtBashToNOP();
+
+ // Change the copy expression so it preserves key
+ // source side effects.
+ JITDUMP("\nBashing COPY [%06u]", dspTreeID(copy));
+
+ if (!hasSrcSideEffect)
+ {
+ // If there were no copy source side effects just bash
+ // the copy to a NOP.
+ copy->gtBashToNOP();
+ JITDUMP(" to NOP; no source side effects.\n");
+ }
+ else if (!isStructCopy)
+ {
+ // For scalar types, go ahead and produce the
+ // value as the copy is fairly cheap and likely
+ // the optimizer can trim things down to just the
+ // minimal side effect parts.
+ copyStmt->gtStmt.gtStmtExpr = copySrc;
+ JITDUMP(" to scalar read via [%06u]\n", dspTreeID(copySrc));
+ }
+ else
+ {
+ // For struct types read the first byte of the
+ // source struct; there's no need to read the
+ // entire thing, and no place to put it.
+ assert(copySrc->gtOper == GT_OBJ || copySrc->gtOper == GT_IND || copySrc->gtOper == GT_FIELD);
+ copyStmt->gtStmt.gtStmtExpr = copySrc;
+
+ if (options == BR_REMOVE_AND_NARROW || options == BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE)
+ {
+ JITDUMP(" to read first byte of struct via modified [%06u]\n", dspTreeID(copySrc));
+ copySrc->ChangeOper(GT_IND);
+ copySrc->gtType = TYP_BYTE;
+ }
+ else
+ {
+ JITDUMP(" to read entire struct via modified [%06u]\n", dspTreeID(copySrc));
+ }
+ }
+
+ if (fgStmtListThreaded)
+ {
+ fgSetStmtSeq(asgStmt);
+ fgSetStmtSeq(copyStmt);
+ }
+
+ // Box effects were successfully optimized.
+
+ if (options == BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE)
+ {
+ return boxTypeHandle;
+ }
+ else
+ {
+ return copySrc;
+ }
+}
+
+//------------------------------------------------------------------------
+// gtOptimizeEnumHasFlag: given the operands for a call to Enum.HasFlag,
+// try and optimize the call to a simple and/compare tree.
+//
+// Arguments:
+// thisOp - first argument to the call
+// flagOp - second argument to the call
+//
+// Return Value:
+// A new cmp/amd tree if successful. nullptr on failure.
+//
+// Notes:
+// If successful, may allocate new temps and modify connected
+// statements.
+
+GenTree* Compiler::gtOptimizeEnumHasFlag(GenTree* thisOp, GenTree* flagOp)
+{
+ JITDUMP("Considering optimizing call to Enum.HasFlag....\n");
+
+ // Operands must be boxes
+ if (!thisOp->IsBoxedValue() || !flagOp->IsBoxedValue())
+ {
+ JITDUMP("bailing, need both inputs to be BOXes\n");
+ return nullptr;
+ }
+
+ // Operands must have same type
+ bool isExactThis = false;
+ bool isNonNullThis = false;
+ CORINFO_CLASS_HANDLE thisHnd = gtGetClassHandle(thisOp, &isExactThis, &isNonNullThis);
+
+ if (thisHnd == nullptr)
+ {
+ JITDUMP("bailing, can't find type for 'this' operand\n");
+ return nullptr;
+ }
+
+ // A boxed thisOp should have exact type and non-null instance
+ assert(isExactThis);
+ assert(isNonNullThis);
+
+ bool isExactFlag = false;
+ bool isNonNullFlag = false;
+ CORINFO_CLASS_HANDLE flagHnd = gtGetClassHandle(flagOp, &isExactFlag, &isNonNullFlag);
+
+ if (flagHnd == nullptr)
+ {
+ JITDUMP("bailing, can't find type for 'flag' operand\n");
+ return nullptr;
+ }
+
+ // A boxed flagOp should have exact type and non-null instance
+ assert(isExactFlag);
+ assert(isNonNullFlag);
+
+ if (flagHnd != thisHnd)
+ {
+ JITDUMP("bailing, operand types differ\n");
+ return nullptr;
+ }
+
+ // If we have a shared type instance we can't safely check type
+ // equality, so bail.
+ DWORD classAttribs = info.compCompHnd->getClassAttribs(thisHnd);
+ if (classAttribs & CORINFO_FLG_SHAREDINST)
+ {
+ JITDUMP("bailing, have shared instance type\n");
+ return nullptr;
+ }
+
+ // Simulate removing the box for thisOP. We need to know that it can
+ // be safely removed before we can optimize.
+ GenTree* thisVal = gtTryRemoveBoxUpstreamEffects(thisOp, BR_DONT_REMOVE);
+ if (thisVal == nullptr)
+ {
+ // Note we may fail here if the this operand comes from
+ // a call. We should be able to retry this post-inlining.
+ JITDUMP("bailing, can't undo box of 'this' operand\n");
+ return nullptr;
+ }
+
+ GenTree* flagVal = gtTryRemoveBoxUpstreamEffects(flagOp, BR_REMOVE_BUT_NOT_NARROW);
+ if (flagVal == nullptr)
+ {
+ // Note we may fail here if the flag operand comes from
+ // a call. We should be able to retry this post-inlining.
+ JITDUMP("bailing, can't undo box of 'flag' operand\n");
+ return nullptr;
+ }
+
+ // Yes, both boxes can be cleaned up. Optimize.
+ JITDUMP("Optimizing call to Enum.HasFlag\n");
+
+ // Undo the boxing of thisOp and prepare to operate directly
+ // on the original enum values.
+ thisVal = gtTryRemoveBoxUpstreamEffects(thisOp, BR_REMOVE_BUT_NOT_NARROW);
+
+ // Our trial removal above should guarantee successful removal here.
+ assert(thisVal != nullptr);
+
+ // We should have a consistent view of the type
+ var_types type = thisVal->TypeGet();
+ assert(type == flagVal->TypeGet());
+
+ // The thisVal and flagVal trees come from earlier statements.
+ //
+ // Unless they are invariant values, we need to evaluate them both
+ // to temps at those points to safely transmit the values here.
+ //
+ // Also we need to use the flag twice, so we need two trees for it.
+ GenTree* thisValOpt = nullptr;
+ GenTree* flagValOpt = nullptr;
+ GenTree* flagValOptCopy = nullptr;
+
+ if (thisVal->IsIntegralConst())
+ {
+ thisValOpt = gtClone(thisVal);
+ }
+ else
+ {
+ const unsigned thisTmp = lvaGrabTemp(true DEBUGARG("Enum:HasFlag this temp"));
+ GenTree* thisAsg = gtNewTempAssign(thisTmp, thisVal);
+ GenTree* thisAsgStmt = thisOp->AsBox()->gtCopyStmtWhenInlinedBoxValue;
+ thisAsgStmt->gtStmt.gtStmtExpr = thisAsg;
+ thisValOpt = gtNewLclvNode(thisTmp, type);
+ }
+
+ if (flagVal->IsIntegralConst())
+ {
+ flagValOpt = gtClone(flagVal);
+ flagValOptCopy = gtClone(flagVal);
+ }
+ else
+ {
+ const unsigned flagTmp = lvaGrabTemp(true DEBUGARG("Enum:HasFlag flag temp"));
+ GenTree* flagAsg = gtNewTempAssign(flagTmp, flagVal);
+ GenTree* flagAsgStmt = flagOp->AsBox()->gtCopyStmtWhenInlinedBoxValue;
+ flagAsgStmt->gtStmt.gtStmtExpr = flagAsg;
+ flagValOpt = gtNewLclvNode(flagTmp, type);
+ flagValOptCopy = gtNewLclvNode(flagTmp, type);
+ }
+
+ // Turn the call into (thisValTmp & flagTmp) == flagTmp.
+ GenTree* andTree = gtNewOperNode(GT_AND, type, thisValOpt, flagValOpt);
+ GenTree* cmpTree = gtNewOperNode(GT_EQ, TYP_INT, andTree, flagValOptCopy);
+
+ JITDUMP("Optimized call to Enum.HasFlag\n");
+
+ return cmpTree;
+}
+
/*****************************************************************************
*
* Fold the given constant tree.
@@ -13121,7 +13681,7 @@ GenTreePtr Compiler::gtFoldExprConst(GenTreePtr tree)
assert(op1);
op2 = op1;
- op1 = gtNewHelperCallNode(CORINFO_HELP_OVERFLOW, TYP_VOID, GTF_EXCEPT,
+ op1 = gtNewHelperCallNode(CORINFO_HELP_OVERFLOW, TYP_VOID,
gtNewArgList(gtNewIconNode(compCurBB->bbTryIndex)));
if (vnStore != nullptr)
@@ -13409,6 +13969,11 @@ GenTreePtr Compiler::gtFoldExprConst(GenTreePtr tree)
CNS_LONG:
+ if (fieldSeq != FieldSeqStore::NotAField())
+ {
+ return tree;
+ }
+
#ifdef DEBUG
if (verbose)
{
@@ -13642,13 +14207,33 @@ DONE:
#pragma warning(pop)
#endif
-/*****************************************************************************
- *
- * Create an assignment of the given value to a temp.
- */
+//------------------------------------------------------------------------
+// gtNewTempAssign: Create an assignment of the given value to a temp.
+//
+// Arguments:
+// tmp - local number for a compiler temp
+// val - value to assign to the temp
+//
+// Return Value:
+// Normally a new assignment node.
+// However may return a nop node if val is simply a reference to the temp.
+//
+// Notes:
+// Self-assignments may be represented via NOPs.
+//
+// May update the type of the temp, if it was previously unknown.
+//
+// May set compFloatingPointUsed.
+//
GenTreePtr Compiler::gtNewTempAssign(unsigned tmp, GenTreePtr val)
{
+ // Self-assignment is a nop.
+ if (val->OperGet() == GT_LCL_VAR && val->gtLclVarCommon.gtLclNum == tmp)
+ {
+ return gtNewNothingNode();
+ }
+
LclVarDsc* varDsc = lvaTable + tmp;
if (varDsc->TypeGet() == TYP_I_IMPL && val->TypeGet() == TYP_BYREF)
@@ -13835,7 +14420,7 @@ GenTreePtr Compiler::gtNewRefCOMfield(GenTreePtr objPtr,
args = gtNewListNode(objPtr, args);
}
- GenTreePtr tree = gtNewHelperCallNode(pFieldInfo->helper, genActualType(helperType), 0, args);
+ GenTreePtr tree = gtNewHelperCallNode(pFieldInfo->helper, genActualType(helperType), args);
if (pFieldInfo->fieldAccessor == CORINFO_FIELD_INSTANCE_HELPER)
{
@@ -13915,62 +14500,31 @@ bool Compiler::gtNodeHasSideEffects(GenTreePtr tree, unsigned flags)
{
if (tree->OperGet() == GT_CALL)
{
- // Generally all GT_CALL nodes are considered to have side-effects.
- // But we may have a helper call that doesn't have any important side effects.
- //
- if (tree->gtCall.gtCallType == CT_HELPER)
+ GenTreeCall* const call = tree->AsCall();
+ const bool ignoreExceptions = (flags & GTF_EXCEPT) == 0;
+ const bool ignoreCctors = (flags & GTF_IS_IN_CSE) != 0; // We can CSE helpers that run cctors.
+ if (!call->HasSideEffects(this, ignoreExceptions, ignoreCctors))
{
- // But if this tree is a helper call we may not care about the side-effects
- //
- CorInfoHelpFunc helper = eeGetHelperNum(tree->AsCall()->gtCallMethHnd);
-
- // We definitely care about the side effects if MutatesHeap is true
- //
- if (s_helperCallProperties.MutatesHeap(helper))
- {
- return true;
- }
-
- // with GTF_IS_IN_CSE we will CSE helper calls that can run cctors.
- //
- if (((flags & GTF_IS_IN_CSE) == 0) && (s_helperCallProperties.MayRunCctor(helper)))
- {
- return true;
- }
-
- // If we also care about exceptions then check if the helper can throw
- //
- if (((flags & GTF_EXCEPT) != 0) && !s_helperCallProperties.NoThrow(helper))
+ // If this call is otherwise side effect free, check its arguments.
+ for (GenTreeArgList* args = call->gtCallArgs; args != nullptr; args = args->Rest())
{
- return true;
- }
-
- // If this is a Pure helper call or an allocator (that will not need to run a finalizer)
- // then we don't need to preserve the side effects (of this call -- we may care about those of the
- // arguments).
- if (s_helperCallProperties.IsPure(helper) ||
- (s_helperCallProperties.IsAllocator(helper) && !s_helperCallProperties.MayFinalize(helper)))
- {
- GenTreeCall* call = tree->AsCall();
- for (GenTreeArgList* args = call->gtCallArgs; args != nullptr; args = args->Rest())
+ if (gtTreeHasSideEffects(args->Current(), flags))
{
- if (gtTreeHasSideEffects(args->Current(), flags))
- {
- return true;
- }
+ return true;
}
- // I'm a little worried that args that assign to temps that are late args will look like
- // side effects...but better to be conservative for now.
- for (GenTreeArgList* args = call->gtCallLateArgs; args != nullptr; args = args->Rest())
+ }
+ // I'm a little worried that args that assign to temps that are late args will look like
+ // side effects...but better to be conservative for now.
+ for (GenTreeArgList* args = call->gtCallLateArgs; args != nullptr; args = args->Rest())
+ {
+ if (gtTreeHasSideEffects(args->Current(), flags))
{
- if (gtTreeHasSideEffects(args->Current(), flags))
- {
- return true;
- }
+ return true;
}
- // Otherwise:
- return false;
}
+
+ // Otherwise:
+ return false;
}
// Otherwise the GT_CALL is considered to have side-effects.
@@ -13980,7 +14534,7 @@ bool Compiler::gtNodeHasSideEffects(GenTreePtr tree, unsigned flags)
if (flags & GTF_EXCEPT)
{
- if (tree->OperMayThrow())
+ if (tree->OperMayThrow(this))
{
return true;
}
@@ -14503,11 +15057,35 @@ void Compiler::gtCheckQuirkAddrExposedLclVar(GenTreePtr tree, GenTreeStack* pare
#endif
}
-// Checks to see if we're allowed to optimize Type::op_Equality or Type::op_Inequality on this operand.
-// We're allowed to convert to GT_EQ/GT_NE if one of the operands is:
-// 1) The result of Object::GetType
-// 2) The result of typeof(...)
-// 3) a local variable of type RuntimeType.
+//------------------------------------------------------------------------
+// gtCanOptimizeTypeEquality: check if tree is a suitable input for a type
+// equality optimization
+//
+// Arguments:
+// tree - tree to examine
+//
+// Return Value:
+// True, if value of tree can be used to optimize type equality tests
+//
+// Notes:
+// Checks to see if the jit is able to optimize Type::op_Equality or
+// Type::op_Inequality calls that consume this tree.
+//
+// The jit can safely convert these methods to GT_EQ/GT_NE if one of
+// the operands is:
+// 1) The result of Object::GetType
+// 2) The result of typeof(...)
+// 3) Is a null reference
+// 4) Is otherwise known to have type RuntimeType
+//
+// The null reference case is surprisingly common because operator
+// overloading turns the otherwise innocuous
+//
+// Type t = ....;
+// if (t == null)
+//
+// into a method call.
+
bool Compiler::gtCanOptimizeTypeEquality(GenTreePtr tree)
{
if (tree->gtOper == GT_CALL)
@@ -14531,15 +15109,19 @@ bool Compiler::gtCanOptimizeTypeEquality(GenTreePtr tree)
{
return true;
}
- else if (tree->gtOper == GT_LCL_VAR)
+ else if ((tree->gtOper == GT_CNS_INT) && (tree->gtIntCon.gtIconVal == 0))
{
- LclVarDsc* lcl = &(lvaTable[tree->gtLclVarCommon.gtLclNum]);
- if (lcl->TypeGet() == TYP_REF)
+ return true;
+ }
+ else
+ {
+ bool isExact = false;
+ bool isNonNull = false;
+ CORINFO_CLASS_HANDLE clsHnd = gtGetClassHandle(tree, &isExact, &isNonNull);
+
+ if (clsHnd == info.compCompHnd->getBuiltinClass(CLASSID_RUNTIME_TYPE))
{
- if (lcl->lvVerTypeInfo.GetClassHandle() == info.compCompHnd->getBuiltinClass(CLASSID_RUNTIME_TYPE))
- {
- return true;
- }
+ return true;
}
}
return false;
@@ -15031,10 +15613,13 @@ bool GenTree::isContained() const
}
// these actually produce a register (the flags reg, we just don't model it)
- // and are a separate instruction from the branch that consumes the result
+ // and are a separate instruction from the branch that consumes the result.
+ // They can only produce a result if the child is a SIMD equality comparison.
else if (OperKind() & GTK_RELOP)
{
- assert(!isMarkedContained);
+ // We have to cast away const-ness since AsOp() method is non-const.
+ GenTree* childNode = const_cast<GenTree*>(this)->AsOp()->gtOp1;
+ assert((isMarkedContained == false) || childNode->IsSIMDEqualityOrInequality());
}
// these either produce a result in register or set flags reg.
@@ -15137,15 +15722,15 @@ unsigned GenTreeIndir::Scale()
}
}
-size_t GenTreeIndir::Offset()
+ssize_t GenTreeIndir::Offset()
{
if (isIndirAddrMode())
{
- return Addr()->AsAddrMode()->gtOffset;
+ return Addr()->AsAddrMode()->Offset();
}
else if (Addr()->gtOper == GT_CLS_VAR_ADDR)
{
- return (size_t)Addr()->gtClsVar.gtClsVarHnd;
+ return static_cast<ssize_t>(reinterpret_cast<intptr_t>(Addr()->gtClsVar.gtClsVarHnd));
}
else if (Addr()->IsCnsIntOrI() && Addr()->isContained())
{
@@ -15398,10 +15983,16 @@ bool GenTree::IsFieldAddr(Compiler* comp, GenTreePtr* pObj, GenTreePtr* pStatic,
}
else if (OperGet() == GT_ADD)
{
- // op1 should never be a field sequence (or any other kind of handle)
- assert((gtOp.gtOp1->gtOper != GT_CNS_INT) || !gtOp.gtOp1->IsIconHandle());
- if (gtOp.gtOp2->OperGet() == GT_CNS_INT)
+ // If one operator is a field sequence/handle, the other operator must not also be a field sequence/handle.
+ if ((gtOp.gtOp1->OperGet() == GT_CNS_INT) && gtOp.gtOp1->IsIconHandle())
+ {
+ assert((gtOp.gtOp2->gtOper != GT_CNS_INT) || !gtOp.gtOp2->IsIconHandle());
+ newFldSeq = gtOp.gtOp1->AsIntCon()->gtFieldSeq;
+ baseAddr = gtOp.gtOp2;
+ }
+ else if (gtOp.gtOp2->OperGet() == GT_CNS_INT)
{
+ assert((gtOp.gtOp1->gtOper != GT_CNS_INT) || !gtOp.gtOp1->IsIconHandle());
newFldSeq = gtOp.gtOp2->AsIntCon()->gtFieldSeq;
baseAddr = gtOp.gtOp1;
}
@@ -15491,6 +16082,9 @@ CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleIfPresent(GenTree* tree)
case GT_INDEX:
structHnd = tree->gtIndex.gtStructElemClass;
break;
+ case GT_INDEX_ADDR:
+ structHnd = tree->AsIndexAddr()->gtStructElemClass;
+ break;
case GT_FIELD:
info.compCompHnd->getFieldType(tree->gtField.gtFldHnd, &structHnd);
break;
@@ -15519,12 +16113,12 @@ CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleIfPresent(GenTree* tree)
}
else
#endif
- if (tree->gtFlags & GTF_IND_ARR_INDEX)
{
ArrayInfo arrInfo;
- bool b = GetArrayInfoMap()->Lookup(tree, &arrInfo);
- assert(b);
- structHnd = EncodeElemType(arrInfo.m_elemType, arrInfo.m_elemStructType);
+ if (TryGetArrayInfo(tree->AsIndir(), &arrInfo))
+ {
+ structHnd = EncodeElemType(arrInfo.m_elemType, arrInfo.m_elemStructType);
+ }
}
break;
#ifdef FEATURE_SIMD
@@ -15744,6 +16338,21 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTreePtr tree, bool* isExact,
break;
}
+ case GT_BOX:
+ {
+ // Box should just wrap a local var reference which has
+ // the type we're looking for. Also box only represents a
+ // non-nullable value type so result cannot be null.
+ GenTreeBox* box = obj->AsBox();
+ GenTree* boxTemp = box->BoxOp();
+ assert(boxTemp->IsLocal());
+ const unsigned boxTempLcl = boxTemp->AsLclVar()->GetLclNum();
+ objClass = lvaTable[boxTempLcl].lvClassHnd;
+ *isExact = lvaTable[boxTempLcl].lvClassIsExact;
+ *isNonNull = true;
+ break;
+ }
+
default:
{
break;
diff --git a/src/jit/gentree.h b/src/jit/gentree.h
index 33d6b203fb..a8911d0333 100644
--- a/src/jit/gentree.h
+++ b/src/jit/gentree.h
@@ -727,9 +727,6 @@ public:
// Copy the _gtRegNum/_gtRegPair/gtRegTag fields
void CopyReg(GenTreePtr from);
-
- void gtClearReg(Compiler* compiler);
-
bool gtHasReg() const;
regMaskTP gtGetRegMask() const;
@@ -865,6 +862,7 @@ public:
#define GTF_ZSF_SET 0x00000400 // the zero(ZF) and sign(SF) flags set to the operand
#define GTF_SET_FLAGS 0x00000800 // Requires that codegen for this node set the flags. Use gtSetFlags() to check this flag.
+#define GTF_USE_FLAGS 0x00001000 // Indicates that this node uses the flags bits.
#define GTF_MAKE_CSE 0x00002000 // Hoisted expression: try hard to make this into CSE (see optPerformHoistExpr)
#define GTF_DONT_CSE 0x00004000 // Don't bother CSE'ing this expr
@@ -945,9 +943,11 @@ public:
#define GTF_INX_REFARR_LAYOUT 0x20000000 // GT_INDEX -- same as GTF_IND_REFARR_LAYOUT
#define GTF_INX_STRING_LAYOUT 0x40000000 // GT_INDEX -- this uses the special string array layout
-#define GTF_IND_NONFAULTING GTF_SET_FLAGS // GT_IND -- An indir that cannot fault. GTF_SET_FLAGS is not used on indirs.
+#define GTF_IND_ARR_LEN 0x80000000 // GT_IND -- the indirection represents an array length (of the REF
+ // contribution to its argument).
#define GTF_IND_VOLATILE 0x40000000 // GT_IND -- the load or store must use volatile sematics (this is a nop on X86)
-#define GTF_IND_REFARR_LAYOUT 0x20000000 // GT_IND -- the array holds object refs (only affects layout of Arrays)
+#define GTF_IND_NONFAULTING 0x20000000 // Operations for which OperIsIndir() is true -- An indir that cannot fault.
+ // Same as GTF_ARRLEN_NONFAULTING.
#define GTF_IND_TGTANYWHERE 0x10000000 // GT_IND -- the target could be anywhere
#define GTF_IND_TLS_REF 0x08000000 // GT_IND -- the target is accessed via TLS
#define GTF_IND_ASG_LHS 0x04000000 // GT_IND -- this GT_IND node is (the effective val) of the LHS of an
@@ -961,12 +961,10 @@ public:
#define GTF_IND_UNALIGNED 0x02000000 // GT_IND -- the load or store is unaligned (we assume worst case
// alignment of 1 byte)
#define GTF_IND_INVARIANT 0x01000000 // GT_IND -- the target is invariant (a prejit indirection)
-#define GTF_IND_ARR_LEN 0x80000000 // GT_IND -- the indirection represents an array length (of the REF
- // contribution to its argument).
#define GTF_IND_ARR_INDEX 0x00800000 // GT_IND -- the indirection represents an (SZ) array index
#define GTF_IND_FLAGS \
- (GTF_IND_VOLATILE | GTF_IND_REFARR_LAYOUT | GTF_IND_TGTANYWHERE | GTF_IND_NONFAULTING | GTF_IND_TLS_REF | \
+ (GTF_IND_VOLATILE | GTF_IND_TGTANYWHERE | GTF_IND_NONFAULTING | GTF_IND_TLS_REF | \
GTF_IND_UNALIGNED | GTF_IND_INVARIANT | GTF_IND_ARR_INDEX)
#define GTF_CLS_VAR_VOLATILE 0x40000000 // GT_FIELD/GT_CLS_VAR -- same as GTF_IND_VOLATILE
@@ -993,6 +991,8 @@ public:
#define GTF_RELOP_ZTT 0x08000000 // GT_<relop> -- Loop test cloned for converting while-loops into do-while
// with explicit "loop test" in the header block.
+#define GTF_RET_MERGED 0x80000000 // GT_RETURN -- This is a return generated during epilog merging.
+
#define GTF_QMARK_CAST_INSTOF 0x80000000 // GT_QMARK -- Is this a top (not nested) level qmark created for
// castclass or instanceof?
@@ -1036,6 +1036,7 @@ public:
#define GTF_ARR_BOUND_INBND 0x80000000 // GT_ARR_BOUNDS_CHECK -- have proved this check is always in-bounds
#define GTF_ARRLEN_ARR_IDX 0x80000000 // GT_ARR_LENGTH -- Length which feeds into an array index expression
+#define GTF_ARRLEN_NONFAULTING 0x20000000 // GT_ARR_LENGTH -- An array length operation that cannot fault. Same as GT_IND_NONFAULTING.
#define GTF_FIELD_LIST_HEAD 0x80000000 // GT_FIELD_LIST -- Indicates that this is the first field in a list of
// struct fields constituting a single call argument.
@@ -1333,6 +1334,22 @@ public:
return OperIsPutArgStk() || OperIsPutArgReg() || OperIsPutArgSplit();
}
+ bool OperIsMultiRegOp() const
+ {
+#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
+#ifdef ARM_SOFTFP
+ if (gtOper == GT_MUL_LONG || gtOper == GT_PUTARG_REG)
+#else
+ if (gtOper == GT_MUL_LONG)
+#endif
+ {
+ return true;
+ }
+#endif
+
+ return false;
+ }
+
bool OperIsAddrMode() const
{
return OperIsAddrMode(OperGet());
@@ -1425,6 +1442,20 @@ public:
|| OperIsShiftOrRotate(op);
}
+#ifdef _TARGET_XARCH_
+ static bool OperIsRMWMemOp(genTreeOps gtOper)
+ {
+ // Return if binary op is one of the supported operations for RMW of memory.
+ return (gtOper == GT_ADD || gtOper == GT_SUB || gtOper == GT_AND || gtOper == GT_OR || gtOper == GT_XOR ||
+ gtOper == GT_NOT || gtOper == GT_NEG || OperIsShiftOrRotate(gtOper));
+ }
+ bool OperIsRMWMemOp() const
+ {
+ // Return if binary op is one of the supported operations for RMW of memory.
+ return OperIsRMWMemOp(gtOper);
+ }
+#endif // _TARGET_XARCH_
+
#if !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_)
static bool OperIsHigh(genTreeOps gtOper)
{
@@ -1515,11 +1546,21 @@ public:
return gtOper == GT_IND || gtOper == GT_STOREIND || gtOper == GT_NULLCHECK || OperIsBlk(gtOper);
}
+ static bool OperIsIndirOrArrLength(genTreeOps gtOper)
+ {
+ return OperIsIndir(gtOper) || (gtOper == GT_ARR_LENGTH);
+ }
+
bool OperIsIndir() const
{
return OperIsIndir(gtOper);
}
+ bool OperIsIndirOrArrLength() const
+ {
+ return OperIsIndirOrArrLength(gtOper);
+ }
+
static bool OperIsImplicitIndir(genTreeOps gtOper)
{
switch (gtOper)
@@ -1756,10 +1797,9 @@ public:
// Returns true if it is a GT_COPY or GT_RELOAD of a multi-reg call node
inline bool IsCopyOrReloadOfMultiRegCall() const;
- // Returns true if it is a MultiRegOp
- inline bool IsMultiReg() const;
+ bool OperRequiresAsgFlag();
- bool OperMayThrow();
+ bool OperMayThrow(Compiler* comp);
unsigned GetScaleIndexMul();
unsigned GetScaleIndexShf();
@@ -1986,13 +2026,15 @@ public:
void SetContained()
{
+ assert(IsValue());
gtFlags |= GTF_CONTAINED;
}
void ClearContained()
{
+ assert(IsValue());
gtFlags &= ~GTF_CONTAINED;
- gtLsraInfo.regOptional = false;
+ ClearRegOptional();
}
#endif // !LEGACY_BACKEND
@@ -2102,6 +2144,12 @@ public:
// that codegen can still generate code even if it wasn't allocated a
// register.
bool IsRegOptional() const;
+#ifndef LEGACY_BACKEND
+ void ClearRegOptional()
+ {
+ gtLsraInfo.regOptional = false;
+ }
+#endif
// Returns "true" iff "this" is a phi-related node (i.e. a GT_PHI_ARG, GT_PHI, or a PhiDefn).
bool IsPhiNode();
@@ -2212,6 +2260,12 @@ public:
gtFlags &= ~GTF_REUSE_REG_VAL;
}
+ void SetIndirExceptionFlags(Compiler* comp)
+ {
+ assert(OperIsIndirOrArrLength());
+ gtFlags |= OperMayThrow(comp) ? GTF_EXCEPT : GTF_IND_NONFAULTING;
+ }
+
#if MEASURE_NODE_SIZE
static void DumpNodeSizes(FILE* fp);
#endif
@@ -2965,9 +3019,16 @@ struct GenTreeBox : public GenTreeUnOp
// This is the statement that contains the assignment tree when the node is an inlined GT_BOX on a value
// type
GenTreePtr gtAsgStmtWhenInlinedBoxValue;
+ // And this is the statement that copies from the value being boxed to the box payload
+ GenTreePtr gtCopyStmtWhenInlinedBoxValue;
- GenTreeBox(var_types type, GenTreePtr boxOp, GenTreePtr asgStmtWhenInlinedBoxValue)
- : GenTreeUnOp(GT_BOX, type, boxOp), gtAsgStmtWhenInlinedBoxValue(asgStmtWhenInlinedBoxValue)
+ GenTreeBox(var_types type,
+ GenTreePtr boxOp,
+ GenTreePtr asgStmtWhenInlinedBoxValue,
+ GenTreePtr copyStmtWhenInlinedBoxValue)
+ : GenTreeUnOp(GT_BOX, type, boxOp)
+ , gtAsgStmtWhenInlinedBoxValue(asgStmtWhenInlinedBoxValue)
+ , gtCopyStmtWhenInlinedBoxValue(copyStmtWhenInlinedBoxValue)
{
}
#if DEBUGGABLE_GENTREE
@@ -3093,6 +3154,11 @@ struct GenTreeFieldList : public GenTreeArgList
{
prevList->gtOp2 = this;
}
+#ifndef LEGACY_BACKEND
+ // A GT_FIELD_LIST is always contained. Note that this should only matter for the head node, but
+ // the list may be reordered.
+ gtFlags |= GTF_CONTAINED;
+#endif
}
};
@@ -3494,8 +3560,10 @@ struct GenTreeCall final : public GenTree
bits |= PACKED_GTF_SPILLED;
}
+ const unsigned char packedFlags = PACKED_GTF_SPILL | PACKED_GTF_SPILLED;
+
// Clear anything that was already there by masking out the bits before 'or'ing in what we want there.
- gtSpillFlags = (unsigned char)((gtSpillFlags & ~(0xffU << (idx * 2))) | (bits << (idx * 2)));
+ gtSpillFlags = (unsigned char)((gtSpillFlags & ~(packedFlags << (idx * 2))) | (bits << (idx * 2)));
#else
unreached();
#endif
@@ -3786,6 +3854,8 @@ struct GenTreeCall final : public GenTree
bool IsPure(Compiler* compiler) const;
+ bool HasSideEffects(Compiler* compiler, bool ignoreExceptions = false, bool ignoreCctors = false) const;
+
void ClearFatPointerCandidate()
{
gtCallMoreFlags &= ~GTF_CALL_M_FAT_POINTER_CHECK;
@@ -3873,7 +3943,7 @@ struct GenTreeCmpXchg : public GenTree
{
// There's no reason to do a compare-exchange on a local location, so we'll assume that all of these
// have global effects.
- gtFlags |= GTF_GLOB_EFFECT;
+ gtFlags |= (GTF_GLOB_REF | GTF_ASG);
}
#if DEBUGGABLE_GENTREE
GenTreeCmpXchg() : GenTree()
@@ -3887,9 +3957,148 @@ struct GenTreeMultiRegOp : public GenTreeOp
{
regNumber gtOtherReg;
+ // GTF_SPILL or GTF_SPILLED flag on a multi-reg call node indicates that one or
+ // more of its result regs are in that state. The spill flag of each of the
+ // return register is stored here. We only need 2 bits per returned register,
+ // so this is treated as a 2-bit array. No architecture needs more than 8 bits.
+
+ static const unsigned PACKED_GTF_SPILL = 1;
+ static const unsigned PACKED_GTF_SPILLED = 2;
+ unsigned char gtSpillFlags;
+
GenTreeMultiRegOp(genTreeOps oper, var_types type, GenTreePtr op1, GenTreePtr op2)
: GenTreeOp(oper, type, op1, op2), gtOtherReg(REG_NA)
{
+ ClearOtherRegFlags();
+ }
+
+ unsigned GetRegCount() const
+ {
+ if (gtRegNum == REG_NA || gtRegNum == REG_STK)
+ {
+ return 0;
+ }
+ return (gtOtherReg == REG_NA || gtOtherReg == REG_STK) ? 1 : 2;
+ }
+
+ //---------------------------------------------------------------------------
+ // GetRegNumByIdx: get ith register allocated to this struct argument.
+ //
+ // Arguments:
+ // idx - index of the register
+ //
+ // Return Value:
+ // Return regNumber of ith register of this register argument
+ //
+ regNumber GetRegNumByIdx(unsigned idx) const
+ {
+ assert(idx < 2);
+
+ if (idx == 0)
+ {
+ return gtRegNum;
+ }
+
+ return gtOtherReg;
+ }
+
+ //----------------------------------------------------------------------
+ // GetRegSpillFlagByIdx: get spill flag associated with the register
+ // specified by its index.
+ //
+ // Arguments:
+ // idx - Position or index of the register
+ //
+ // Return Value:
+ // Returns GTF_* flags associated with the register. Only GTF_SPILL and GTF_SPILLED are considered.
+ //
+ unsigned GetRegSpillFlagByIdx(unsigned idx) const
+ {
+ assert(idx < MAX_REG_ARG);
+
+ unsigned bits = gtSpillFlags >> (idx * 2); // It doesn't matter that we possibly leave other high bits here.
+ unsigned spillFlags = 0;
+ if (bits & PACKED_GTF_SPILL)
+ {
+ spillFlags |= GTF_SPILL;
+ }
+ if (bits & PACKED_GTF_SPILLED)
+ {
+ spillFlags |= GTF_SPILLED;
+ }
+
+ return spillFlags;
+ }
+
+ //----------------------------------------------------------------------
+ // SetRegSpillFlagByIdx: set spill flags for the register
+ // specified by its index.
+ //
+ // Arguments:
+ // flags - GTF_* flags. Only GTF_SPILL and GTF_SPILLED are allowed.
+ // idx - Position or index of the register
+ //
+ // Return Value:
+ // None
+ //
+ void SetRegSpillFlagByIdx(unsigned flags, unsigned idx)
+ {
+ assert(idx < MAX_REG_ARG);
+
+ unsigned bits = 0;
+ if (flags & GTF_SPILL)
+ {
+ bits |= PACKED_GTF_SPILL;
+ }
+ if (flags & GTF_SPILLED)
+ {
+ bits |= PACKED_GTF_SPILLED;
+ }
+
+ const unsigned char packedFlags = PACKED_GTF_SPILL | PACKED_GTF_SPILLED;
+
+ // Clear anything that was already there by masking out the bits before 'or'ing in what we want there.
+ gtSpillFlags = (unsigned char)((gtSpillFlags & ~(packedFlags << (idx * 2))) | (bits << (idx * 2)));
+ }
+
+ //--------------------------------------------------------------------------
+ // GetRegType: Get var_type of the register specified by index.
+ //
+ // Arguments:
+ // index - Index of the register.
+ // First register will have an index 0 and so on.
+ //
+ // Return Value:
+ // var_type of the register specified by its index.
+
+ var_types GetRegType(unsigned index)
+ {
+ assert(index < 2);
+ // The type of register is usually the same as GenTree type
+ // since most of time GenTreeMultiRegOp uses only a single reg (when gtOtherReg is REG_NA).
+ // The special case is when we have TYP_LONG here, which was `TYP_DOUBLE` originally
+ // (copied to int regs for argument push on armel). Then we need to separate them into int for each index.
+ var_types result = TypeGet();
+ if (result == TYP_LONG)
+ {
+ assert(gtOtherReg != REG_NA);
+ result = TYP_INT;
+ }
+ return result;
+ }
+
+ //-------------------------------------------------------------------
+ // clearOtherRegFlags: clear GTF_* flags associated with gtOtherRegs
+ //
+ // Arguments:
+ // None
+ //
+ // Return Value:
+ // None
+ //
+ void ClearOtherRegFlags()
+ {
+ gtSpillFlags = 0;
}
#if DEBUGGABLE_GENTREE
@@ -4056,6 +4265,68 @@ struct GenTreeIndex : public GenTreeOp
#endif
};
+// gtIndexAddr: given an array object and an index, checks that the index is within the bounds of the array if
+// necessary and produces the address of the value at that index of the array.
+struct GenTreeIndexAddr : public GenTreeOp
+{
+ GenTree*& Arr()
+ {
+ return gtOp1;
+ }
+ GenTree*& Index()
+ {
+ return gtOp2;
+ }
+
+ CORINFO_CLASS_HANDLE gtStructElemClass; // If the element type is a struct, this is the struct type.
+
+ GenTree* gtIndRngFailBB; // Label to jump to for array-index-out-of-range
+ unsigned gtStkDepth; // Stack depth at which the jump occurs (required for fgSetRngChkTarget)
+
+ var_types gtElemType; // The element type of the array.
+ unsigned gtElemSize; // size of elements in the array
+ unsigned gtLenOffset; // The offset from the array's base address to its length.
+ unsigned gtElemOffset; // The offset from the array's base address to its first element.
+
+ GenTreeIndexAddr(GenTree* arr,
+ GenTree* ind,
+ var_types elemType,
+ CORINFO_CLASS_HANDLE structElemClass,
+ unsigned elemSize,
+ unsigned lenOffset,
+ unsigned elemOffset)
+ : GenTreeOp(GT_INDEX_ADDR, TYP_BYREF, arr, ind)
+ , gtStructElemClass(structElemClass)
+ , gtIndRngFailBB(nullptr)
+ , gtStkDepth(0)
+ , gtElemType(elemType)
+ , gtElemSize(elemSize)
+ , gtLenOffset(lenOffset)
+ , gtElemOffset(elemOffset)
+ {
+#ifdef DEBUG
+ if (JitConfig.JitSkipArrayBoundCheck() == 1)
+ {
+ // Skip bounds check
+ }
+ else
+#endif
+ {
+ // Do bounds check
+ gtFlags |= GTF_INX_RNGCHK;
+ }
+
+ // REVERSE_OPS is set because we must evaluate the index before the array address.
+ gtFlags |= GTF_EXCEPT | GTF_GLOB_REF | GTF_REVERSE_OPS;
+ }
+
+#if DEBUGGABLE_GENTREE
+ GenTreeIndexAddr() : GenTreeOp()
+ {
+ }
+#endif
+};
+
/* gtArrLen -- array length (GT_ARR_LENGTH)
GT_ARR_LENGTH is used for "arr.length" */
@@ -4351,9 +4622,24 @@ struct GenTreeAddrMode : public GenTreeOp
return gtOp2;
}
- unsigned gtScale; // The scale factor
+ int Offset()
+ {
+ return static_cast<int>(gtOffset);
+ }
+
+ unsigned gtScale; // The scale factor
+
+#ifndef LEGACY_BACKEND
+private:
+#endif
+ // TODO-Cleanup: gtOffset should be changed to 'int' to match the getter function and avoid accidental
+ // zero extension to 64 bit. However, this is used by legacy code and initialized, via the offset
+ // parameter of the constructor, by Lowering::TryCreateAddrMode & CodeGenInterface::genCreateAddrMode.
+ // The later computes the offset as 'ssize_t' but returns it as 'unsigned'. We should change
+ // genCreateAddrMode to return 'int' or 'ssize_t' and then update this as well.
unsigned gtOffset; // The offset to add
+public:
GenTreeAddrMode(var_types type, GenTreePtr base, GenTreePtr index, unsigned scale, unsigned offset)
: GenTreeOp(GT_LEA, type, base, index)
{
@@ -4388,7 +4674,7 @@ struct GenTreeIndir : public GenTreeOp
GenTree* Base();
GenTree* Index();
unsigned Scale();
- size_t Offset();
+ ssize_t Offset();
GenTreeIndir(genTreeOps oper, var_types type, GenTree* addr, GenTree* data) : GenTreeOp(oper, type, addr, data)
{
@@ -5151,8 +5437,10 @@ struct GenTreePutArgSplit : public GenTreePutArgStk
bits |= PACKED_GTF_SPILLED;
}
+ const unsigned char packedFlags = PACKED_GTF_SPILL | PACKED_GTF_SPILLED;
+
// Clear anything that was already there by masking out the bits before 'or'ing in what we want there.
- gtSpillFlags = (unsigned char)((gtSpillFlags & ~(0xffU << (idx * 2))) | (bits << (idx * 2)));
+ gtSpillFlags = (unsigned char)((gtSpillFlags & ~(packedFlags << (idx * 2))) | (bits << (idx * 2)));
}
//--------------------------------------------------------------------------
@@ -5798,27 +6086,6 @@ inline bool GenTree::IsCopyOrReloadOfMultiRegCall() const
return false;
}
-//-----------------------------------------------------------------------------------
-// IsMultiReg: whether this is a MultiReg node (i.e. GT_MUL_LONG or GT_PUTARG_REG)
-//
-// Arguments:
-// None
-//
-// Return Value:
-// Returns true if this GenTree is a MultiReg node
-inline bool GenTree::IsMultiReg() const
-{
-#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
-#ifdef ARM_SOFTFP
- return (gtOper == GT_MUL_LONG) || (gtOper == GT_PUTARG_REG);
-#else // !ARM_SOFTFP
- return gtOper == GT_MUL_LONG;
-#endif // !ARM_SOFTFP
-#else // ! _TARGET_ARM_
- return false;
-#endif // ! _TARGET_ARM_
-}
-
inline bool GenTree::IsCnsIntOrI() const
{
return (gtOper == GT_CNS_INT);
diff --git a/src/jit/gtlist.h b/src/jit/gtlist.h
index 7e78b27dd2..544023391f 100644
--- a/src/jit/gtlist.h
+++ b/src/jit/gtlist.h
@@ -163,6 +163,8 @@ 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
+ // aiming to minimize compile times.
GTNODE(MKREFANY , GenTreeOp ,0,GTK_BINOP)
diff --git a/src/jit/gtstructs.h b/src/jit/gtstructs.h
index bb3f82b5b5..1661809f6c 100644
--- a/src/jit/gtstructs.h
+++ b/src/jit/gtstructs.h
@@ -71,6 +71,7 @@ GTSTRUCT_1(Colon , GT_COLON)
GTSTRUCT_1(FptrVal , GT_FTN_ADDR)
GTSTRUCT_1(Intrinsic , GT_INTRINSIC)
GTSTRUCT_1(Index , GT_INDEX)
+GTSTRUCT_1(IndexAddr , GT_INDEX_ADDR)
#ifdef FEATURE_SIMD
GTSTRUCT_2(BoundsChk , GT_ARR_BOUNDS_CHECK, GT_SIMD_CHK)
#else // !FEATURE_SIMD
@@ -106,7 +107,11 @@ GTSTRUCT_1(SIMD , GT_SIMD)
GTSTRUCT_1(AllocObj , GT_ALLOCOBJ)
GTSTRUCT_2(CC , GT_JCC, GT_SETCC)
#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
+#ifdef ARM_SOFTFP
GTSTRUCT_2(MultiRegOp , GT_MUL_LONG, GT_PUTARG_REG)
+#else
+GTSTRUCT_1(MultiRegOp , GT_MUL_LONG)
+#endif
#endif
/*****************************************************************************/
#undef GTSTRUCT_0
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index 47f5106355..08ac0d57a3 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -825,14 +825,10 @@ void Compiler::impAssignTempGen(unsigned tmpNum,
* prefixTree at the head of the list.
*/
-GenTreeArgList* Compiler::impPopList(unsigned count,
- unsigned* flagsPtr,
- CORINFO_SIG_INFO* sig,
- GenTreeArgList* prefixTree)
+GenTreeArgList* Compiler::impPopList(unsigned count, CORINFO_SIG_INFO* sig, GenTreeArgList* prefixTree)
{
assert(sig == nullptr || count == sig->numArgs);
- unsigned flags = 0;
CORINFO_CLASS_HANDLE structType;
GenTreeArgList* treeList;
@@ -860,12 +856,9 @@ GenTreeArgList* Compiler::impPopList(unsigned count,
}
/* NOTE: we defer bashing the type for I_IMPL to fgMorphArgs */
- flags |= temp->gtFlags;
treeList = gtNewListNode(temp, treeList);
}
- *flagsPtr = flags;
-
if (sig != nullptr)
{
if (sig->retTypeSigClass != nullptr && sig->retType != CORINFO_TYPE_CLASS &&
@@ -954,15 +947,12 @@ GenTreeArgList* Compiler::impPopList(unsigned count,
* The first "skipReverseCount" items are not reversed.
*/
-GenTreeArgList* Compiler::impPopRevList(unsigned count,
- unsigned* flagsPtr,
- CORINFO_SIG_INFO* sig,
- unsigned skipReverseCount)
+GenTreeArgList* Compiler::impPopRevList(unsigned count, CORINFO_SIG_INFO* sig, unsigned skipReverseCount)
{
assert(skipReverseCount <= count);
- GenTreeArgList* list = impPopList(count, flagsPtr, sig);
+ GenTreeArgList* list = impPopList(count, sig);
// reverse the list
if (list == nullptr || skipReverseCount == count)
@@ -1829,7 +1819,7 @@ GenTreeCall* Compiler::impReadyToRunHelperToTree(
return nullptr;
}
- GenTreeCall* op1 = gtNewHelperCallNode(helper, type, GTF_EXCEPT, args);
+ GenTreeCall* op1 = gtNewHelperCallNode(helper, type, args);
op1->setEntryPoint(lookup);
@@ -1963,7 +1953,7 @@ GenTreePtr Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedTok
gtNewArgList(ctxTree, gtNewIconEmbHndNode(pRuntimeLookup->signature, nullptr, GTF_ICON_TOKEN_HDL, 0,
nullptr, compileTimeHandle));
- return gtNewHelperCallNode(pRuntimeLookup->helper, TYP_I_IMPL, GTF_EXCEPT, helperArgs);
+ return gtNewHelperCallNode(pRuntimeLookup->helper, TYP_I_IMPL, helperArgs);
}
// Slot pointer
@@ -1980,10 +1970,10 @@ GenTreePtr Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedTok
// Applied repeated indirections
for (WORD i = 0; i < pRuntimeLookup->indirections; i++)
{
- if (i == 1 && pRuntimeLookup->indirectFirstOffset)
+ if ((i == 1 && pRuntimeLookup->indirectFirstOffset) || (i == 2 && pRuntimeLookup->indirectSecondOffset))
{
indOffTree = impCloneExpr(slotPtrTree, &slotPtrTree, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL,
- nullptr DEBUGARG("impRuntimeLookup indirectFirstOffset"));
+ nullptr DEBUGARG("impRuntimeLookup indirectOffset"));
}
if (i != 0)
@@ -1993,7 +1983,7 @@ GenTreePtr Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedTok
slotPtrTree->gtFlags |= GTF_IND_INVARIANT;
}
- if (i == 1 && pRuntimeLookup->indirectFirstOffset)
+ if ((i == 1 && pRuntimeLookup->indirectFirstOffset) || (i == 2 && pRuntimeLookup->indirectSecondOffset))
{
slotPtrTree = gtNewOperNode(GT_ADD, TYP_I_IMPL, indOffTree, slotPtrTree);
}
@@ -2063,7 +2053,7 @@ GenTreePtr Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedTok
GenTreeArgList* helperArgs =
gtNewArgList(ctxTree, gtNewIconEmbHndNode(pRuntimeLookup->signature, nullptr, GTF_ICON_TOKEN_HDL, 0, nullptr,
compileTimeHandle));
- GenTreePtr helperCall = gtNewHelperCallNode(pRuntimeLookup->helper, TYP_I_IMPL, GTF_EXCEPT, helperArgs);
+ GenTreePtr helperCall = gtNewHelperCallNode(pRuntimeLookup->helper, TYP_I_IMPL, helperArgs);
// Check for null and possibly call helper
GenTreePtr relop = gtNewOperNode(GT_NE, TYP_INT, handle, gtNewIconNode(0, TYP_I_IMPL));
@@ -3257,18 +3247,18 @@ GenTreePtr Compiler::impInitializeArrayIntrinsic(CORINFO_SIG_INFO* sig)
impPopStack();
const unsigned blkSize = size.Value();
- GenTreePtr dst;
+ unsigned dataOffset;
if (isMDArray)
{
- unsigned dataOffset = eeGetMDArrayDataOffset(elementType, rank);
-
- dst = gtNewOperNode(GT_ADD, TYP_BYREF, arrayLocalNode, gtNewIconNode(dataOffset, TYP_I_IMPL));
+ dataOffset = eeGetMDArrayDataOffset(elementType, rank);
}
else
{
- dst = gtNewOperNode(GT_ADDR, TYP_BYREF, gtNewIndexRef(elementType, arrayLocalNode, gtNewIconNode(0)));
+ dataOffset = eeGetArrayDataOffset(elementType);
}
+
+ GenTreePtr dst = gtNewOperNode(GT_ADD, TYP_BYREF, arrayLocalNode, gtNewIconNode(dataOffset, TYP_I_IMPL));
GenTreePtr blk = gtNewBlockVal(dst, blkSize);
GenTreePtr srcAddr = gtNewIconHandleNode((size_t)initData, GTF_ICON_STATIC_HDL);
GenTreePtr src = gtNewOperNode(GT_IND, TYP_STRUCT, srcAddr);
@@ -3291,12 +3281,23 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis,
int memberRef,
bool readonlyCall,
bool tailCall,
- CorInfoIntrinsics* pIntrinsicID)
+ bool isJitIntrinsic,
+ CorInfoIntrinsics* pIntrinsicID,
+ bool* isSpecialIntrinsic)
{
bool mustExpand = false;
+ bool isSpecial = false;
CorInfoIntrinsics intrinsicID = info.compCompHnd->getIntrinsicID(method, &mustExpand);
*pIntrinsicID = intrinsicID;
+ // Jit intrinsics are always optional to expand, and won't have an
+ // Intrinsic ID.
+ if (isJitIntrinsic)
+ {
+ assert(!mustExpand);
+ assert(intrinsicID == CORINFO_INTRINSIC_Illegal);
+ }
+
#ifndef _TARGET_ARM_
genTreeOps interlockedOperator;
#endif
@@ -3492,7 +3493,7 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis,
// on a local are probably pretty useless anyway, so we probably don't care.
op1 = gtNewOperNode(interlockedOperator, genActualType(callType), op1, op2);
- op1->gtFlags |= GTF_GLOB_EFFECT;
+ op1->gtFlags |= GTF_GLOB_REF | GTF_ASG;
retNode = op1;
break;
#endif // _TARGET_XARCH_
@@ -3502,7 +3503,7 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis,
assert(sig->numArgs == 0);
op1 = new (this, GT_MEMORYBARRIER) GenTree(GT_MEMORYBARRIER, TYP_VOID);
- op1->gtFlags |= GTF_GLOB_EFFECT;
+ op1->gtFlags |= GTF_GLOB_REF | GTF_ASG;
retNode = op1;
break;
@@ -3533,9 +3534,8 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis,
op1 = impPopStack().val;
if (!opts.MinOpts() && !opts.compDbgCode)
{
- GenTreeArrLen* arrLen =
- new (this, GT_ARR_LENGTH) GenTreeArrLen(TYP_INT, op1, offsetof(CORINFO_String, stringLen));
- op1 = arrLen;
+ GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, op1, offsetof(CORINFO_String, stringLen));
+ op1 = arrLen;
}
else
{
@@ -3610,16 +3610,58 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis,
#ifndef LEGACY_BACKEND
case CORINFO_INTRINSIC_Object_GetType:
-
+ {
op1 = impPopStack().val;
- op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, intrinsicID, method);
- // Set the CALL flag to indicate that the operator is implemented by a call.
- // Set also the EXCEPTION flag because the native implementation of
- // CORINFO_INTRINSIC_Object_GetType intrinsic can throw NullReferenceException.
- op1->gtFlags |= (GTF_CALL | GTF_EXCEPT);
- retNode = op1;
+ // If we're calling GetType on a boxed value, just get the type directly.
+ if (!opts.MinOpts() && !opts.compDbgCode)
+ {
+ if (op1->IsBoxedValue())
+ {
+#ifdef DEBUG
+ JITDUMP("Attempting to optimize box(...).getType() to direct type construction\n");
+#endif
+
+ // Try and clean up the box. Obtain the handle we
+ // were going to pass to the newobj.
+ GenTree* boxTypeHandle = gtTryRemoveBoxUpstreamEffects(op1, BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE);
+
+ if (boxTypeHandle != nullptr)
+ {
+ // Note we don't need to play the TYP_STRUCT games here like
+ // do for LDTOKEN since the return value of this operator is Type,
+ // not RuntimeTypeHandle.
+ GenTreeArgList* helperArgs = gtNewArgList(boxTypeHandle);
+ GenTree* runtimeType =
+ gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE, TYP_REF, helperArgs);
+ retNode = runtimeType;
+
+#ifdef DEBUG
+ JITDUMP("Optimized; result is\n");
+ if (verbose)
+ {
+ gtDispTree(retNode);
+ }
+#endif
+ }
+ }
+ }
+
+ // Else expand as an intrinsic
+ if (retNode == nullptr)
+ {
+ op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, intrinsicID, method);
+
+ // Set the CALL flag to indicate that the operator is implemented by a call.
+ // Set also the EXCEPTION flag because the native implementation of
+ // CORINFO_INTRINSIC_Object_GetType intrinsic can throw NullReferenceException.
+ op1->gtFlags |= (GTF_CALL | GTF_EXCEPT);
+ retNode = op1;
+ // Might be further optimizable during morph
+ isSpecial = true;
+ }
break;
+ }
#endif
// Implement ByReference Ctor. This wraps the assignment of the ref into a byref-like field
// in a value type. The canonical example of this is Span<T>. In effect this is just a
@@ -3761,11 +3803,56 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis,
break;
}
+ case CORINFO_INTRINSIC_TypeEQ:
+ case CORINFO_INTRINSIC_TypeNEQ:
+ case CORINFO_INTRINSIC_GetCurrentManagedThread:
+ case CORINFO_INTRINSIC_GetManagedThreadId:
+ {
+ // Retry optimizing these during morph
+ isSpecial = true;
+ break;
+ }
+
default:
/* Unknown intrinsic */
break;
}
+ // Look for new-style jit intrinsics by name
+ if (isJitIntrinsic)
+ {
+ assert(retNode == nullptr);
+ const NamedIntrinsic ni = lookupNamedIntrinsic(method);
+
+ switch (ni)
+ {
+ case NI_Enum_HasFlag:
+ {
+ GenTree* thisOp = impStackTop(1).val;
+ GenTree* flagOp = impStackTop(0).val;
+ GenTree* optTree = gtOptimizeEnumHasFlag(thisOp, flagOp);
+
+ if (optTree != nullptr)
+ {
+ // Optimization successful. Pop the stack for real.
+ impPopStack();
+ impPopStack();
+ retNode = optTree;
+ }
+ else
+ {
+ // Retry optimizing this during morph.
+ isSpecial = true;
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
if (mustExpand)
{
if (retNode == nullptr)
@@ -3774,9 +3861,52 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis,
}
}
+ // Optionally report if this intrinsic is special
+ // (that is, potentially re-optimizable during morph).
+ if (isSpecialIntrinsic != nullptr)
+ {
+ *isSpecialIntrinsic = isSpecial;
+ }
+
return retNode;
}
+//------------------------------------------------------------------------
+// lookupNamedIntrinsic: map method to jit named intrinsic value
+//
+// Arguments:
+// method -- method handle for method
+//
+// Return Value:
+// Id for the named intrinsic, or Illegal if none.
+//
+// Notes:
+// method should have CORINFO_FLG_JIT_INTRINSIC set in its attributes,
+// otherwise it is not a named jit intrinsic.
+//
+
+NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
+{
+ NamedIntrinsic result = NI_Illegal;
+
+ const char* className = nullptr;
+ const char* namespaceName = nullptr;
+ const char* methodName = info.compCompHnd->getMethodNameFromMetadata(method, &className, &namespaceName);
+
+ if ((namespaceName != nullptr) && strcmp(namespaceName, "System") == 0)
+ {
+ if ((className != nullptr) && strcmp(className, "Enum") == 0)
+ {
+ if ((methodName != nullptr) && strcmp(methodName, "HasFlag") == 0)
+ {
+ result = NI_Enum_HasFlag;
+ }
+ }
+ }
+
+ return result;
+}
+
/*****************************************************************************/
GenTreePtr Compiler::impArrayAccessIntrinsic(
@@ -4039,8 +4169,8 @@ void Compiler::verConvertBBToThrowVerificationException(BasicBlock* block DEBUGA
}
assert(verCurrentState.esStackDepth == 0);
- GenTreePtr op1 = gtNewHelperCallNode(CORINFO_HELP_VERIFICATION, TYP_VOID, GTF_EXCEPT,
- gtNewArgList(gtNewIconNode(block->bbCodeOffs)));
+ GenTreePtr op1 =
+ gtNewHelperCallNode(CORINFO_HELP_VERIFICATION, TYP_VOID, gtNewArgList(gtNewIconNode(block->bbCodeOffs)));
// verCurrentState.esStackDepth = 0;
impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
@@ -5130,7 +5260,7 @@ GenTreePtr Compiler::impImportLdvirtftn(GenTreePtr thisPtr,
{
runtimeMethodHandle = gtNewIconEmbMethHndNode(pResolvedToken->hMethod);
}
- return gtNewHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL, GTF_EXCEPT,
+ return gtNewHelperCallNode(CORINFO_HELP_GVMLOOKUP_FOR_SLOT, TYP_I_IMPL,
gtNewArgList(thisPtr, runtimeMethodHandle));
}
@@ -5139,8 +5269,8 @@ GenTreePtr Compiler::impImportLdvirtftn(GenTreePtr thisPtr,
{
if (!pCallInfo->exactContextNeedsRuntimeLookup)
{
- GenTreeCall* call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR, TYP_I_IMPL, GTF_EXCEPT,
- gtNewArgList(thisPtr));
+ GenTreeCall* call =
+ gtNewHelperCallNode(CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR, TYP_I_IMPL, gtNewArgList(thisPtr));
call->setEntryPoint(pCallInfo->codePointerLookup.constLookup);
@@ -5179,36 +5309,81 @@ GenTreePtr Compiler::impImportLdvirtftn(GenTreePtr thisPtr,
// Call helper function. This gets the target address of the final destination callsite.
- return gtNewHelperCallNode(CORINFO_HELP_VIRTUAL_FUNC_PTR, TYP_I_IMPL, GTF_EXCEPT, helpArgs);
+ return gtNewHelperCallNode(CORINFO_HELP_VIRTUAL_FUNC_PTR, TYP_I_IMPL, helpArgs);
}
-/*****************************************************************************
- *
- * Build and import a box node
- */
+//------------------------------------------------------------------------
+// impImportAndPushBox: build and import a value-type box
+//
+// Arguments:
+// pResolvedToken - resolved token from the box operation
+//
+// Return Value:
+// None.
+//
+// Side Effects:
+// The value to be boxed is popped from the stack, and a tree for
+// the boxed value is pushed. This method may create upstream
+// statements, spill side effecting trees, and create new temps.
+//
+// If importing an inlinee, we may also discover the inline must
+// fail. If so there is no new value pushed on the stack. Callers
+// should use CompDoNotInline after calling this method to see if
+// ongoing importation should be aborted.
+//
+// Notes:
+// Boxing of ref classes results in the same value as the value on
+// the top of the stack, so is handled inline in impImportBlockCode
+// for the CEE_BOX case. Only value or primitive type boxes make it
+// here.
+//
+// Boxing for nullable types is done via a helper call; boxing
+// of other value types is expanded inline or handled via helper
+// call, depending on the jit's codegen mode.
+//
+// When the jit is operating in size and time constrained modes,
+// using a helper call here can save jit time and code size. But it
+// also may inhibit cleanup optimizations that could have also had a
+// even greater benefit effect on code size and jit time. An optimal
+// strategy may need to peek ahead and see if it is easy to tell how
+// the box is being used. For now, we defer.
void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
{
- // Get the tree for the type handle for the boxed object. In the case
- // of shared generic code or ngen'd code this might be an embedded
- // computation.
- // Note we can only box do it if the class construtor has been called
- // We can always do it on primitive types
-
- GenTreePtr op1 = nullptr;
- GenTreePtr op2 = nullptr;
- var_types lclTyp;
-
+ // Spill any special side effects
impSpillSpecialSideEff();
- // Now get the expression to box from the stack.
+ // Get get the expression to box from the stack.
+ GenTreePtr op1 = nullptr;
+ GenTreePtr op2 = nullptr;
StackEntry se = impPopStack();
CORINFO_CLASS_HANDLE operCls = se.seTypeInfo.GetClassHandle();
GenTreePtr exprToBox = se.val;
+ // Look at what helper we should use.
CorInfoHelpFunc boxHelper = info.compCompHnd->getBoxHelper(pResolvedToken->hClass);
- if (boxHelper == CORINFO_HELP_BOX)
+
+ // Determine what expansion to prefer.
+ //
+ // In size/time/debuggable constrained modes, the helper call
+ // expansion for box is generally smaller and is preferred, unless
+ // the value to box is a struct that comes from a call. In that
+ // case the call can construct its return value directly into the
+ // box payload, saving possibly some up-front zeroing.
+ //
+ // Currently primitive type boxes always get inline expanded. We may
+ // want to do the same for small structs if they don't come from
+ // calls and don't have GC pointers, since explicitly copying such
+ // structs is cheap.
+ JITDUMP("\nCompiler::impImportAndPushBox -- handling BOX(value class) via");
+ bool canExpandInline = (boxHelper == CORINFO_HELP_BOX);
+ bool optForSize = !exprToBox->IsCall() && (operCls != nullptr) && (opts.compDbgCode || opts.MinOpts());
+ bool expandInline = canExpandInline && !optForSize;
+
+ if (expandInline)
{
+ JITDUMP(" inline allocate/copy sequence\n");
+
// we are doing 'normal' boxing. This means that we can inline the box operation
// Box(expr) gets morphed into
// temp = new(clsHnd)
@@ -5220,9 +5395,23 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
// and the other you get
// *(temp+4) = expr
- if (impBoxTempInUse || impBoxTemp == BAD_VAR_NUM)
+ if (opts.MinOpts() || opts.compDbgCode)
+ {
+ // For minopts/debug code, try and minimize the total number
+ // of box temps by reusing an existing temp when possible.
+ if (impBoxTempInUse || impBoxTemp == BAD_VAR_NUM)
+ {
+ impBoxTemp = lvaGrabTemp(true DEBUGARG("Reusable Box Helper"));
+ }
+ }
+ else
{
- impBoxTemp = lvaGrabTemp(true DEBUGARG("Box Helper"));
+ // When optimizing, use a new temp for each box operation
+ // since we then know the exact class of the box temp.
+ impBoxTemp = lvaGrabTemp(true DEBUGARG("Single-def Box Helper"));
+ lvaTable[impBoxTemp].lvType = TYP_REF;
+ const bool isExact = true;
+ lvaSetClass(impBoxTemp, pResolvedToken->hClass, isExact);
}
// needs to stay in use until this box expression is appended
@@ -5252,15 +5441,17 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
// Ensure that the value class is restored
op2 = impTokenToHandle(pResolvedToken, nullptr, TRUE /* mustRestoreHandle */);
if (op2 == nullptr)
- { // compDonotInline()
+ {
+ // We must be backing out of an inline.
+ assert(compDonotInline());
return;
}
- op1 = gtNewHelperCallNode(info.compCompHnd->getNewHelper(pResolvedToken, info.compMethodHnd), TYP_REF, 0,
+ op1 = gtNewHelperCallNode(info.compCompHnd->getNewHelper(pResolvedToken, info.compMethodHnd), TYP_REF,
gtNewArgList(op2));
}
- /* Remember that this basic block contains 'new' of an array */
+ /* Remember that this basic block contains 'new' of an object */
compCurBB->bbFlags |= BBF_HAS_NEWOBJ;
GenTreePtr asg = gtNewTempAssign(impBoxTemp, op1);
@@ -5278,7 +5469,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
}
else
{
- lclTyp = exprToBox->TypeGet();
+ var_types lclTyp = exprToBox->TypeGet();
if (lclTyp == TYP_BYREF)
{
lclTyp = TYP_I_IMPL;
@@ -5302,11 +5493,16 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
op1 = gtNewAssignNode(gtNewOperNode(GT_IND, lclTyp, op1), exprToBox);
}
- op2 = gtNewLclvNode(impBoxTemp, TYP_REF);
- op1 = gtNewOperNode(GT_COMMA, TYP_REF, op1, op2);
+ // Spill eval stack to flush out any pending side effects.
+ impSpillSideEffects(true, (unsigned)CHECK_SPILL_ALL DEBUGARG("impImportAndPushBox"));
+
+ // Set up this copy as a second assignment.
+ GenTreePtr copyStmt = impAppendTree(op1, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+
+ op1 = gtNewLclvNode(impBoxTemp, TYP_REF);
- // Record that this is a "box" node.
- op1 = new (this, GT_BOX) GenTreeBox(TYP_REF, op1, asgStmt);
+ // Record that this is a "box" node and keep track of the matching parts.
+ op1 = new (this, GT_BOX) GenTreeBox(TYP_REF, op1, asgStmt, copyStmt);
// If it is a value class, mark the "box" node. We can use this information
// to optimise several cases:
@@ -5320,17 +5516,21 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
}
else
{
- // Don't optimize, just call the helper and be done with it
+ // Don't optimize, just call the helper and be done with it.
+ JITDUMP(" helper call because: %s\n", canExpandInline ? "optimizing for size" : "nullable");
+ assert(operCls != nullptr);
// Ensure that the value class is restored
op2 = impTokenToHandle(pResolvedToken, nullptr, TRUE /* mustRestoreHandle */);
if (op2 == nullptr)
- { // compDonotInline()
+ {
+ // We must be backing out of an inline.
+ assert(compDonotInline());
return;
}
GenTreeArgList* args = gtNewArgList(op2, impGetStructAddr(exprToBox, operCls, (unsigned)CHECK_SPILL_ALL, true));
- op1 = gtNewHelperCallNode(boxHelper, TYP_REF, GTF_EXCEPT, args);
+ op1 = gtNewHelperCallNode(boxHelper, TYP_REF, args);
}
/* Push the result back on the stack, */
@@ -5440,7 +5640,7 @@ void Compiler::impImportNewObjArray(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORI
args = gtNewListNode(classHandle, args);
- node = gtNewHelperCallNode(CORINFO_HELP_NEW_MDARR_NONVARARG, TYP_REF, 0, args);
+ node = gtNewHelperCallNode(CORINFO_HELP_NEW_MDARR_NONVARARG, TYP_REF, args);
}
else
{
@@ -5456,9 +5656,9 @@ void Compiler::impImportNewObjArray(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORI
args = gtNewListNode(gtNewIconNode(pCallInfo->sig.numArgs), args);
unsigned argFlags = 0;
- args = impPopList(pCallInfo->sig.numArgs, &argFlags, &pCallInfo->sig, args);
+ args = impPopList(pCallInfo->sig.numArgs, &pCallInfo->sig, args);
- node = gtNewHelperCallNode(CORINFO_HELP_NEW_MDARR, TYP_REF, 0, args);
+ node = gtNewHelperCallNode(CORINFO_HELP_NEW_MDARR, TYP_REF, args);
// varargs, so we pop the arguments
node->gtFlags |= GTF_CALL_POP_ARGS;
@@ -5870,9 +6070,7 @@ void Compiler::impPopArgsForUnmanagedCall(GenTreePtr call, CORINFO_SIG_INFO* sig
/* The argument list is now "clean" - no out-of-order side effects
* Pop the argument list in reverse order */
- unsigned argFlags = 0;
- GenTreePtr args = call->gtCall.gtCallArgs =
- impPopRevList(sig->numArgs, &argFlags, sig, sig->numArgs - argsToReverse);
+ GenTreePtr args = call->gtCall.gtCallArgs = impPopRevList(sig->numArgs, sig, sig->numArgs - argsToReverse);
if (call->gtCall.gtCallMoreFlags & GTF_CALL_M_UNMGD_THISCALL)
{
@@ -5920,7 +6118,7 @@ GenTreePtr Compiler::impInitClass(CORINFO_RESOLVED_TOKEN* pResolvedToken)
if (runtimeLookup)
{
- node = gtNewHelperCallNode(CORINFO_HELP_INITCLASS, TYP_VOID, 0, gtNewArgList(node));
+ node = gtNewHelperCallNode(CORINFO_HELP_INITCLASS, TYP_VOID, gtNewArgList(node));
}
else
{
@@ -6033,7 +6231,7 @@ GenTreePtr Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolve
break;
}
- op1 = gtNewHelperCallNode(pFieldInfo->helper, type, 0, gtNewArgList(op1));
+ op1 = gtNewHelperCallNode(pFieldInfo->helper, type, gtNewArgList(op1));
FieldSeqNode* fs = GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField);
op1 = gtNewOperNode(GT_ADD, type, op1,
@@ -6053,7 +6251,8 @@ GenTreePtr Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolve
callFlags |= GTF_CALL_HOISTABLE;
}
- op1 = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_STATIC_BASE, TYP_BYREF, callFlags);
+ op1 = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_STATIC_BASE, TYP_BYREF);
+ op1->gtFlags |= callFlags;
op1->gtCall.setEntryPoint(pFieldInfo->fieldLookup);
}
@@ -6088,7 +6287,8 @@ GenTreePtr Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolve
callFlags |= GTF_CALL_HOISTABLE;
}
var_types type = TYP_BYREF;
- op1 = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE, type, callFlags, args);
+ op1 = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE, type, args);
+ op1->gtFlags |= callFlags;
op1->gtCall.setEntryPoint(pFieldInfo->fieldLookup);
FieldSeqNode* fs = GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField);
@@ -6262,7 +6462,7 @@ void Compiler::impInsertHelperCall(CORINFO_HELPER_DESC* helperInfo)
* Mark as CSE'able, and hoistable. Consider marking hoistable unless you're in the inlinee.
* Also, consider sticking this in the first basic block.
*/
- GenTreePtr callout = gtNewHelperCallNode(helperInfo->helperNum, TYP_VOID, GTF_EXCEPT, args);
+ GenTreePtr callout = gtNewHelperCallNode(helperInfo->helperNum, TYP_VOID, args);
impAppendTree(callout, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
}
@@ -6717,10 +6917,13 @@ var_types Compiler::impImportCall(OPCODE opcode,
#endif // DEBUG
// <NICE> Factor this into getCallInfo </NICE>
- if ((mflags & CORINFO_FLG_INTRINSIC) && !pConstrainedResolvedToken)
+ const bool isIntrinsic = (mflags & CORINFO_FLG_INTRINSIC) != 0;
+ const bool isJitIntrinsic = (mflags & CORINFO_FLG_JIT_INTRINSIC) != 0;
+ bool isSpecialIntrinsic = false;
+ if ((isIntrinsic || isJitIntrinsic) && !pConstrainedResolvedToken)
{
call = impIntrinsic(newobjThis, clsHnd, methHnd, sig, pResolvedToken->token, readonlyCall,
- (canTailCall && (tailCall != 0)), &intrinsicID);
+ (canTailCall && (tailCall != 0)), isJitIntrinsic, &intrinsicID, &isSpecialIntrinsic);
if (compIsForInlining() && compInlineResult->IsFailure())
{
@@ -6786,7 +6989,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
exactContextHnd = callInfo->contextHandle;
exactContextNeedsRuntimeLookup = callInfo->exactContextNeedsRuntimeLookup == TRUE;
- // Recursive call is treaded as a loop to the begining of the method.
+ // Recursive call is treated as a loop to the begining of the method.
if (methHnd == info.compMethodHnd)
{
#ifdef DEBUG
@@ -6907,7 +7110,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
// OK, We've been told to call via LDVIRTFTN, so just
// take the call now....
- args = impPopList(sig->numArgs, &argFlags, sig);
+ args = impPopList(sig->numArgs, sig);
GenTreePtr thisPtr = impPopStack().val;
thisPtr = impTransformThis(thisPtr, pConstrainedResolvedToken, callInfo->thisTransform);
@@ -7036,9 +7239,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
}
// Mark call if it's one of the ones we will maybe treat as an intrinsic
- if (intrinsicID == CORINFO_INTRINSIC_Object_GetType || intrinsicID == CORINFO_INTRINSIC_TypeEQ ||
- intrinsicID == CORINFO_INTRINSIC_TypeNEQ || intrinsicID == CORINFO_INTRINSIC_GetCurrentManagedThread ||
- intrinsicID == CORINFO_INTRINSIC_GetManagedThreadId)
+ if (isSpecialIntrinsic)
{
call->gtCall.gtCallMoreFlags |= GTF_CALL_M_SPECIAL_INTRINSIC;
}
@@ -7418,7 +7619,19 @@ var_types Compiler::impImportCall(OPCODE opcode,
}
else
{
- instParam = impParentClassTokenToHandle(pResolvedToken, &runtimeLookup, TRUE /*mustRestoreHandle*/);
+ // If the EE was able to resolve a constrained call, the instantiating parameter to use is the type
+ // by which the call was constrained with. We embed pConstrainedResolvedToken as the extra argument
+ // because pResolvedToken is an interface method and interface types make a poor generic context.
+ if (pConstrainedResolvedToken)
+ {
+ instParam = impTokenToHandle(pConstrainedResolvedToken, &runtimeLookup, TRUE /*mustRestoreHandle*/,
+ FALSE /* importParent */);
+ }
+ else
+ {
+ instParam = impParentClassTokenToHandle(pResolvedToken, &runtimeLookup, TRUE /*mustRestoreHandle*/);
+ }
+
if (instParam == nullptr)
{
return callRetTyp;
@@ -7459,7 +7672,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
//-------------------------------------------------------------------------
// The main group of arguments
- args = call->gtCall.gtCallArgs = impPopList(sig->numArgs, &argFlags, sig, extraArg);
+ args = call->gtCall.gtCallArgs = impPopList(sig->numArgs, sig, extraArg);
if (args)
{
@@ -8080,41 +8293,6 @@ GenTreePtr Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_H
#else // not FEATURE_UNIX_AMD64_STRUCT_PASSING
-#if FEATURE_MULTIREG_RET && defined(_TARGET_ARM_)
- // There is no fixup necessary if the return type is a HFA struct.
- // HFA structs are returned in registers for ARM32 and ARM64
- //
- if (!call->IsVarargs() && IsHfa(retClsHnd))
- {
- if (call->CanTailCall())
- {
- if (info.compIsVarArgs)
- {
- // We cannot tail call because control needs to return to fixup the calling
- // convention for result return.
- call->gtCallMoreFlags &= ~GTF_CALL_M_EXPLICIT_TAILCALL;
- }
- else
- {
- // If we can tail call returning HFA, then don't assign it to
- // a variable back and forth.
- return call;
- }
- }
-
- if (call->gtFlags & GTF_CALL_INLINE_CANDIDATE)
- {
- return call;
- }
-
- unsigned retRegCount = retTypeDesc->GetReturnRegCount();
- if (retRegCount >= 2)
- {
- return impAssignMultiRegTypeToVar(call, retClsHnd);
- }
- }
-#endif // _TARGET_ARM_
-
// Check for TYP_STRUCT type that wraps a primitive type
// Such structs are returned using a single register
// and we change the return type on those calls here.
@@ -8471,8 +8649,8 @@ void Compiler::impImportLeave(BasicBlock* block)
if (verbose)
{
printf("impImportLeave - jumping out of a finally-protected try, convert block to BBJ_CALLFINALLY "
- "block BB%02u [%08p]\n",
- callBlock->bbNum, dspPtr(callBlock));
+ "block %s\n",
+ callBlock->dspToString());
}
#endif
}
@@ -8494,9 +8672,8 @@ void Compiler::impImportLeave(BasicBlock* block)
#ifdef DEBUG
if (verbose)
{
- printf("impImportLeave - jumping out of a finally-protected try, new BBJ_CALLFINALLY block BB%02u "
- "[%08p]\n",
- callBlock->bbNum, dspPtr(callBlock));
+ printf("impImportLeave - jumping out of a finally-protected try, new BBJ_CALLFINALLY block %s\n",
+ callBlock->dspToString());
}
#endif
@@ -8525,9 +8702,8 @@ void Compiler::impImportLeave(BasicBlock* block)
#ifdef DEBUG
if (verbose)
{
- printf("impImportLeave - jumping out of a finally-protected try, created step (BBJ_ALWAYS) block "
- "BB%02u [%08p]\n",
- step->bbNum, dspPtr(step));
+ printf("impImportLeave - jumping out of a finally-protected try, created step (BBJ_ALWAYS) block %s\n",
+ step->dspToString());
}
#endif
@@ -8561,8 +8737,8 @@ void Compiler::impImportLeave(BasicBlock* block)
if (verbose)
{
printf("impImportLeave - no enclosing finally-protected try blocks; convert CEE_LEAVE block to BBJ_ALWAYS "
- "block BB%02u [%08p]\n",
- block->bbNum, dspPtr(block));
+ "block %s\n",
+ block->dspToString());
}
#endif
}
@@ -8587,8 +8763,8 @@ void Compiler::impImportLeave(BasicBlock* block)
#ifdef DEBUG
if (verbose)
{
- printf("impImportLeave - finalStep block required (encFinallies(%d) > 0), new block BB%02u [%08p]\n",
- encFinallies, finalStep->bbNum, dspPtr(finalStep));
+ printf("impImportLeave - finalStep block required (encFinallies(%d) > 0), new block %s\n", encFinallies,
+ finalStep->dspToString());
}
#endif
@@ -9401,86 +9577,94 @@ var_types Compiler::impGetByRefResultType(genTreeOps oper, bool fUnsigned, GenTr
return type;
}
-/*****************************************************************************
- * Casting Helper Function to service both CEE_CASTCLASS and CEE_ISINST
- *
- * typeRef contains the token, op1 to contain the value being cast,
- * and op2 to contain code that creates the type handle corresponding to typeRef
- * isCastClass = true means CEE_CASTCLASS, false means CEE_ISINST
- */
+//------------------------------------------------------------------------
+// impCastClassOrIsInstToTree: build and import castclass/isinst
+//
+// Arguments:
+// op1 - value to cast
+// op2 - type handle for type to cast to
+// pResolvedToken - resolved token from the cast operation
+// isCastClass - true if this is castclass, false means isinst
+//
+// Return Value:
+// Tree representing the cast
+//
+// Notes:
+// May expand into a series of runtime checks or a helper call.
+
GenTreePtr Compiler::impCastClassOrIsInstToTree(GenTreePtr op1,
GenTreePtr op2,
CORINFO_RESOLVED_TOKEN* pResolvedToken,
bool isCastClass)
{
- bool expandInline;
-
assert(op1->TypeGet() == TYP_REF);
- CorInfoHelpFunc helper = info.compCompHnd->getCastingHelper(pResolvedToken, isCastClass);
+ // Optimistically assume the jit should expand this as an inline test
+ bool shouldExpandInline = true;
- if (isCastClass)
+ // Profitability check.
+ //
+ // Don't bother with inline expansion when jit is trying to
+ // generate code quickly, or the cast is in code that won't run very
+ // often, or the method already is pretty big.
+ if (compCurBB->isRunRarely() || opts.compDbgCode || opts.MinOpts())
{
- // We only want to expand inline the normal CHKCASTCLASS helper;
- expandInline = (helper == CORINFO_HELP_CHKCASTCLASS);
+ // not worth the code expansion if jitting fast or in a rarely run block
+ shouldExpandInline = false;
}
- else
+ else if ((op1->gtFlags & GTF_GLOB_EFFECT) && lvaHaveManyLocals())
{
- if (helper == CORINFO_HELP_ISINSTANCEOFCLASS)
- {
- // Get the Class Handle abd class attributes for the type we are casting to
- //
- DWORD flags = info.compCompHnd->getClassAttribs(pResolvedToken->hClass);
-
- //
- // If the class handle is marked as final we can also expand the IsInst check inline
- //
- expandInline = ((flags & CORINFO_FLG_FINAL) != 0);
-
- //
- // But don't expand inline these two cases
- //
- if (flags & CORINFO_FLG_MARSHAL_BYREF)
- {
- expandInline = false;
- }
- else if (flags & CORINFO_FLG_CONTEXTFUL)
- {
- expandInline = false;
- }
- }
- else
- {
- //
- // We can't expand inline any other helpers
- //
- expandInline = false;
- }
+ // not worth creating an untracked local variable
+ shouldExpandInline = false;
}
- if (expandInline)
+ // Pessimistically assume the jit cannot expand this as an inline test
+ bool canExpandInline = false;
+ const CorInfoHelpFunc helper = info.compCompHnd->getCastingHelper(pResolvedToken, isCastClass);
+
+ // Legality check.
+ //
+ // Not all classclass/isinst operations can be inline expanded.
+ // Check legality only if an inline expansion is desirable.
+ if (shouldExpandInline)
{
- if (compCurBB->isRunRarely())
+ if (isCastClass)
{
- expandInline = false; // not worth the code expansion in a rarely run block
+ // Jit can only inline expand the normal CHKCASTCLASS helper.
+ canExpandInline = (helper == CORINFO_HELP_CHKCASTCLASS);
}
-
- if ((op1->gtFlags & GTF_GLOB_EFFECT) && lvaHaveManyLocals())
+ else
{
- expandInline = false; // not worth creating an untracked local variable
+ if (helper == CORINFO_HELP_ISINSTANCEOFCLASS)
+ {
+ // Check the class attributes.
+ DWORD flags = info.compCompHnd->getClassAttribs(pResolvedToken->hClass);
+
+ // If the class is final and is not marshal byref or
+ // contextful, the jit can expand the IsInst check inline.
+ DWORD flagsMask = CORINFO_FLG_FINAL | CORINFO_FLG_MARSHAL_BYREF | CORINFO_FLG_CONTEXTFUL;
+ canExpandInline = ((flags & flagsMask) == CORINFO_FLG_FINAL);
+ }
}
}
+ const bool expandInline = canExpandInline && shouldExpandInline;
+
if (!expandInline)
{
+ JITDUMP("\nExpanding %s as call because %s\n", isCastClass ? "castclass" : "isinst",
+ canExpandInline ? "want smaller code or faster jitting" : "inline expansion not legal");
+
// If we CSE this class handle we prevent assertionProp from making SubType assertions
// so instead we force the CSE logic to not consider CSE-ing this class handle.
//
op2->gtFlags |= GTF_DONT_CSE;
- return gtNewHelperCallNode(helper, TYP_REF, 0, gtNewArgList(op2, op1));
+ return gtNewHelperCallNode(helper, TYP_REF, gtNewArgList(op2, op1));
}
+ JITDUMP("\nExpanding %s inline\n", isCastClass ? "castclass" : "isinst");
+
impSpillSideEffects(true, CHECK_SPILL_ALL DEBUGARG("bubbling QMark2"));
GenTreePtr temp;
@@ -9534,9 +9718,9 @@ GenTreePtr Compiler::impCastClassOrIsInstToTree(GenTreePtr op1,
//
// use the special helper that skips the cases checked by our inlined cast
//
- helper = CORINFO_HELP_CHKCASTCLASS_SPECIAL;
+ const CorInfoHelpFunc specialHelper = CORINFO_HELP_CHKCASTCLASS_SPECIAL;
- condTrue = gtNewHelperCallNode(helper, TYP_REF, 0, gtNewArgList(op2Var, gtClone(op1)));
+ condTrue = gtNewHelperCallNode(specialHelper, TYP_REF, gtNewArgList(op2Var, gtClone(op1)));
}
else
{
@@ -9627,7 +9811,6 @@ void Compiler::impImportBlockCode(BasicBlock* block)
IL_OFFSET nxtStmtOffs;
GenTreePtr arrayNodeFrom, arrayNodeTo, arrayNodeToIndex;
- bool expandInline;
CorInfoHelpFunc helper;
CorInfoIsAccessAllowedResult accessAllowedResult;
CORINFO_HELPER_DESC calloutHelper;
@@ -9911,9 +10094,9 @@ void Compiler::impImportBlockCode(BasicBlock* block)
} cval;
case CEE_PREFIX1:
- opcode = (OPCODE)(getU1LittleEndian(codeAddr) + 256);
- codeAddr += sizeof(__int8);
+ opcode = (OPCODE)(getU1LittleEndian(codeAddr) + 256);
opcodeOffs = (IL_OFFSET)(codeAddr - info.compCode);
+ codeAddr += sizeof(__int8);
goto DECODE_OPCODE;
SPILL_APPEND:
@@ -10683,7 +10866,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
args = gtNewArgList(op1); // Type
args = gtNewListNode(impPopStack().val, args); // index
args = gtNewListNode(impPopStack().val, args); // array
- op1 = gtNewHelperCallNode(CORINFO_HELP_LDELEMA_REF, TYP_BYREF, GTF_EXCEPT, args);
+ op1 = gtNewHelperCallNode(CORINFO_HELP_LDELEMA_REF, TYP_BYREF, args);
impPushOnStack(op1, tiRetVal);
break;
@@ -10983,7 +11166,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
STELEM_REF_POST_VERIFY:
/* Call a helper function to do the assignment */
- op1 = gtNewHelperCallNode(CORINFO_HELP_ARRADDR_ST, TYP_VOID, 0, impPopList(3, &flags, nullptr));
+ op1 = gtNewHelperCallNode(CORINFO_HELP_ARRADDR_ST, TYP_VOID, impPopList(3, nullptr));
goto SPILL_APPEND;
@@ -11308,7 +11491,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
/* Special case: integer/long division may throw an exception */
- if (varTypeIsIntegral(op1->TypeGet()) && op1->OperMayThrow())
+ if (varTypeIsIntegral(op1->TypeGet()) && op1->OperMayThrow(this))
{
op1->gtFlags |= GTF_EXCEPT;
}
@@ -12387,9 +12570,9 @@ void Compiler::impImportBlockCode(BasicBlock* block)
impValidateMemoryAccessOpcode(codeAddr, codeEndp, false);
PREFIX:
- opcode = (OPCODE)getU1LittleEndian(codeAddr);
- codeAddr += sizeof(__int8);
+ opcode = (OPCODE)getU1LittleEndian(codeAddr);
opcodeOffs = (IL_OFFSET)(codeAddr - info.compCode);
+ codeAddr += sizeof(__int8);
goto DECODE_OPCODE;
case CEE_VOLATILE:
@@ -12723,6 +12906,12 @@ void Compiler::impImportBlockCode(BasicBlock* block)
/* get a temporary for the new object */
lclNum = lvaGrabTemp(true DEBUGARG("NewObj constructor temp"));
+ if (compDonotInline())
+ {
+ // Fail fast if lvaGrabTemp fails with CALLSITE_TOO_MANY_LOCALS.
+ assert(compInlineResult->GetObservation() == InlineObservation::CALLSITE_TOO_MANY_LOCALS);
+ return;
+ }
// In the value class case we only need clsHnd for size calcs.
//
@@ -12770,16 +12959,18 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// and potentially exploitable.
lvaSetStruct(lclNum, resolvedToken.hClass, true /* unsafe value cls check */);
}
-
- // Append a tree to zero-out the temp
- newObjThisPtr = gtNewLclvNode(lclNum, lvaTable[lclNum].TypeGet());
-
- newObjThisPtr = gtNewBlkOpNode(newObjThisPtr, // Dest
- gtNewIconNode(0), // Value
- size, // Size
- false, // isVolatile
- false); // not copyBlock
- impAppendTree(newObjThisPtr, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ if (compIsForInlining() || fgStructTempNeedsExplicitZeroInit(lvaTable + lclNum, block))
+ {
+ // Append a tree to zero-out the temp
+ newObjThisPtr = gtNewLclvNode(lclNum, lvaTable[lclNum].TypeGet());
+
+ newObjThisPtr = gtNewBlkOpNode(newObjThisPtr, // Dest
+ gtNewIconNode(0), // Value
+ size, // Size
+ false, // isVolatile
+ false); // not copyBlock
+ impAppendTree(newObjThisPtr, (unsigned)CHECK_SPILL_NONE, impCurStmtOffs);
+ }
// Obtain the address of the temp
newObjThisPtr =
@@ -13835,8 +14026,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// Note that this only works for shared generic code because the same helper is used for all
// reference array types
- op1 =
- gtNewHelperCallNode(info.compCompHnd->getNewArrHelper(resolvedToken.hClass), TYP_REF, 0, args);
+ op1 = gtNewHelperCallNode(info.compCompHnd->getNewArrHelper(resolvedToken.hClass), TYP_REF, args);
}
op1->gtCall.compileTimeHelperArgumentHandle = (CORINFO_GENERIC_HANDLE)resolvedToken.hClass;
@@ -13991,7 +14181,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// Call helper GETREFANY(classHandle, op1);
args = gtNewArgList(op2, op1);
- op1 = gtNewHelperCallNode(CORINFO_HELP_GETREFANY, TYP_BYREF, 0, args);
+ op1 = gtNewHelperCallNode(CORINFO_HELP_GETREFANY, TYP_BYREF, args);
impPushOnStack(op1, tiRetVal);
break;
@@ -14040,8 +14230,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
{
GenTreeArgList* helperArgs = gtNewArgList(op1);
- op1 = gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL, TYP_STRUCT, GTF_EXCEPT,
- helperArgs);
+ op1 = gtNewHelperCallNode(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL, TYP_STRUCT, helperArgs);
// The handle struct is returned in register
op1->gtCall.gtReturnType = TYP_REF;
@@ -14081,7 +14270,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
GenTreeArgList* helperArgs = gtNewArgList(op1);
- op1 = gtNewHelperCallNode(helper, TYP_STRUCT, GTF_EXCEPT, helperArgs);
+ op1 = gtNewHelperCallNode(helper, TYP_STRUCT, helperArgs);
// The handle struct is returned in register
op1->gtCall.gtReturnType = TYP_REF;
@@ -14104,7 +14293,8 @@ void Compiler::impImportBlockCode(BasicBlock* block)
BOOL runtimeLookup;
op2 = impTokenToHandle(&resolvedToken, &runtimeLookup);
if (op2 == nullptr)
- { // compDonotInline()
+ {
+ assert(compDonotInline());
return;
}
@@ -14122,6 +14312,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
tiRetVal = verMakeTypeInfo(resolvedToken.hClass);
tiRetVal.NormaliseForStack();
}
+ JITDUMP("\n Importing UNBOX.ANY(refClass) as CASTCLASS\n");
op1 = impPopStack().val;
goto CASTCLASS;
}
@@ -14151,19 +14342,13 @@ void Compiler::impImportBlockCode(BasicBlock* block)
helper = info.compCompHnd->getUnBoxHelper(resolvedToken.hClass);
assert(helper == CORINFO_HELP_UNBOX || helper == CORINFO_HELP_UNBOX_NULLABLE);
- // We only want to expand inline the normal UNBOX helper;
- expandInline = (helper == CORINFO_HELP_UNBOX);
+ // Check legality and profitability of inline expansion for unboxing.
+ const bool canExpandInline = (helper == CORINFO_HELP_UNBOX);
+ const bool shouldExpandInline = !(compCurBB->isRunRarely() || opts.compDbgCode || opts.MinOpts());
- if (expandInline)
- {
- if (compCurBB->isRunRarely())
- {
- expandInline = false; // not worth the code expansion
- }
- }
-
- if (expandInline)
+ if (canExpandInline && shouldExpandInline)
{
+ JITDUMP("\n Importing %s as inline sequence\n", opcode == CEE_UNBOX ? "UNBOX" : "UNBOX.ANY");
// we are doing normal unboxing
// inline the common case of the unbox helper
// UNBOX(exp) morphs into
@@ -14186,7 +14371,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
return;
}
args = gtNewArgList(op2, op1);
- op1 = gtNewHelperCallNode(helper, TYP_VOID, 0, args);
+ op1 = gtNewHelperCallNode(helper, TYP_VOID, args);
op1 = new (this, GT_COLON) GenTreeColon(TYP_VOID, gtNewNothingNode(), op1);
op1 = gtNewQmarkNode(TYP_VOID, condBox, op1);
@@ -14207,13 +14392,14 @@ void Compiler::impImportBlockCode(BasicBlock* block)
}
else
{
- unsigned callFlags = (helper == CORINFO_HELP_UNBOX) ? 0 : GTF_EXCEPT;
+ JITDUMP("\n Importing %s as helper call because %s\n", opcode == CEE_UNBOX ? "UNBOX" : "UNBOX.ANY",
+ canExpandInline ? "want smaller code or faster jitting" : "inline expansion not legal");
// Don't optimize, just call the helper and be done with it
args = gtNewArgList(op2, op1);
- op1 = gtNewHelperCallNode(helper,
- (var_types)((helper == CORINFO_HELP_UNBOX) ? TYP_BYREF : TYP_STRUCT),
- callFlags, args);
+ op1 =
+ gtNewHelperCallNode(helper,
+ (var_types)((helper == CORINFO_HELP_UNBOX) ? TYP_BYREF : TYP_STRUCT), args);
}
assert(helper == CORINFO_HELP_UNBOX && op1->gtType == TYP_BYREF || // Unbox helper returns a byref.
@@ -14368,6 +14554,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// stack changes (in generic code a 'T' becomes a 'boxed T')
if (!eeIsValueClass(resolvedToken.hClass))
{
+ JITDUMP("\n Importing BOX(refClass) as NOP\n");
verCurrentState.esStack[verCurrentState.esStackDepth - 1].seTypeInfo = tiRetVal;
break;
}
@@ -14384,6 +14571,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
if (unboxResolvedToken.hClass == resolvedToken.hClass)
{
+ JITDUMP("\n Importing BOX; UNBOX.ANY as NOP\n");
// Skip the next unbox.any instruction
sz += sizeof(mdToken) + 1;
break;
@@ -14526,7 +14714,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
block->bbSetRunRarely(); // any block with a throw is rare
/* Pop the exception object and create the 'throw' helper call */
- op1 = gtNewHelperCallNode(CORINFO_HELP_THROW, TYP_VOID, GTF_EXCEPT, gtNewArgList(impPopStack().val));
+ op1 = gtNewHelperCallNode(CORINFO_HELP_THROW, TYP_VOID, gtNewArgList(impPopStack().val));
EVAL_APPEND:
if (verCurrentState.esStackDepth > 0)
@@ -14565,7 +14753,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
/* Create the 'rethrow' helper call */
- op1 = gtNewHelperCallNode(CORINFO_HELP_RETHROW, TYP_VOID, GTF_EXCEPT);
+ op1 = gtNewHelperCallNode(CORINFO_HELP_RETHROW, TYP_VOID);
goto EVAL_APPEND;
@@ -14915,8 +15103,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
if (!opts.MinOpts() && !opts.compDbgCode)
{
/* Use GT_ARR_LENGTH operator so rng check opts see this */
- GenTreeArrLen* arrLen =
- new (this, GT_ARR_LENGTH) GenTreeArrLen(TYP_INT, op1, offsetof(CORINFO_Array, length));
+ GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, op1, offsetof(CORINFO_Array, length));
/* Mark the block as containing a length expression */
@@ -14932,13 +15119,10 @@ void Compiler::impImportBlockCode(BasicBlock* block)
/* Create the expression "*(array_addr + ArrLenOffs)" */
op1 = gtNewOperNode(GT_ADD, TYP_BYREF, op1,
gtNewIconNode(offsetof(CORINFO_Array, length), TYP_I_IMPL));
- op1 = gtNewOperNode(GT_IND, TYP_INT, op1);
+ op1 = gtNewIndir(TYP_INT, op1);
op1->gtFlags |= GTF_IND_ARR_LEN;
}
- /* An indirection will cause a GPF if the address is null */
- op1->gtFlags |= GTF_EXCEPT;
-
/* Push the result back on the stack */
impPushOnStack(op1, tiRetVal);
break;
@@ -15222,7 +15406,7 @@ bool Compiler::impReturnInstruction(BasicBlock* block, int prefixFlags, OPCODE&
// confirm that the argument is a GC pointer (for debugging (GC stress))
GenTreeArgList* args = gtNewArgList(op2);
- op2 = gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_REF, 0, args);
+ op2 = gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_REF, args);
if (verbose)
{
@@ -18110,11 +18294,22 @@ GenTreePtr Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo,
lvaTable[tmpNum].lvType = lclTyp;
- // Copy over class handle for ref types. Note this may be
- // further improved if it is a shared type and we know the exact context.
+ // For ref types, determine the type of the temp.
if (lclTyp == TYP_REF)
{
- lvaSetClass(tmpNum, lclInfo.lclVerTypeInfo.GetClassHandleForObjRef());
+ if (!argCanBeModified)
+ {
+ // If the arg can't be modified in the method
+ // body, use the type of the value, if
+ // known. Otherwise, use the declared type.
+ lvaSetClass(tmpNum, argInfo.argNode, lclInfo.lclVerTypeInfo.GetClassHandleForObjRef());
+ }
+ else
+ {
+ // Arg might be modified, use the delcared type of
+ // the argument.
+ lvaSetClass(tmpNum, lclInfo.lclVerTypeInfo.GetClassHandleForObjRef());
+ }
}
assert(lvaTable[tmpNum].lvAddrExposed == 0);
diff --git a/src/jit/instr.cpp b/src/jit/instr.cpp
index c4e894bdc8..e250902472 100644
--- a/src/jit/instr.cpp
+++ b/src/jit/instr.cpp
@@ -977,7 +977,7 @@ void CodeGen::sched_AM(instruction ins,
// Setup regVal
//
- regVal = regSet.rsPickReg(RBM_ALLINT & ~avoidMask);
+ regVal = regSet.rsPickFreeReg(RBM_ALLINT & ~avoidMask);
regTracker.rsTrackRegTrash(regVal);
avoidMask |= genRegMask(regVal);
var_types load_store_type;
@@ -2532,7 +2532,24 @@ AGAIN:
}
else
{
- regTmp = regSet.rsPickReg(RBM_ALLINT & ~genRegMask(reg));
+ // Lock the destination register to ensure that rsPickReg does not choose it.
+ const regMaskTP regMask = genRegMask(reg);
+ if ((regMask & regSet.rsMaskUsed) == 0)
+ {
+ regSet.rsLockReg(regMask);
+ regTmp = regSet.rsPickReg(RBM_ALLINT);
+ regSet.rsUnlockReg(regMask);
+ }
+ else if ((regMask & regSet.rsMaskLock) == 0)
+ {
+ regSet.rsLockUsedReg(regMask);
+ regTmp = regSet.rsPickReg(RBM_ALLINT);
+ regSet.rsUnlockUsedReg(regMask);
+ }
+ else
+ {
+ regTmp = regSet.rsPickReg(RBM_ALLINT);
+ }
}
#endif // LEGACY_BACKEND
diff --git a/src/jit/jit.h b/src/jit/jit.h
index d489247eb0..40533c04d7 100644
--- a/src/jit/jit.h
+++ b/src/jit/jit.h
@@ -514,11 +514,10 @@ const bool dspGCtbls = true;
#endif // !DEBUG
#ifdef DEBUG
-void JitDump(const char* pcFormat, ...);
#define JITDUMP(...) \
{ \
if (JitTls::GetCompiler()->verbose) \
- JitDump(__VA_ARGS__); \
+ logf(__VA_ARGS__); \
}
#define JITLOG(x) \
{ \
diff --git a/src/jit/jitconfig.cpp b/src/jit/jitconfig.cpp
index 9f0e226e3a..94a94305d1 100644
--- a/src/jit/jitconfig.cpp
+++ b/src/jit/jitconfig.cpp
@@ -14,6 +14,7 @@ JitConfigValues JitConfig;
void JitConfigValues::MethodSet::initialize(const wchar_t* list, ICorJitHost* host)
{
assert(m_list == nullptr);
+ assert(m_names == nullptr);
enum State
{
@@ -45,7 +46,7 @@ void JitConfigValues::MethodSet::initialize(const wchar_t* list, ICorJitHost* ho
{
// Failed to convert the list. Free the memory and ignore the list.
host->freeMemory(reinterpret_cast<void*>(const_cast<char*>(m_list)));
- m_list = "";
+ m_list = nullptr;
return;
}
@@ -230,11 +231,12 @@ void JitConfigValues::MethodSet::destroy(ICorJitHost* host)
next = name->m_next;
host->freeMemory(reinterpret_cast<void*>(const_cast<MethodName*>(name)));
}
-
- host->freeMemory(reinterpret_cast<void*>(const_cast<char*>(m_list)));
-
+ if (m_list != nullptr)
+ {
+ host->freeMemory(reinterpret_cast<void*>(const_cast<char*>(m_list)));
+ m_list = nullptr;
+ }
m_names = nullptr;
- m_list = nullptr;
}
static bool matchesName(const char* const name, int nameLen, const char* const s2)
diff --git a/src/jit/jitconfigvalues.h b/src/jit/jitconfigvalues.h
index 2db5dfef0b..cb503659fa 100644
--- a/src/jit/jitconfigvalues.h
+++ b/src/jit/jitconfigvalues.h
@@ -94,6 +94,7 @@ CONFIG_INTEGER(JitNoRegLoc, W("JitNoRegLoc"), 0)
CONFIG_INTEGER(JitNoStructPromotion, W("JitNoStructPromotion"), 0) // Disables struct promotion in Jit32
CONFIG_INTEGER(JitNoUnroll, W("JitNoUnroll"), 0)
CONFIG_INTEGER(JitOrder, W("JitOrder"), 0)
+CONFIG_INTEGER(JitReportFastTailCallDecisions, W("JitReportFastTailCallDecisions"), 0)
CONFIG_INTEGER(JitPInvokeCheckEnabled, W("JITPInvokeCheckEnabled"), 0)
CONFIG_INTEGER(JitPInvokeEnabled, W("JITPInvokeEnabled"), 1)
CONFIG_INTEGER(JitPrintInlinedMethods, W("JitPrintInlinedMethods"), 0)
diff --git a/src/jit/jitee.h b/src/jit/jitee.h
index 7a03dd69a9..5fc2c2cd8b 100644
--- a/src/jit/jitee.h
+++ b/src/jit/jitee.h
@@ -84,8 +84,10 @@ public:
#if defined(_TARGET_ARM_)
JIT_FLAG_RELATIVE_CODE_RELOCS = 41, // JIT should generate PC-relative address computations instead of EE relocation records
#else // !defined(_TARGET_ARM_)
- JIT_FLAG_UNUSED11 = 41
+ JIT_FLAG_UNUSED11 = 41,
#endif // !defined(_TARGET_ARM_)
+
+ JIT_FLAG_NO_INLINING = 42, // JIT should not inline any called method into this method
};
// clang-format on
@@ -204,6 +206,8 @@ public:
#endif // _TARGET_ARM_
+ FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_NO_INLINING, JIT_FLAG_NO_INLINING);
+
#undef FLAGS_EQUAL
}
diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp
index 374f255173..b46948773c 100644
--- a/src/jit/lclvars.cpp
+++ b/src/jit/lclvars.cpp
@@ -235,6 +235,60 @@ void Compiler::lvaInitTypeRef()
lvaInitArgs(&varDscInfo);
+#if FEATURE_FASTTAILCALL
+
+ //-------------------------------------------------------------------------
+ // Calculate the argument register usage.
+ //
+ // This will later be used for fastTailCall determination
+ //-------------------------------------------------------------------------
+
+ unsigned argRegCount = 0;
+ unsigned floatingRegCount = 0;
+ size_t stackSize = 0;
+
+ auto incrementRegCount = [&floatingRegCount, &argRegCount](LclVarDsc* varDsc) {
+ if (varDsc->lvIsHfa())
+ {
+ floatingRegCount += varDsc->lvHfaSlots();
+ }
+ else
+ {
+ varDsc->IsFloatRegType() ? ++floatingRegCount : ++argRegCount;
+ }
+ };
+
+ unsigned argNum;
+ LclVarDsc* curDsc;
+
+ for (curDsc = lvaTable, argNum = 0; argNum < varDscInfo.varNum; argNum++, curDsc++)
+ {
+ if (curDsc->lvIsRegArg)
+ {
+ incrementRegCount(curDsc);
+#if FEATURE_MULTIREG_ARGS
+ if (curDsc->lvOtherArgReg != REG_NA)
+ {
+ incrementRegCount(curDsc);
+ }
+#endif // FEATURE_MULTIREG_ARGS
+ }
+ else
+ {
+ stackSize += curDsc->lvArgStackSize();
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // Save the register usage information and stack size.
+ //-------------------------------------------------------------------------
+
+ info.compArgRegCount = argRegCount;
+ info.compFloatArgRegCount = floatingRegCount;
+ info.compArgStackSize = stackSize;
+
+#endif // FEATURE_FASTTAILCALL
+
//-------------------------------------------------------------------------
// Finally the local variables
//-------------------------------------------------------------------------
@@ -247,15 +301,16 @@ void Compiler::lvaInitTypeRef()
i++, varNum++, varDsc++, localsSig = info.compCompHnd->getArgNext(localsSig))
{
CORINFO_CLASS_HANDLE typeHnd;
- CorInfoTypeWithMod corInfoType =
+ CorInfoTypeWithMod corInfoTypeWithMod =
info.compCompHnd->getArgType(&info.compMethodInfo->locals, localsSig, &typeHnd);
+ CorInfoType corInfoType = strip(corInfoTypeWithMod);
- lvaInitVarDsc(varDsc, varNum, strip(corInfoType), typeHnd, localsSig, &info.compMethodInfo->locals);
+ lvaInitVarDsc(varDsc, varNum, corInfoType, typeHnd, localsSig, &info.compMethodInfo->locals);
- varDsc->lvPinned = ((corInfoType & CORINFO_TYPE_MOD_PINNED) != 0);
+ varDsc->lvPinned = ((corInfoTypeWithMod & CORINFO_TYPE_MOD_PINNED) != 0);
varDsc->lvOnFrame = true; // The final home for this local variable might be our local stack frame
- if (strip(corInfoType) == CORINFO_TYPE_CLASS)
+ if (corInfoType == CORINFO_TYPE_CLASS)
{
CORINFO_CLASS_HANDLE clsHnd = info.compCompHnd->getArgClass(&info.compMethodInfo->locals, localsSig);
lvaSetClass(varNum, clsHnd);
@@ -1253,6 +1308,10 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc,
#ifdef DEBUG
varDsc->lvStkOffs = BAD_STK_OFFS;
#endif
+
+#if FEATURE_MULTIREG_ARGS
+ varDsc->lvOtherArgReg = REG_NA;
+#endif // FEATURE_MULTIREG_ARGS
}
/*****************************************************************************
@@ -1477,14 +1536,13 @@ void Compiler::lvaCanPromoteStructType(CORINFO_CLASS_HANDLE typeHnd,
#if 1 // TODO-Cleanup: Consider removing this entire #if block in the future
-// This method has two callers. The one in Importer.cpp passes sortFields == false
-// and the other passes sortFields == true.
-// This is a workaround that leaves the inlining behavior the same as before while still
-// performing extra struct promotions when compiling the method.
+// This method has two callers. The one in Importer.cpp passes `sortFields == false` and the other passes
+// `sortFields == true`. This is a workaround that leaves the inlining behavior the same as before while still
+// performing extra struct promotion when compiling the method.
//
-// The x86 legacy back-end can't handle the more general RyuJIT struct promotion (notably structs
-// with holes), in genPushArgList(), so in that case always check for custom layout.
-#if FEATURE_FIXED_OUT_ARGS || !defined(LEGACY_BACKEND)
+// The legacy back-end can't handle this more general struct promotion (notably structs with holes) in
+// morph/genPushArgList()/SetupLateArgs, so in that case always check for custom layout.
+#if !defined(LEGACY_BACKEND)
if (!sortFields) // the condition "!sortFields" really means "we are inlining"
#endif
{
@@ -2036,7 +2094,12 @@ void Compiler::lvaPromoteLongVars()
fieldVarDsc->lvFldOffset = (unsigned char)(index * genTypeSize(TYP_INT));
fieldVarDsc->lvFldOrdinal = (unsigned char)index;
fieldVarDsc->lvParentLcl = lclNum;
- fieldVarDsc->lvIsParam = isParam;
+ // Currently we do not support enregistering incoming promoted aggregates with more than one field.
+ if (isParam)
+ {
+ fieldVarDsc->lvIsParam = true;
+ lvaSetVarDoNotEnregister(varNum DEBUGARG(DNER_LongParamField));
+ }
}
}
@@ -2171,6 +2234,11 @@ void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregister
assert(varDsc->lvPinned);
break;
#endif
+#if !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_)
+ case DNER_LongParamField:
+ JITDUMP("it is a decomposed field of a long parameter\n");
+ break;
+#endif
default:
unreached();
break;
@@ -3441,6 +3509,48 @@ void LclVarDsc::lvaDisqualifyVar()
}
#endif // ASSERTION_PROP
+/**********************************************************************************
+* Get stack size of the varDsc.
+*/
+const size_t LclVarDsc::lvArgStackSize() const
+{
+ // Make sure this will have a stack size
+ assert(!this->lvIsRegArg);
+
+ size_t stackSize = 0;
+ if (varTypeIsStruct(this))
+ {
+#if defined(WINDOWS_AMD64_ABI)
+ // Structs are either passed by reference or can be passed by value using one pointer
+ stackSize = TARGET_POINTER_SIZE;
+#elif defined(_TARGET_ARM64_) || defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ // lvSize performs a roundup.
+ stackSize = this->lvSize();
+
+#if defined(_TARGET_ARM64_)
+ if ((stackSize > TARGET_POINTER_SIZE * 2) && (!this->lvIsHfa()))
+ {
+ // If the size is greater than 16 bytes then it will
+ // be passed by reference.
+ stackSize = TARGET_POINTER_SIZE;
+ }
+#endif // defined(_TARGET_ARM64_)
+
+#else // !_TARGET_ARM64_ !WINDOWS_AMD64_ABI !FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+ NYI("Unsupported target.");
+ unreached();
+
+#endif // !_TARGET_ARM64_ !WINDOWS_AMD64_ABI !FEATURE_UNIX_AMD64_STRUCT_PASSING
+ }
+ else
+ {
+ stackSize = TARGET_POINTER_SIZE;
+ }
+
+ return stackSize;
+}
+
#ifndef LEGACY_BACKEND
/**********************************************************************************
* Get type of a variable when passed as an argument.
@@ -4478,7 +4588,7 @@ unsigned Compiler::lvaGetMaxSpillTempSize()
void Compiler::lvaAssignFrameOffsets(FrameLayoutState curState)
{
- noway_assert(lvaDoneFrameLayout < curState);
+ noway_assert((lvaDoneFrameLayout < curState) || (curState == REGALLOC_FRAME_LAYOUT));
lvaDoneFrameLayout = curState;
@@ -5643,13 +5753,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
bool tempsAllocated = false;
-#ifdef _TARGET_ARM_
- // On ARM, SP based offsets use smaller encoding. Since temps are relatively
- // rarer than lcl usage, allocate them farther from SP.
- if (!opts.MinOpts() && !compLocallocUsed)
-#else
if (lvaTempsHaveLargerOffsetThanVars() && !codeGen->isFramePointerUsed())
-#endif
{
// Because we want the temps to have a larger offset than locals
// and we're not using a frame pointer, we have to place the temps
diff --git a/src/jit/lir.h b/src/jit/lir.h
index 762c79c3c3..4a71947be7 100644
--- a/src/jit/lir.h
+++ b/src/jit/lir.h
@@ -112,12 +112,12 @@ public:
GenTree* m_firstNode;
GenTree* m_lastNode;
- ReadOnlyRange(GenTree* firstNode, GenTree* lastNode);
-
ReadOnlyRange(const ReadOnlyRange& other) = delete;
ReadOnlyRange& operator=(const ReadOnlyRange& other) = delete;
public:
+ ReadOnlyRange(GenTree* firstNode, GenTree* lastNode);
+
class Iterator
{
friend class ReadOnlyRange;
@@ -312,6 +312,9 @@ public:
inline void GenTree::SetUnusedValue()
{
gtLIRFlags |= LIR::Flags::UnusedValue;
+#ifndef LEGACY_BACKEND
+ ClearContained();
+#endif
}
inline void GenTree::ClearUnusedValue()
diff --git a/src/jit/liveness.cpp b/src/jit/liveness.cpp
index 4b8d602aac..fdec25bb02 100644
--- a/src/jit/liveness.cpp
+++ b/src/jit/liveness.cpp
@@ -1031,7 +1031,7 @@ void Compiler::fgExtendDbgLifetimes()
#if !defined(_TARGET_64BIT_)
DecomposeLongs::DecomposeRange(this, blockWeight, initRange);
#endif // !defined(_TARGET_64BIT_)
- m_pLowering->LowerRange(std::move(initRange));
+ m_pLowering->LowerRange(block, initRange);
#endif // !LEGACY_BACKEND
// Naively inserting the initializer at the end of the block may add code after the block's
@@ -1688,203 +1688,238 @@ void Compiler::fgComputeLifeCall(VARSET_TP& life, GenTreeCall* call)
}
//------------------------------------------------------------------------
-// Compiler::fgComputeLifeLocal: compute the changes to local var liveness
-// due to a use or a def of a local var and
-// indicates wither the use/def is a dead
-// store.
+// Compiler::fgComputeLifeTrackedLocalUse:
+// Compute the changes to local var liveness due to a use of a tracked local var.
//
// Arguments:
// life - The live set that is being computed.
-// keepAliveVars - The currents set of variables to keep alive
-// regardless of their actual lifetime.
-// lclVarNode - The node that corresponds to the local var def or
-// use. Only differs from `node` when targeting the
-// legacy backend.
-// node - The actual tree node being processed.
-//
-// Returns:
-// `true` if the local var node corresponds to a dead store; `false`
-// otherwise.
-//
-bool Compiler::fgComputeLifeLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, GenTree* lclVarNode, GenTree* node)
+// varDsc - The LclVar descriptor for the variable being used or defined.
+// node - The node that is defining the lclVar.
+void Compiler::fgComputeLifeTrackedLocalUse(VARSET_TP& life, LclVarDsc& varDsc, GenTreeLclVarCommon* node)
{
- unsigned lclNum = lclVarNode->gtLclVarCommon.gtLclNum;
-
- assert(lclNum < lvaCount);
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ assert(node != nullptr);
+ assert((node->gtFlags & GTF_VAR_DEF) == 0);
+ assert(varDsc.lvTracked);
- unsigned varIndex;
- VARSET_TP varBit;
+ const unsigned varIndex = varDsc.lvVarIndex;
- // Is this a tracked variable?
- if (varDsc->lvTracked)
+ // Is the variable already known to be alive?
+ if (VarSetOps::IsMember(this, life, varIndex))
{
- varIndex = varDsc->lvVarIndex;
- assert(varIndex < lvaTrackedCount);
-
- /* Is this a definition or use? */
-
- if (lclVarNode->gtFlags & GTF_VAR_DEF)
- {
- /*
- The variable is being defined here. The variable
- should be marked dead from here until its closest
- previous use.
-
- IMPORTANT OBSERVATION:
-
- For GTF_VAR_USEASG (i.e. x <op>= a) we cannot
- consider it a "pure" definition because it would
- kill x (which would be wrong because x is
- "used" in such a construct) -> see below the case when x is live
- */
-
- if (VarSetOps::IsMember(this, life, varIndex))
- {
- /* The variable is live */
-
- if ((lclVarNode->gtFlags & GTF_VAR_USEASG) == 0)
- {
- /* Mark variable as dead from here to its closest use */
+ // Since we may do liveness analysis multiple times, clear the GTF_VAR_DEATH if set.
+ node->gtFlags &= ~GTF_VAR_DEATH;
+ return;
+ }
- if (!VarSetOps::IsMember(this, keepAliveVars, varIndex))
- {
- VarSetOps::RemoveElemD(this, life, varIndex);
- }
#ifdef DEBUG
- if (verbose && 0)
- {
- printf("Def V%02u,T%02u at ", lclNum, varIndex);
- printTreeID(lclVarNode);
- printf(" life %s -> %s\n",
- VarSetOps::ToString(this, VarSetOps::Union(this, life,
- VarSetOps::MakeSingleton(this, varIndex))),
- VarSetOps::ToString(this, life));
- }
+ if (verbose && 0)
+ {
+ printf("Ref V%02u,T%02u] at ", node->gtLclNum, varIndex);
+ printTreeID(node);
+ printf(" life %s -> %s\n", VarSetOps::ToString(this, life),
+ VarSetOps::ToString(this, VarSetOps::AddElem(this, life, varIndex)));
+ }
#endif // DEBUG
- }
- }
- else
- {
- /* Dead assignment to the variable */
- lclVarNode->gtFlags |= GTF_VAR_DEATH;
- if (!opts.MinOpts())
- {
- // keepAliveVars always stay alive
- noway_assert(!VarSetOps::IsMember(this, keepAliveVars, varIndex));
+ // The variable is being used, and it is not currently live.
+ // So the variable is just coming to life
+ node->gtFlags |= GTF_VAR_DEATH;
+ VarSetOps::AddElemD(this, life, varIndex);
- /* This is a dead store unless the variable is marked
- GTF_VAR_USEASG and we are in an interior statement
- that will be used (e.g. while (i++) or a GT_COMMA) */
+ // Record interference with other live variables
+ fgMarkIntf(life, varIndex);
+}
- // Do not consider this store dead if the target local variable represents
- // a promoted struct field of an address exposed local or if the address
- // of the variable has been exposed. Improved alias analysis could allow
- // stores to these sorts of variables to be removed at the cost of compile
- // time.
- return !varDsc->lvAddrExposed &&
- !(varDsc->lvIsStructField && lvaTable[varDsc->lvParentLcl].lvAddrExposed);
- }
- }
+//------------------------------------------------------------------------
+// Compiler::fgComputeLifeTrackedLocalDef:
+// Compute the changes to local var liveness due to a def of a tracked local var and return `true` if the def is a
+// dead store.
+//
+// Arguments:
+// life - The live set that is being computed.
+// keepAliveVars - The current set of variables to keep alive regardless of their actual lifetime.
+// varDsc - The LclVar descriptor for the variable being used or defined.
+// node - The node that is defining the lclVar.
+//
+// Returns:
+// `true` if the def is a dead store; `false` otherwise.
+bool Compiler::fgComputeLifeTrackedLocalDef(VARSET_TP& life,
+ VARSET_VALARG_TP keepAliveVars,
+ LclVarDsc& varDsc,
+ GenTreeLclVarCommon* node)
+{
+ assert(node != nullptr);
+ assert((node->gtFlags & GTF_VAR_DEF) != 0);
+ assert(varDsc.lvTracked);
- return false;
- }
- else // it is a use
+ const unsigned varIndex = varDsc.lvVarIndex;
+ if (VarSetOps::IsMember(this, life, varIndex))
+ {
+ // The variable is live
+ if ((node->gtFlags & GTF_VAR_USEASG) == 0)
{
- // Is the variable already known to be alive?
- if (VarSetOps::IsMember(this, life, varIndex))
+ // Remove the variable from the live set if it is not in the keepalive set.
+ if (!VarSetOps::IsMember(this, keepAliveVars, varIndex))
{
- // Since we may do liveness analysis multiple times, clear the GTF_VAR_DEATH if set.
- lclVarNode->gtFlags &= ~GTF_VAR_DEATH;
- return false;
+ VarSetOps::RemoveElemD(this, life, varIndex);
}
-
#ifdef DEBUG
if (verbose && 0)
{
- printf("Ref V%02u,T%02u] at ", lclNum, varIndex);
+ printf("Def V%02u,T%02u at ", node->gtLclNum, varIndex);
printTreeID(node);
- printf(" life %s -> %s\n", VarSetOps::ToString(this, life),
- VarSetOps::ToString(this, VarSetOps::AddElem(this, life, varIndex)));
+ printf(" life %s -> %s\n",
+ VarSetOps::ToString(this,
+ VarSetOps::Union(this, life, VarSetOps::MakeSingleton(this, varIndex))),
+ VarSetOps::ToString(this, life));
}
#endif // DEBUG
+ }
+ }
+ else
+ {
+ // Dead store
+ node->gtFlags |= GTF_VAR_DEATH;
- // The variable is being used, and it is not currently live.
- // So the variable is just coming to life
- lclVarNode->gtFlags |= GTF_VAR_DEATH;
- VarSetOps::AddElemD(this, life, varIndex);
+ if (!opts.MinOpts())
+ {
+ // keepAliveVars always stay alive
+ noway_assert(!VarSetOps::IsMember(this, keepAliveVars, varIndex));
- // Record interference with other live variables
- fgMarkIntf(life, varIndex);
+ // Do not consider this store dead if the target local variable represents
+ // a promoted struct field of an address exposed local or if the address
+ // of the variable has been exposed. Improved alias analysis could allow
+ // stores to these sorts of variables to be removed at the cost of compile
+ // time.
+ return !varDsc.lvAddrExposed && !(varDsc.lvIsStructField && lvaTable[varDsc.lvParentLcl].lvAddrExposed);
}
}
- // Note that promoted implies not tracked (i.e. only the fields are tracked).
- else if (varTypeIsStruct(varDsc->lvType))
- {
- noway_assert(!varDsc->lvTracked);
- lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
+ return false;
+}
- if (promotionType != PROMOTION_TYPE_NONE)
- {
- VarSetOps::AssignNoCopy(this, varBit, VarSetOps::MakeEmpty(this));
+//------------------------------------------------------------------------
+// Compiler::fgComputeLifeUntrackedLocal:
+// Compute the changes to local var liveness due to a use or a def of an untracked local var.
+//
+// Note:
+// It may seem a bit counter-intuitive that a change to an untracked lclVar could affect the liveness of tracked
+// lclVars. In theory, this could happen with promoted (especially dependently-promoted) structs: in these cases,
+// a use or def of the untracked struct var is treated as a use or def of any of its component fields that are
+// tracked.
+//
+// Arguments:
+// life - The live set that is being computed.
+// keepAliveVars - The current set of variables to keep alive regardless of their actual lifetime.
+// varDsc - The LclVar descriptor for the variable being used or defined.
+// lclVarNode - The node that corresponds to the local var def or use. Only differs from `node` when targeting
+// the legacy backend.
+// node - The actual tree node being processed.
+void Compiler::fgComputeLifeUntrackedLocal(
+ VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, LclVarDsc& varDsc, GenTreeLclVarCommon* lclVarNode, GenTree* node)
+{
+ assert(lclVarNode != nullptr);
+ assert(node != nullptr);
- for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
- {
+ if (!varTypeIsStruct(varDsc.lvType) || (lvaGetPromotionType(&varDsc) == PROMOTION_TYPE_NONE))
+ {
+ return;
+ }
+
+ VARSET_TP varBit(VarSetOps::MakeEmpty(this));
+
+ for (unsigned i = varDsc.lvFieldLclStart; i < varDsc.lvFieldLclStart + varDsc.lvFieldCnt; ++i)
+ {
#if !defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND)
- if (!varTypeIsLong(lvaTable[i].lvType) || !lvaTable[i].lvPromoted)
+ if (!varTypeIsLong(lvaTable[i].lvType) || !lvaTable[i].lvPromoted)
#endif // !defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND)
- {
- noway_assert(lvaTable[i].lvIsStructField);
- }
- if (lvaTable[i].lvTracked)
- {
- varIndex = lvaTable[i].lvVarIndex;
- noway_assert(varIndex < lvaTrackedCount);
- VarSetOps::AddElemD(this, varBit, varIndex);
- }
- }
- if (node->gtFlags & GTF_VAR_DEF)
- {
- VarSetOps::DiffD(this, varBit, keepAliveVars);
- VarSetOps::DiffD(this, life, varBit);
- return false;
- }
- // This is a use.
+ {
+ noway_assert(lvaTable[i].lvIsStructField);
+ }
+ if (lvaTable[i].lvTracked)
+ {
+ const unsigned varIndex = lvaTable[i].lvVarIndex;
+ noway_assert(varIndex < lvaTrackedCount);
+ VarSetOps::AddElemD(this, varBit, varIndex);
+ }
+ }
+ if (node->gtFlags & GTF_VAR_DEF)
+ {
+ VarSetOps::DiffD(this, varBit, keepAliveVars);
+ VarSetOps::DiffD(this, life, varBit);
+ return;
+ }
+ // This is a use.
- // Are the variables already known to be alive?
- if (VarSetOps::IsSubset(this, varBit, life))
- {
- node->gtFlags &= ~GTF_VAR_DEATH; // Since we may now call this multiple times, reset if live.
- return false;
- }
+ // Are the variables already known to be alive?
+ if (VarSetOps::IsSubset(this, varBit, life))
+ {
+ node->gtFlags &= ~GTF_VAR_DEATH; // Since we may now call this multiple times, reset if live.
+ return;
+ }
- // Some variables are being used, and they are not currently live.
- // So they are just coming to life, in the backwards traversal; in a forwards
- // traversal, one or more are dying. Mark this.
+ // Some variables are being used, and they are not currently live.
+ // So they are just coming to life, in the backwards traversal; in a forwards
+ // traversal, one or more are dying. Mark this.
- node->gtFlags |= GTF_VAR_DEATH;
+ node->gtFlags |= GTF_VAR_DEATH;
- // Are all the variables becoming alive (in the backwards traversal), or just a subset?
- if (!VarSetOps::IsEmptyIntersection(this, varBit, life))
- {
- // Only a subset of the variables are become live; we must record that subset.
- // (Lack of an entry for "lclVarNode" will be considered to imply all become dead in the
- // forward traversal.)
- VARSET_TP* deadVarSet = new (this, CMK_bitset) VARSET_TP;
- VarSetOps::AssignNoCopy(this, *deadVarSet, VarSetOps::Diff(this, varBit, life));
- GetPromotedStructDeathVars()->Set(lclVarNode, deadVarSet);
- }
+ // Are all the variables becoming alive (in the backwards traversal), or just a subset?
+ if (!VarSetOps::IsEmptyIntersection(this, varBit, life))
+ {
+ // Only a subset of the variables are become live; we must record that subset.
+ // (Lack of an entry for "lclVarNode" will be considered to imply all become dead in the
+ // forward traversal.)
+ VARSET_TP* deadVarSet = new (this, CMK_bitset) VARSET_TP;
+ VarSetOps::AssignNoCopy(this, *deadVarSet, VarSetOps::Diff(this, varBit, life));
+ GetPromotedStructDeathVars()->Set(lclVarNode, deadVarSet);
+ }
+
+ // In any case, all the field vars are now live (in the backwards traversal).
+ VarSetOps::UnionD(this, life, varBit);
+
+ // Record interference with other live variables
+ fgMarkIntf(life, varBit);
+}
+
+//------------------------------------------------------------------------
+// Compiler::fgComputeLifeLocal:
+// Compute the changes to local var liveness due to a use or a def of a local var and indicates whether the use/def
+// is a dead store.
+//
+// Arguments:
+// life - The live set that is being computed.
+// keepAliveVars - The current set of variables to keep alive regardless of their actual lifetime.
+// lclVarNode - The node that corresponds to the local var def or use. Only differs from `node` when targeting
+// the legacy backend.
+// node - The actual tree node being processed.
+//
+// Returns:
+// `true` if the local var node corresponds to a dead store; `false` otherwise.
+bool Compiler::fgComputeLifeLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, GenTree* lclVarNode, GenTree* node)
+{
+ unsigned lclNum = lclVarNode->gtLclVarCommon.gtLclNum;
- // In any case, all the field vars are now live (in the backwards traversal).
- VarSetOps::UnionD(this, life, varBit);
+ assert(lclNum < lvaCount);
+ LclVarDsc& varDsc = lvaTable[lclNum];
- // Record interference with other live variables
- fgMarkIntf(life, varBit);
+ // Is this a tracked variable?
+ if (varDsc.lvTracked)
+ {
+ /* Is this a definition or use? */
+ if (lclVarNode->gtFlags & GTF_VAR_DEF)
+ {
+ return fgComputeLifeTrackedLocalDef(life, keepAliveVars, varDsc, lclVarNode->AsLclVarCommon());
+ }
+ else
+ {
+ fgComputeLifeTrackedLocalUse(life, varDsc, lclVarNode->AsLclVarCommon());
}
}
-
+ else
+ {
+ fgComputeLifeUntrackedLocal(life, keepAliveVars, varDsc, lclVarNode->AsLclVarCommon(), node);
+ }
return false;
}
@@ -1963,17 +1998,244 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR
{
next = node->gtPrev;
- if (node->OperGet() == GT_CALL)
+ bool isDeadStore;
+ switch (node->OperGet())
{
- fgComputeLifeCall(life, node->AsCall());
- }
- else if (node->OperIsNonPhiLocal() || node->OperIsLocalAddr())
- {
- bool isDeadStore = fgComputeLifeLocal(life, keepAliveVars, node, node);
- if (isDeadStore && fgTryRemoveDeadLIRStore(blockRange, node, &next))
+ case GT_CALL:
{
- fgStmtRemoved = true;
+ GenTreeCall* const call = node->AsCall();
+ if (((call->TypeGet() == TYP_VOID) || call->IsUnusedValue()) && !call->HasSideEffects(this))
+ {
+ JITDUMP("Removing dead call:\n");
+ DISPNODE(call);
+
+ node->VisitOperands([](GenTree* operand) -> GenTree::VisitResult {
+ if (operand->IsValue())
+ {
+ operand->SetUnusedValue();
+ }
+
+ // Special-case PUTARG_STK: since this operator is not considered a value, DCE will not remove
+ // these nodes.
+ if (operand->OperIs(GT_PUTARG_STK))
+ {
+ operand->AsPutArgStk()->gtOp1->SetUnusedValue();
+ operand->gtBashToNOP();
+ }
+
+ return GenTree::VisitResult::Continue;
+ });
+
+ blockRange.Remove(node);
+
+ // Removing a call does not affect liveness unless it is a tail call in a nethod with P/Invokes or
+ // is itself a P/Invoke, in which case it may affect the liveness of the frame root variable.
+ fgStmtRemoved = !opts.MinOpts() && !opts.ShouldUsePInvokeHelpers() &&
+ ((call->IsTailCall() && info.compCallUnmanaged) || call->IsUnmanaged()) &&
+ lvaTable[info.compLvFrameListRoot].lvTracked;
+ }
+ else
+ {
+ fgComputeLifeCall(life, call);
+ }
+ break;
+ }
+
+ case GT_LCL_VAR:
+ case GT_LCL_FLD:
+ {
+ GenTreeLclVarCommon* const lclVarNode = node->AsLclVarCommon();
+ LclVarDsc& varDsc = lvaTable[lclVarNode->gtLclNum];
+
+ if (node->IsUnusedValue())
+ {
+ JITDUMP("Removing dead LclVar use:\n");
+ DISPNODE(lclVarNode);
+
+ blockRange.Delete(this, block, node);
+ fgStmtRemoved = varDsc.lvTracked && !opts.MinOpts();
+ }
+ else if (varDsc.lvTracked)
+ {
+ fgComputeLifeTrackedLocalUse(life, varDsc, lclVarNode);
+ }
+ else
+ {
+ fgComputeLifeUntrackedLocal(life, keepAliveVars, varDsc, lclVarNode, node);
+ }
+ break;
+ }
+
+ case GT_LCL_VAR_ADDR:
+ case GT_LCL_FLD_ADDR:
+ if (node->IsUnusedValue())
+ {
+ JITDUMP("Removing dead LclVar address:\n");
+ DISPNODE(node);
+
+ const bool isTracked = lvaTable[node->AsLclVarCommon()->gtLclNum].lvTracked;
+ blockRange.Delete(this, block, node);
+ fgStmtRemoved = isTracked && !opts.MinOpts();
+ }
+ else
+ {
+ isDeadStore = fgComputeLifeLocal(life, keepAliveVars, node, node);
+ if (isDeadStore)
+ {
+ LIR::Use addrUse;
+ if (blockRange.TryGetUse(node, &addrUse) && (addrUse.User()->OperGet() == GT_STOREIND))
+ {
+ // Remove the store. DCE will iteratively clean up any ununsed operands.
+ GenTreeStoreInd* const store = addrUse.User()->AsStoreInd();
+
+ JITDUMP("Removing dead indirect store:\n");
+ DISPNODE(store);
+
+ assert(store->Addr() == node);
+ blockRange.Delete(this, block, node);
+
+ store->Data()->SetUnusedValue();
+
+ blockRange.Remove(store);
+
+ assert(!opts.MinOpts());
+ fgStmtRemoved = true;
+ }
+ }
+ }
+ break;
+
+ case GT_STORE_LCL_VAR:
+ case GT_STORE_LCL_FLD:
+ {
+ GenTreeLclVarCommon* const lclVarNode = node->AsLclVarCommon();
+
+ LclVarDsc& varDsc = lvaTable[lclVarNode->gtLclNum];
+ if (varDsc.lvTracked)
+ {
+ isDeadStore = fgComputeLifeTrackedLocalDef(life, keepAliveVars, varDsc, lclVarNode);
+ if (isDeadStore)
+ {
+ JITDUMP("Removing dead store:\n");
+ DISPNODE(lclVarNode);
+
+ // Remove the store. DCE will iteratively clean up any ununsed operands.
+ lclVarNode->gtOp1->SetUnusedValue();
+
+ lvaDecRefCnts(block, node);
+
+ // If the store is marked as a late argument, it is referenced by a call. Instead of removing
+ // it, bash it to a NOP.
+ if ((node->gtFlags & GTF_LATE_ARG) != 0)
+ {
+ JITDUMP("node is a late arg; replacing with NOP\n");
+ node->gtBashToNOP();
+
+ // NOTE: this is a bit of a hack. We need to keep these nodes around as they are
+ // referenced by the call, but they're considered side-effect-free non-value-producing
+ // nodes, so they will be removed if we don't do this.
+ node->gtFlags |= GTF_ORDER_SIDEEFF;
+ }
+ else
+ {
+ blockRange.Remove(node);
+ }
+
+ assert(!opts.MinOpts());
+ fgStmtRemoved = true;
+ }
+ }
+ else
+ {
+ fgComputeLifeUntrackedLocal(life, keepAliveVars, varDsc, lclVarNode, node);
+ }
+ break;
}
+
+ case GT_LABEL:
+ case GT_FTN_ADDR:
+ case GT_CNS_INT:
+ case GT_CNS_LNG:
+ case GT_CNS_DBL:
+ case GT_CNS_STR:
+ case GT_CLS_VAR_ADDR:
+ case GT_PHYSREG:
+ // These are all side-effect-free leaf nodes.
+ if (node->IsUnusedValue())
+ {
+ JITDUMP("Removing dead node:\n");
+ DISPNODE(node);
+
+ blockRange.Remove(node);
+ }
+ break;
+
+ case GT_LOCKADD:
+ case GT_XADD:
+ case GT_XCHG:
+ case GT_CMPXCHG:
+ case GT_MEMORYBARRIER:
+ case GT_JMP:
+ case GT_STOREIND:
+ case GT_ARR_BOUNDS_CHECK:
+ case GT_STORE_OBJ:
+ case GT_STORE_BLK:
+ case GT_STORE_DYN_BLK:
+#if defined(FEATURE_SIMD)
+ case GT_SIMD_CHK:
+#endif // FEATURE_SIMD
+ case GT_CMP:
+ case GT_JCC:
+ case GT_JTRUE:
+ case GT_RETURN:
+ case GT_SWITCH:
+ case GT_RETFILT:
+ case GT_START_NONGC:
+ case GT_PROF_HOOK:
+#if !FEATURE_EH_FUNCLETS
+ case GT_END_LFIN:
+#endif // !FEATURE_EH_FUNCLETS
+ case GT_SWITCH_TABLE:
+ case GT_PINVOKE_PROLOG:
+ case GT_PINVOKE_EPILOG:
+ case GT_RETURNTRAP:
+ case GT_PUTARG_STK:
+ case GT_IL_OFFSET:
+ // Never remove these nodes, as they are always side-effecting.
+ //
+ // NOTE: the only side-effect of some of these nodes (GT_CMP, GT_SUB_HI) is a write to the flags
+ // register.
+ // Properly modeling this would allow these nodes to be removed.
+ break;
+
+ case GT_NOP:
+ // NOTE: we need to keep some NOPs around because they are referenced by calls. See the dead store
+ // removal code above (case GT_STORE_LCL_VAR) for more explanation.
+ if ((node->gtFlags & GTF_ORDER_SIDEEFF) != 0)
+ {
+ break;
+ }
+ __fallthrough;
+
+ default:
+ assert(!node->OperIsLocal());
+ if (!node->IsValue() || node->IsUnusedValue())
+ {
+ unsigned sideEffects = node->gtFlags & (GTF_SIDE_EFFECT | GTF_SET_FLAGS);
+ if ((sideEffects == 0) || ((sideEffects == GTF_EXCEPT) && !node->OperMayThrow(this)))
+ {
+ JITDUMP("Removing dead node:\n");
+ DISPNODE(node);
+
+ node->VisitOperands([](GenTree* operand) -> GenTree::VisitResult {
+ operand->SetUnusedValue();
+ return GenTree::VisitResult::Continue;
+ });
+
+ blockRange.Remove(node);
+ }
+ }
+ break;
}
}
}
@@ -2322,89 +2584,6 @@ void Compiler::fgComputeLife(VARSET_TP& life,
#endif // !LEGACY_BACKEND
-bool Compiler::fgTryRemoveDeadLIRStore(LIR::Range& blockRange, GenTree* node, GenTree** next)
-{
- assert(node != nullptr);
- assert(next != nullptr);
-
- assert(node->OperIsLocalStore() || node->OperIsLocalAddr());
-
- GenTree* store = nullptr;
- GenTree* value = nullptr;
- if (node->OperIsLocalStore())
- {
- store = node;
- value = store->gtGetOp1();
- }
- else if (node->OperIsLocalAddr())
- {
- LIR::Use addrUse;
- if (!blockRange.TryGetUse(node, &addrUse) || (addrUse.User()->OperGet() != GT_STOREIND))
- {
- *next = node->gtPrev;
- return false;
- }
-
- store = addrUse.User();
- value = store->gtGetOp2();
- }
-
- bool isClosed = false;
- unsigned sideEffects = 0;
- LIR::ReadOnlyRange operandsRange = blockRange.GetRangeOfOperandTrees(store, &isClosed, &sideEffects);
- if (!isClosed || ((sideEffects & GTF_SIDE_EFFECT) != 0) ||
- (((sideEffects & GTF_ORDER_SIDEEFF) != 0) && (value->OperGet() == GT_CATCH_ARG)))
- {
- // If the range of the operands contains unrelated code or if it contains any side effects,
- // do not remove it. Instead, just remove the store.
-
- store->VisitOperands([](GenTree* operand) -> GenTree::VisitResult {
- operand->SetUnusedValue();
- return GenTree::VisitResult::Continue;
- });
-
- *next = node->gtPrev;
- }
- else
- {
- // Okay, the operands to the store form a contiguous range that has no side effects. Remove the
- // range containing the operands and decrement the local var ref counts appropriately.
-
- // Compute the next node to process. Note that we must be careful not to set the next node to
- // process to a node that we are about to remove.
- if (node->OperIsLocalStore())
- {
- assert(node == store);
- *next = (operandsRange.LastNode()->gtNext == store) ? operandsRange.FirstNode()->gtPrev : node->gtPrev;
- }
- else
- {
- assert(operandsRange.Contains(node));
- *next = operandsRange.FirstNode()->gtPrev;
- }
-
- blockRange.Delete(this, compCurBB, std::move(operandsRange));
- }
-
- // If the store is marked as a late argument, it is referenced by a call. Instead of removing it,
- // bash it to a NOP.
- if ((store->gtFlags & GTF_LATE_ARG) != 0)
- {
- if (store->IsLocal())
- {
- lvaDecRefCnts(compCurBB, store);
- }
-
- store->gtBashToNOP();
- }
- else
- {
- blockRange.Delete(this, compCurBB, store);
- }
-
- return true;
-}
-
// fgRemoveDeadStore - remove a store to a local which has no exposed uses.
//
// pTree - GenTree** to local, including store-form local or local addr (post-rationalize)
@@ -3037,6 +3216,7 @@ void Compiler::fgInterBlockLocalVarLiveness()
{
gtSetStmtInfo(compCurStmt);
fgSetStmtSeq(compCurStmt);
+ gtUpdateStmtSideEffects(compCurStmt);
}
#ifdef DEBUG
diff --git a/src/jit/loopcloning.cpp b/src/jit/loopcloning.cpp
index a1ba14292a..d4a161866f 100644
--- a/src/jit/loopcloning.cpp
+++ b/src/jit/loopcloning.cpp
@@ -43,7 +43,7 @@ GenTreePtr LC_Array::ToGenTree(Compiler* comp)
// If asked for arrlen invoke arr length operator.
if (oper == ArrLen)
{
- GenTreePtr arrLen = new (comp, GT_ARR_LENGTH) GenTreeArrLen(TYP_INT, arr, offsetof(CORINFO_Array, length));
+ GenTreePtr arrLen = comp->gtNewArrLen(TYP_INT, arr, offsetof(CORINFO_Array, length));
return arrLen;
}
else
diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp
index 7981972c33..86c1c5a348 100644
--- a/src/jit/lower.cpp
+++ b/src/jit/lower.cpp
@@ -44,7 +44,6 @@ void Lowering::MakeSrcContained(GenTreePtr parentNode, GenTreePtr childNode)
assert(!parentNode->OperIsLeaf());
assert(childNode->canBeContained());
childNode->SetContained();
- m_lsra->clearOperandCounts(childNode);
}
//------------------------------------------------------------------------
@@ -99,45 +98,6 @@ bool Lowering::IsSafeToContainMem(GenTree* parentNode, GenTree* childNode)
}
//------------------------------------------------------------------------
-// IsContainableMemoryOp: Checks whether this is a memory op that can be contained.
-//
-// Arguments:
-// node - the node of interest.
-// useTracked - true if this is being called after liveness so lvTracked is correct
-//
-// Return value:
-// True if this will definitely be a memory reference that could be contained.
-//
-// Notes:
-// This differs from the isMemoryOp() method on GenTree because it checks for
-// the case of doNotEnregister local. This won't include locals that
-// for some other reason do not become register candidates, nor those that get
-// spilled.
-// Also, if we call this before we redo liveness analysis, any new lclVars
-// introduced after the last dataflow analysis will not yet be marked lvTracked,
-// so we don't use that.
-//
-bool Lowering::IsContainableMemoryOp(GenTree* node, bool useTracked)
-{
-#ifdef _TARGET_XARCH_
- if (node->isMemoryOp())
- {
- return true;
- }
- if (node->IsLocal())
- {
- if (!m_lsra->enregisterLocalVars)
- {
- return true;
- }
- LclVarDsc* varDsc = &comp->lvaTable[node->AsLclVar()->gtLclNum];
- return (varDsc->lvDoNotEnregister || (useTracked && !varDsc->lvTracked));
- }
-#endif // _TARGET_XARCH_
- return false;
-}
-
-//------------------------------------------------------------------------
// This is the main entry point for Lowering.
GenTree* Lowering::LowerNode(GenTree* node)
@@ -147,18 +107,64 @@ GenTree* Lowering::LowerNode(GenTree* node)
{
case GT_IND:
TryCreateAddrMode(LIR::Use(BlockRange(), &node->gtOp.gtOp1, node), true);
+ ContainCheckIndir(node->AsIndir());
break;
case GT_STOREIND:
- LowerStoreInd(node);
+ TryCreateAddrMode(LIR::Use(BlockRange(), &node->gtOp.gtOp1, node), true);
+ if (!comp->codeGen->gcInfo.gcIsWriteBarrierAsgNode(node))
+ {
+ LowerStoreIndir(node->AsIndir());
+ }
break;
case GT_ADD:
- return LowerAdd(node);
+ {
+ GenTree* afterTransform = LowerAdd(node);
+ if (afterTransform != nullptr)
+ {
+ return afterTransform;
+ }
+ __fallthrough;
+ }
+
+#if !defined(_TARGET_64BIT_)
+ case GT_ADD_LO:
+ case GT_ADD_HI:
+ case GT_SUB_LO:
+ case GT_SUB_HI:
+#endif
+ case GT_SUB:
+ case GT_AND:
+ case GT_OR:
+ case GT_XOR:
+ ContainCheckBinary(node->AsOp());
+ break;
+
+#ifdef _TARGET_XARCH_
+ case GT_NEG:
+ // Codegen of this tree node sets ZF and SF flags.
+ if (!varTypeIsFloating(node))
+ {
+ node->gtFlags |= GTF_ZSF_SET;
+ }
+ break;
+#endif // _TARGET_XARCH_
+
+ case GT_MUL:
+ case GT_MULHI:
+#if defined(_TARGET_X86_) && !defined(LEGACY_BACKEND)
+ case GT_MUL_LONG:
+#endif
+ ContainCheckMul(node->AsOp());
+ break;
case GT_UDIV:
case GT_UMOD:
- return LowerUnsignedDivOrMod(node->AsOp());
+ if (!LowerUnsignedDivOrMod(node->AsOp()))
+ {
+ ContainCheckDivOrMod(node->AsOp());
+ }
break;
case GT_DIV:
@@ -178,9 +184,16 @@ GenTree* Lowering::LowerNode(GenTree* node)
case GT_GE:
case GT_EQ:
case GT_NE:
+ case GT_TEST_EQ:
+ case GT_TEST_NE:
+ case GT_CMP:
LowerCompare(node);
break;
+ case GT_JTRUE:
+ ContainCheckJTrue(node->AsOp());
+ break;
+
case GT_JMP:
LowerJmpMethod(node);
break;
@@ -189,68 +202,76 @@ GenTree* Lowering::LowerNode(GenTree* node)
LowerRet(node);
break;
+ case GT_RETURNTRAP:
+ ContainCheckReturnTrap(node->AsOp());
+ break;
+
case GT_CAST:
LowerCast(node);
break;
+#ifdef _TARGET_XARCH_
+ case GT_ARR_BOUNDS_CHECK:
+#ifdef FEATURE_SIMD
+ case GT_SIMD_CHK:
+#endif // FEATURE_SIMD
+ ContainCheckBoundsChk(node->AsBoundsChk());
+ break;
+#endif // _TARGET_XARCH_
case GT_ARR_ELEM:
return LowerArrElem(node);
+ case GT_ARR_OFFSET:
+ ContainCheckArrOffset(node->AsArrOffs());
+ break;
+
case GT_ROL:
case GT_ROR:
LowerRotate(node);
break;
-#ifdef _TARGET_XARCH_
+#ifndef _TARGET_64BIT_
+ case GT_LSH_HI:
+ case GT_RSH_LO:
+ ContainCheckShiftRotate(node->AsOp());
+ break;
+#endif // !_TARGET_64BIT_
+
case GT_LSH:
case GT_RSH:
case GT_RSZ:
+#ifdef _TARGET_XARCH_
LowerShift(node->AsOp());
- break;
+#else
+ ContainCheckShiftRotate(node->AsOp());
#endif
+ break;
case GT_STORE_BLK:
case GT_STORE_OBJ:
case GT_STORE_DYN_BLK:
{
- // TODO-Cleanup: Consider moving this code to LowerBlockStore, which is currently
- // called from TreeNodeInfoInitBlockStore, and calling that method here.
GenTreeBlk* blkNode = node->AsBlk();
TryCreateAddrMode(LIR::Use(BlockRange(), &blkNode->Addr(), blkNode), false);
+ LowerBlockStore(blkNode);
}
break;
-#ifdef FEATURE_SIMD
- case GT_SIMD:
- if (node->TypeGet() == TYP_SIMD12)
- {
- // GT_SIMD node requiring to produce TYP_SIMD12 in fact
- // produces a TYP_SIMD16 result
- node->gtType = TYP_SIMD16;
- }
+ case GT_LCLHEAP:
+ ContainCheckLclHeap(node->AsOp());
+ break;
#ifdef _TARGET_XARCH_
- if ((node->AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicGetItem) && (node->gtGetOp1()->OperGet() == GT_IND))
- {
- // If SIMD vector is already in memory, we force its
- // addr to be evaluated into a reg. This would allow
- // us to generate [regBase] or [regBase+offset] or
- // [regBase+sizeOf(SIMD vector baseType)*regIndex]
- // to access the required SIMD vector element directly
- // from memory.
- //
- // TODO-CQ-XARCH: If addr of GT_IND is GT_LEA, we
- // might be able update GT_LEA to fold the regIndex
- // or offset in some cases. Instead with this
- // approach we always evaluate GT_LEA into a reg.
- // Ideally, we should be able to lower GetItem intrinsic
- // into GT_IND(newAddr) where newAddr combines
- // the addr of SIMD vector with the given index.
- node->gtOp.gtOp1->gtFlags |= GTF_IND_REQ_ADDR_IN_REG;
- }
-#endif
+ case GT_INTRINSIC:
+ ContainCheckIntrinsic(node->AsOp());
break;
-#endif // FEATURE_SIMD
+#endif // _TARGET_XARCH_
+
+#ifdef FEATURE_SIMD
+ case GT_SIMD:
+ LowerSIMD(node->AsSIMD());
+ break;
+#endif //
case GT_LCL_VAR:
WidenSIMD12IfNecessary(node->AsLclVarCommon());
@@ -266,7 +287,6 @@ GenTree* Lowering::LowerNode(GenTree* node)
new (comp, GT_BITCAST) GenTreeOp(GT_BITCAST, store->TypeGet(), store->gtOp1, nullptr);
store->gtOp1 = bitcast;
BlockRange().InsertBefore(store, bitcast);
- break;
}
}
#endif // _TARGET_AMD64_
@@ -289,6 +309,10 @@ GenTree* Lowering::LowerNode(GenTree* node)
LowerStoreLoc(node->AsLclVarCommon());
break;
+ case GT_LOCKADD:
+ CheckImmedAndMakeContained(node, node->gtOp.gtOp2);
+ break;
+
default:
break;
}
@@ -445,7 +469,7 @@ GenTree* Lowering::LowerSwitch(GenTree* node)
unsigned blockWeight = originalSwitchBB->getBBWeight(comp);
LIR::Use use(switchBBRange, &(node->gtOp.gtOp1), node);
- use.ReplaceWithLclVar(comp, blockWeight);
+ ReplaceWithLclVar(use);
// GT_SWITCH(indexExpression) is now two statements:
// 1. a statement containing 'asg' (for temp = indexExpression)
@@ -456,7 +480,7 @@ GenTree* Lowering::LowerSwitch(GenTree* node)
assert(temp->gtOper == GT_LCL_VAR);
unsigned tempLclNum = temp->gtLclVarCommon.gtLclNum;
LclVarDsc* tempVarDsc = comp->lvaTable + tempLclNum;
- var_types tempLclType = tempVarDsc->TypeGet();
+ var_types tempLclType = temp->TypeGet();
BasicBlock* defaultBB = jumpTab[jumpCnt - 1];
BasicBlock* followingBB = originalSwitchBB->bbNext;
@@ -486,7 +510,7 @@ GenTree* Lowering::LowerSwitch(GenTree* node)
// both GT_SWITCH lowering code paths.
// This condition is of the form: if (temp > jumpTableLength - 2){ goto jumpTable[jumpTableLength - 1]; }
GenTreePtr gtDefaultCaseCond = comp->gtNewOperNode(GT_GT, TYP_INT, comp->gtNewLclvNode(tempLclNum, tempLclType),
- comp->gtNewIconNode(jumpCnt - 2, tempLclType));
+ comp->gtNewIconNode(jumpCnt - 2, genActualType(tempLclType)));
// Make sure we perform an unsigned comparison, just in case the switch index in 'temp'
// is now less than zero 0 (that would also hit the default case).
@@ -705,6 +729,7 @@ GenTree* Lowering::LowerSwitch(GenTree* node)
{
// Note that the switch value is unsigned so the cast should be unsigned as well.
switchValue = comp->gtNewCastNode(TYP_I_IMPL, switchValue, TYP_U_IMPL);
+ switchValue->gtFlags |= GTF_UNSIGNED;
}
#endif
GenTreePtr gtTableSwitch =
@@ -797,6 +822,19 @@ GenTreePtr Lowering::NewPutArg(GenTreeCall* call, GenTreePtr arg, fgArgTabEntryP
isOnStack = info->regNum == REG_STK;
#endif // !FEATURE_UNIX_AMD64_STRUCT_PASSING
+#ifdef _TARGET_ARMARCH_
+ // Mark contained when we pass struct
+ // GT_FIELD_LIST is always marked conatained when it is generated
+ if (varTypeIsStruct(type))
+ {
+ arg->SetContained();
+ if ((arg->OperGet() == GT_OBJ) && (arg->AsObj()->Addr()->OperGet() == GT_LCL_VAR_ADDR))
+ {
+ MakeSrcContained(arg, arg->AsObj()->Addr());
+ }
+ }
+#endif
+
#ifdef _TARGET_ARM_
// Struct can be split into register(s) and stack on ARM
if (info->isSplit)
@@ -811,6 +849,7 @@ GenTreePtr Lowering::NewPutArg(GenTreeCall* call, GenTreePtr arg, fgArgTabEntryP
putArg = new (comp, GT_PUTARG_SPLIT)
GenTreePutArgSplit(arg, info->slotNum PUT_STRUCT_ARG_STK_ONLY_ARG(info->numSlots), info->numRegs,
call->IsFastTailCall(), call);
+ putArg->gtRegNum = info->regNum;
// If struct argument is morphed to GT_FIELD_LIST node(s),
// we can know GC info by type of each GT_FIELD_LIST node.
@@ -850,6 +889,9 @@ GenTreePtr Lowering::NewPutArg(GenTreeCall* call, GenTreePtr arg, fgArgTabEntryP
{
var_types regType = fieldListPtr->gtGetOp1()->TypeGet();
argSplit->m_regType[index] = regType;
+
+ // Clear the register assignments on the fieldList nodes, as these are contained.
+ fieldListPtr->gtRegNum = REG_NA;
}
}
}
@@ -907,7 +949,7 @@ GenTreePtr Lowering::NewPutArg(GenTreeCall* call, GenTreePtr arg, fgArgTabEntryP
//
// clang-format on
- putArg = comp->gtNewOperNode(GT_PUTARG_REG, type, arg);
+ putArg = comp->gtNewPutArgReg(type, arg, info->regNum);
}
else if (info->structDesc.eightByteCount == 2)
{
@@ -950,14 +992,16 @@ GenTreePtr Lowering::NewPutArg(GenTreeCall* call, GenTreePtr arg, fgArgTabEntryP
for (unsigned ctr = 0; fieldListPtr != nullptr; fieldListPtr = fieldListPtr->Rest(), ctr++)
{
// Create a new GT_PUTARG_REG node with op1 the original GT_LCL_FLD.
- GenTreePtr newOper = comp->gtNewOperNode(
- GT_PUTARG_REG,
+ GenTreePtr newOper = comp->gtNewPutArgReg(
comp->GetTypeFromClassificationAndSizes(info->structDesc.eightByteClassifications[ctr],
info->structDesc.eightByteSizes[ctr]),
- fieldListPtr->gtOp.gtOp1);
+ fieldListPtr->gtOp.gtOp1, (ctr == 0) ? info->regNum : info->otherRegNum);
// Splice in the new GT_PUTARG_REG node in the GT_FIELD_LIST
ReplaceArgWithPutArgOrCopy(&fieldListPtr->gtOp.gtOp1, newOper);
+
+ // Initialize all the gtRegNum's since the list won't be traversed in an LIR traversal.
+ fieldListPtr->gtRegNum = REG_NA;
}
// Just return arg. The GT_FIELD_LIST is not replaced.
@@ -980,16 +1024,31 @@ GenTreePtr Lowering::NewPutArg(GenTreeCall* call, GenTreePtr arg, fgArgTabEntryP
GenTreeFieldList* fieldListPtr = arg->AsFieldList();
assert(fieldListPtr->IsFieldListHead());
+ // There could be up to 2-4 PUTARG_REGs in the list (3 or 4 can only occur for HFAs)
+ regNumber argReg = info->regNum;
for (unsigned ctr = 0; fieldListPtr != nullptr; fieldListPtr = fieldListPtr->Rest(), ctr++)
{
GenTreePtr curOp = fieldListPtr->gtOp.gtOp1;
var_types curTyp = curOp->TypeGet();
// Create a new GT_PUTARG_REG node with op1
- GenTreePtr newOper = comp->gtNewOperNode(GT_PUTARG_REG, curTyp, curOp);
+ GenTreePtr newOper = comp->gtNewPutArgReg(curTyp, curOp, argReg);
// Splice in the new GT_PUTARG_REG node in the GT_FIELD_LIST
ReplaceArgWithPutArgOrCopy(&fieldListPtr->gtOp.gtOp1, newOper);
+
+ // Update argReg for the next putarg_reg (if any)
+ argReg = genRegArgNext(argReg);
+
+#if defined(_TARGET_ARM_)
+ // A double register is modelled as an even-numbered single one
+ if (fieldListPtr->Current()->TypeGet() == TYP_DOUBLE)
+ {
+ argReg = genRegArgNext(argReg);
+ }
+#endif // _TARGET_ARM_
+ // Initialize all the gtRegNum's since the list won't be traversed in an LIR traversal.
+ fieldListPtr->gtRegNum = REG_NA;
}
// Just return arg. The GT_FIELD_LIST is not replaced.
@@ -1000,7 +1059,7 @@ GenTreePtr Lowering::NewPutArg(GenTreeCall* call, GenTreePtr arg, fgArgTabEntryP
#endif // FEATURE_MULTIREG_ARGS
#endif // not defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
{
- putArg = comp->gtNewPutArgReg(type, arg);
+ putArg = comp->gtNewPutArgReg(type, arg, info->regNum);
}
}
else
@@ -1194,8 +1253,8 @@ void Lowering::LowerArg(GenTreeCall* call, GenTreePtr* ppArg)
GenTreePtr argHi = arg->gtGetOp2();
GenTreeFieldList* fieldList = new (comp, GT_FIELD_LIST) GenTreeFieldList(argLo, 0, TYP_INT, nullptr);
+ // Only the first fieldList node (GTF_FIELD_LIST_HEAD) is in the instruction sequence.
(void)new (comp, GT_FIELD_LIST) GenTreeFieldList(argHi, 4, TYP_INT, fieldList);
-
putArg = NewPutArg(call, fieldList, info, TYP_VOID);
BlockRange().InsertBefore(arg, putArg);
@@ -1215,7 +1274,8 @@ void Lowering::LowerArg(GenTreeCall* call, GenTreePtr* ppArg)
GenTreeFieldList* fieldList = new (comp, GT_FIELD_LIST) GenTreeFieldList(argLo, 0, TYP_INT, nullptr);
// Only the first fieldList node (GTF_FIELD_LIST_HEAD) is in the instruction sequence.
(void)new (comp, GT_FIELD_LIST) GenTreeFieldList(argHi, 4, TYP_INT, fieldList);
- putArg = NewPutArg(call, fieldList, info, TYP_VOID);
+ putArg = NewPutArg(call, fieldList, info, TYP_VOID);
+ putArg->gtRegNum = info->regNum;
// We can't call ReplaceArgWithPutArgOrCopy here because it presumes that we are keeping the original arg.
BlockRange().InsertBefore(arg, fieldList, putArg);
@@ -1319,6 +1379,7 @@ void Lowering::LowerCall(GenTree* node)
DISPTREERANGE(BlockRange(), call);
JITDUMP("\n");
+ call->ClearOtherRegs();
LowerArgsForCall(call);
// note that everything generated from this point on runs AFTER the outgoing args are placed
@@ -1421,6 +1482,7 @@ void Lowering::LowerCall(GenTree* node)
}
}
+ ContainCheckRange(resultRange);
BlockRange().InsertBefore(insertionPoint, std::move(resultRange));
call->gtControlExpr = result;
@@ -1431,6 +1493,7 @@ void Lowering::LowerCall(GenTree* node)
CheckVSQuirkStackPaddingNeeded(call);
}
+ ContainCheckCallOperands(call);
JITDUMP("lowering call (after):\n");
DISPTREERANGE(BlockRange(), call);
JITDUMP("\n");
@@ -1818,6 +1881,7 @@ void Lowering::LowerFastTailCall(GenTreeCall* call)
GenTreeLclVar* local =
new (comp, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, tmpType, callerArgLclNum, BAD_IL_OFFSET);
GenTree* assignExpr = comp->gtNewTempAssign(tmpLclNum, local);
+ ContainCheckRange(local, assignExpr);
BlockRange().InsertBefore(firstPutArgStk, LIR::SeqTree(comp, assignExpr));
}
}
@@ -1873,7 +1937,7 @@ void Lowering::LowerFastTailCall(GenTreeCall* call)
// has already inserted tailcall helper special arguments. This function
// inserts actual data for some placeholders.
//
-// For AMD64, lower
+// For ARM32, AMD64, lower
// tail.call(void* copyRoutine, void* dummyArg, ...)
// as
// Jit_TailCall(void* copyRoutine, void* callTarget, ...)
@@ -1942,9 +2006,9 @@ GenTree* Lowering::LowerTailCallViaHelper(GenTreeCall* call, GenTree* callTarget
fgArgTabEntry* argEntry;
-#if defined(_TARGET_AMD64_)
+#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM_)
-// For AMD64, first argument is CopyRoutine and second argument is a place holder node.
+// For ARM32 and AMD64, first argument is CopyRoutine and second argument is a place holder node.
#ifdef DEBUG
argEntry = comp->gtArgEntryByArgNum(call, 0);
@@ -1960,6 +2024,7 @@ GenTree* Lowering::LowerTailCallViaHelper(GenTreeCall* call, GenTree* callTarget
assert(argEntry->node->gtOper == GT_PUTARG_REG);
GenTree* secondArg = argEntry->node->gtOp.gtOp1;
+ ContainCheckRange(callTargetRange);
BlockRange().InsertAfter(secondArg, std::move(callTargetRange));
bool isClosed;
@@ -1988,6 +2053,7 @@ GenTree* Lowering::LowerTailCallViaHelper(GenTreeCall* call, GenTree* callTarget
assert(argEntry->node->gtOper == GT_PUTARG_STK);
GenTree* arg0 = argEntry->node->gtOp.gtOp1;
+ ContainCheckRange(callTargetRange);
BlockRange().InsertAfter(arg0, std::move(callTargetRange));
bool isClosed;
@@ -2067,7 +2133,7 @@ GenTree* Lowering::LowerTailCallViaHelper(GenTreeCall* call, GenTree* callTarget
// - Decomposes long comparisons that produce a value (X86 specific).
// - Ensures that we don't have a mix of int/long operands (XARCH specific).
// - Narrow operands to enable memory operand containment (XARCH specific).
-// - Transform cmp(and(x, y), 0) into test(x, y) (XARCH specific but could
+// - Transform cmp(and(x, y), 0) into test(x, y) (XARCH/Arm64 specific but could
// be used for ARM as well if support for GT_TEST_EQ/GT_TEST_NE is added).
void Lowering::LowerCompare(GenTree* cmp)
@@ -2117,6 +2183,7 @@ void Lowering::LowerCompare(GenTree* cmp)
{
loCmp = comp->gtNewOperNode(GT_XOR, TYP_INT, loSrc1, loSrc2);
BlockRange().InsertBefore(cmp, loCmp);
+ ContainCheckBinary(loCmp->AsOp());
}
if (hiSrc1->OperIs(GT_CNS_INT))
@@ -2133,10 +2200,12 @@ void Lowering::LowerCompare(GenTree* cmp)
{
hiCmp = comp->gtNewOperNode(GT_XOR, TYP_INT, hiSrc1, hiSrc2);
BlockRange().InsertBefore(cmp, hiCmp);
+ ContainCheckBinary(hiCmp->AsOp());
}
hiCmp = comp->gtNewOperNode(GT_OR, TYP_INT, loCmp, hiCmp);
BlockRange().InsertBefore(cmp, hiCmp);
+ ContainCheckBinary(hiCmp->AsOp());
}
else
{
@@ -2221,12 +2290,15 @@ void Lowering::LowerCompare(GenTree* cmp)
hiCmp = comp->gtNewOperNode(GT_CMP, TYP_VOID, hiSrc1, hiSrc2);
BlockRange().InsertBefore(cmp, hiCmp);
+ ContainCheckCompare(hiCmp->AsOp());
}
else
{
loCmp = comp->gtNewOperNode(GT_CMP, TYP_VOID, loSrc1, loSrc2);
hiCmp = comp->gtNewOperNode(GT_SUB_HI, TYP_INT, hiSrc1, hiSrc2);
BlockRange().InsertBefore(cmp, loCmp, hiCmp);
+ ContainCheckCompare(loCmp->AsOp());
+ ContainCheckBinary(hiCmp->AsOp());
//
// Try to move the first SUB_HI operands right in front of it, this allows using
@@ -2258,7 +2330,7 @@ void Lowering::LowerCompare(GenTree* cmp)
GenTree* jcc = cmpUse.User();
jcc->gtOp.gtOp1 = nullptr;
jcc->ChangeOper(GT_JCC);
- jcc->gtFlags |= (cmp->gtFlags & GTF_UNSIGNED);
+ jcc->gtFlags |= (cmp->gtFlags & GTF_UNSIGNED) | GTF_USE_FLAGS;
jcc->AsCC()->gtCondition = condition;
}
else
@@ -2266,6 +2338,7 @@ void Lowering::LowerCompare(GenTree* cmp)
cmp->gtOp.gtOp1 = nullptr;
cmp->gtOp.gtOp2 = nullptr;
cmp->ChangeOper(GT_SETCC);
+ cmp->gtFlags |= GTF_USE_FLAGS;
cmp->AsCC()->gtCondition = condition;
}
@@ -2273,7 +2346,6 @@ void Lowering::LowerCompare(GenTree* cmp)
}
#endif
-#ifdef _TARGET_XARCH_
#ifdef _TARGET_AMD64_
if (cmp->gtGetOp1()->TypeGet() != cmp->gtGetOp2()->TypeGet())
{
@@ -2312,11 +2384,13 @@ void Lowering::LowerCompare(GenTree* cmp)
GenTree* cast = comp->gtNewCastNode(TYP_LONG, *smallerOpUse, TYP_LONG);
*smallerOpUse = cast;
BlockRange().InsertAfter(cast->gtGetOp1(), cast);
+ ContainCheckCast(cast->AsCast());
}
}
}
#endif // _TARGET_AMD64_
+#if defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)
if (cmp->gtGetOp2()->IsIntegralConst())
{
GenTree* op1 = cmp->gtGetOp1();
@@ -2324,7 +2398,8 @@ void Lowering::LowerCompare(GenTree* cmp)
GenTreeIntCon* op2 = cmp->gtGetOp2()->AsIntCon();
ssize_t op2Value = op2->IconValue();
- if (IsContainableMemoryOp(op1, false) && varTypeIsSmall(op1Type) && genTypeCanRepresentValue(op1Type, op2Value))
+#ifdef _TARGET_XARCH_
+ if (IsContainableMemoryOp(op1) && varTypeIsSmall(op1Type) && genTypeCanRepresentValue(op1Type, op2Value))
{
//
// If op1's type is small then try to narrow op2 so it has the same type as op1.
@@ -2354,12 +2429,25 @@ void Lowering::LowerCompare(GenTree* cmp)
// the result of bool returning calls.
//
- if (castOp->OperIs(GT_CALL, GT_LCL_VAR) || castOp->OperIsLogical() ||
- IsContainableMemoryOp(castOp, false))
+ if (castOp->OperIs(GT_CALL, GT_LCL_VAR) || castOp->OperIsLogical() || IsContainableMemoryOp(castOp))
{
assert(!castOp->gtOverflowEx()); // Must not be an overflow checking operation
- castOp->gtType = castToType;
+ castOp->gtType = castToType;
+ // If we have any contained memory ops on castOp, they must now not be contained.
+ if (castOp->OperIsLogical())
+ {
+ GenTree* op1 = castOp->gtGetOp1();
+ if ((op1 != nullptr) && !op1->IsCnsIntOrI())
+ {
+ op1->ClearContained();
+ }
+ GenTree* op2 = castOp->gtGetOp2();
+ if ((op2 != nullptr) && !op2->IsCnsIntOrI())
+ {
+ op2->ClearContained();
+ }
+ }
cmp->gtOp.gtOp1 = castOp;
op2->gtType = castToType;
@@ -2367,7 +2455,9 @@ void Lowering::LowerCompare(GenTree* cmp)
}
}
}
- else if (op1->OperIs(GT_AND) && cmp->OperIs(GT_EQ, GT_NE))
+ else
+#endif
+ if (op1->OperIs(GT_AND) && cmp->OperIs(GT_EQ, GT_NE))
{
//
// Transform ((x AND y) EQ|NE 0) into (x TEST_EQ|TEST_NE y) when possible.
@@ -2399,8 +2489,12 @@ void Lowering::LowerCompare(GenTree* cmp)
cmp->SetOperRaw(cmp->OperIs(GT_EQ) ? GT_TEST_EQ : GT_TEST_NE);
cmp->gtOp.gtOp1 = andOp1;
cmp->gtOp.gtOp2 = andOp2;
+ // We will re-evaluate containment below
+ andOp1->ClearContained();
+ andOp2->ClearContained();
- if (IsContainableMemoryOp(andOp1, false) && andOp2->IsIntegralConst())
+#ifdef _TARGET_XARCH_
+ if (IsContainableMemoryOp(andOp1) && andOp2->IsIntegralConst())
{
//
// For "test" we only care about the bits that are set in the second operand (mask).
@@ -2431,10 +2525,13 @@ void Lowering::LowerCompare(GenTree* cmp)
andOp2->gtType = TYP_CHAR;
}
}
+#endif
}
}
}
+#endif // defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)
+#ifdef _TARGET_XARCH_
if (cmp->gtGetOp1()->TypeGet() == cmp->gtGetOp2()->TypeGet())
{
if (varTypeIsSmall(cmp->gtGetOp1()->TypeGet()) && varTypeIsUnsigned(cmp->gtGetOp1()->TypeGet()))
@@ -2451,6 +2548,7 @@ void Lowering::LowerCompare(GenTree* cmp)
}
}
#endif // _TARGET_XARCH_
+ ContainCheckCompare(cmp->AsOp());
}
// Lower "jmp <method>" tail call to insert PInvoke method epilog if required.
@@ -2494,6 +2592,7 @@ void Lowering::LowerRet(GenTree* ret)
{
InsertPInvokeMethodEpilog(comp->compCurBB DEBUGARG(ret));
}
+ ContainCheckRet(ret->AsOp());
}
GenTree* Lowering::LowerDirectCall(GenTreeCall* call)
@@ -2649,6 +2748,7 @@ GenTree* Lowering::LowerDelegateInvoke(GenTreeCall* call)
assert(thisArgNode->gtOper == GT_PUTARG_REG);
GenTree* originalThisExpr = thisArgNode->gtOp.gtOp1;
+ GenTree* thisExpr = originalThisExpr;
// We're going to use the 'this' expression multiple times, so make a local to copy it.
@@ -2671,21 +2771,21 @@ GenTree* Lowering::LowerDelegateInvoke(GenTreeCall* call)
unsigned delegateInvokeTmp = comp->lvaGrabTemp(true DEBUGARG("delegate invoke call"));
LIR::Use thisExprUse(BlockRange(), &thisArgNode->gtOp.gtOp1, thisArgNode);
- thisExprUse.ReplaceWithLclVar(comp, m_block->getBBWeight(comp), delegateInvokeTmp);
+ ReplaceWithLclVar(thisExprUse, delegateInvokeTmp);
- originalThisExpr = thisExprUse.Def(); // it's changed; reload it.
- lclNum = delegateInvokeTmp;
+ thisExpr = thisExprUse.Def(); // it's changed; reload it.
+ lclNum = delegateInvokeTmp;
}
// replace original expression feeding into thisPtr with
// [originalThis + offsetOfDelegateInstance]
GenTree* newThisAddr = new (comp, GT_LEA)
- GenTreeAddrMode(TYP_REF, originalThisExpr, nullptr, 0, comp->eeGetEEInfo()->offsetOfDelegateInstance);
+ GenTreeAddrMode(TYP_BYREF, thisExpr, nullptr, 0, comp->eeGetEEInfo()->offsetOfDelegateInstance);
GenTree* newThis = comp->gtNewOperNode(GT_IND, TYP_REF, newThisAddr);
- BlockRange().InsertAfter(originalThisExpr, newThisAddr, newThis);
+ BlockRange().InsertAfter(thisExpr, newThisAddr, newThis);
thisArgNode->gtOp.gtOp1 = newThis;
@@ -2780,11 +2880,9 @@ GenTree* Lowering::SetGCState(int state)
GenTree* base = new (comp, GT_LCL_VAR) GenTreeLclVar(TYP_I_IMPL, comp->info.compLvFrameListRoot, -1);
- GenTree* storeGcState = new (comp, GT_STOREIND)
- GenTreeStoreInd(TYP_BYTE,
- new (comp, GT_LEA) GenTreeAddrMode(TYP_I_IMPL, base, nullptr, 1, pInfo->offsetOfGCState),
- new (comp, GT_CNS_INT) GenTreeIntCon(TYP_BYTE, state));
-
+ GenTree* stateNode = new (comp, GT_CNS_INT) GenTreeIntCon(TYP_BYTE, state);
+ GenTree* addr = new (comp, GT_LEA) GenTreeAddrMode(TYP_I_IMPL, base, nullptr, 1, pInfo->offsetOfGCState);
+ GenTree* storeGcState = new (comp, GT_STOREIND) GenTreeStoreInd(TYP_BYTE, addr, stateNode);
return storeGcState;
}
@@ -2905,7 +3003,7 @@ void Lowering::InsertPInvokeMethodProlog()
GenTreeArgList* argList = comp->gtNewArgList(frameAddr, PhysReg(REG_SECRET_STUB_PARAM));
#endif
- GenTree* call = comp->gtNewHelperCallNode(CORINFO_HELP_INIT_PINVOKE_FRAME, TYP_I_IMPL, 0, argList);
+ GenTree* call = comp->gtNewHelperCallNode(CORINFO_HELP_INIT_PINVOKE_FRAME, TYP_I_IMPL, argList);
// some sanity checks on the frame list root vardsc
LclVarDsc* varDsc = &comp->lvaTable[comp->info.compLvFrameListRoot];
@@ -2934,6 +3032,7 @@ void Lowering::InsertPInvokeMethodProlog()
GenTreeLclFld* storeSP = new (comp, GT_STORE_LCL_FLD)
GenTreeLclFld(GT_STORE_LCL_FLD, TYP_I_IMPL, comp->lvaInlinedPInvokeFrameVar, callFrameInfo.offsetOfCallSiteSP);
storeSP->gtOp1 = PhysReg(REG_SPBASE);
+ storeSP->gtFlags |= GTF_VAR_DEF;
firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, storeSP));
DISPTREERANGE(firstBlockRange, storeSP);
@@ -2950,6 +3049,7 @@ void Lowering::InsertPInvokeMethodProlog()
new (comp, GT_STORE_LCL_FLD) GenTreeLclFld(GT_STORE_LCL_FLD, TYP_I_IMPL, comp->lvaInlinedPInvokeFrameVar,
callFrameInfo.offsetOfCalleeSavedFP);
storeFP->gtOp1 = PhysReg(REG_FPBASE);
+ storeFP->gtFlags |= GTF_VAR_DEF;
firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, storeFP));
DISPTREERANGE(firstBlockRange, storeFP);
@@ -2967,6 +3067,7 @@ void Lowering::InsertPInvokeMethodProlog()
// The init routine sets InlinedCallFrame's m_pNext, so we just set the thead's top-of-stack
GenTree* frameUpd = CreateFrameLinkUpdate(PushFrame);
firstBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, frameUpd));
+ ContainCheckStoreIndir(frameUpd->AsIndir());
DISPTREERANGE(firstBlockRange, frameUpd);
}
#endif // _TARGET_64BIT_
@@ -3031,6 +3132,7 @@ void Lowering::InsertPInvokeMethodEpilog(BasicBlock* returnBB DEBUGARG(GenTreePt
// That is [tcb + offsetOfGcState] = 1
GenTree* storeGCState = SetGCState(1);
returnBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, storeGCState));
+ ContainCheckStoreIndir(storeGCState->AsIndir());
// Pop the frame if necessary. This always happens in the epilog on 32-bit targets. For 64-bit targets, we only do
// this in the epilog for IL stubs; for non-IL stubs the frame is popped after every PInvoke call.
@@ -3042,6 +3144,7 @@ void Lowering::InsertPInvokeMethodEpilog(BasicBlock* returnBB DEBUGARG(GenTreePt
{
GenTree* frameUpd = CreateFrameLinkUpdate(PopFrame);
returnBlockRange.InsertBefore(insertionPoint, LIR::SeqTree(comp, frameUpd));
+ ContainCheckStoreIndir(frameUpd->AsIndir());
}
}
@@ -3081,7 +3184,7 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call)
// Insert call to CORINFO_HELP_JIT_PINVOKE_BEGIN
GenTree* helperCall =
- comp->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_BEGIN, TYP_VOID, 0, comp->gtNewArgList(frameAddr));
+ comp->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_BEGIN, TYP_VOID, comp->gtNewArgList(frameAddr));
comp->fgMorphTree(helperCall);
BlockRange().InsertBefore(insertBefore, LIR::SeqTree(comp, helperCall));
@@ -3148,8 +3251,9 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call)
new (comp, GT_STORE_LCL_FLD) GenTreeLclFld(GT_STORE_LCL_FLD, TYP_I_IMPL, comp->lvaInlinedPInvokeFrameVar,
callFrameInfo.offsetOfCallTarget);
store->gtOp1 = src;
+ store->gtFlags |= GTF_VAR_DEF;
- BlockRange().InsertBefore(insertBefore, LIR::SeqTree(comp, store));
+ InsertTreeBeforeAndContainCheck(insertBefore, store);
}
#ifdef _TARGET_X86_
@@ -3161,8 +3265,9 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call)
GenTreeLclFld(GT_STORE_LCL_FLD, TYP_I_IMPL, comp->lvaInlinedPInvokeFrameVar, callFrameInfo.offsetOfCallSiteSP);
storeCallSiteSP->gtOp1 = PhysReg(REG_SPBASE);
+ storeCallSiteSP->gtFlags |= GTF_VAR_DEF;
- BlockRange().InsertBefore(insertBefore, LIR::SeqTree(comp, storeCallSiteSP));
+ InsertTreeBeforeAndContainCheck(insertBefore, storeCallSiteSP);
#endif
@@ -3178,8 +3283,9 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call)
GenTreeLabel* labelRef = new (comp, GT_LABEL) GenTreeLabel(nullptr);
labelRef->gtType = TYP_I_IMPL;
storeLab->gtOp1 = labelRef;
+ storeLab->gtFlags |= GTF_VAR_DEF;
- BlockRange().InsertBefore(insertBefore, LIR::SeqTree(comp, storeLab));
+ InsertTreeBeforeAndContainCheck(insertBefore, storeLab);
// Push the PInvoke frame if necessary. On 32-bit targets this only happens in the method prolog if a method
// contains PInvokes; on 64-bit targets this is necessary in non-stubs.
@@ -3195,6 +3301,7 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call)
// Stubs do this once per stub, not once per call.
GenTree* frameUpd = CreateFrameLinkUpdate(PushFrame);
BlockRange().InsertBefore(insertBefore, LIR::SeqTree(comp, frameUpd));
+ ContainCheckStoreIndir(frameUpd->AsIndir());
}
#endif // _TARGET_64BIT_
@@ -3205,6 +3312,7 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call)
GenTree* storeGCState = SetGCState(0);
BlockRange().InsertBefore(insertBefore, LIR::SeqTree(comp, storeGCState));
+ ContainCheckStoreIndir(storeGCState->AsIndir());
}
//------------------------------------------------------------------------
@@ -3230,11 +3338,12 @@ void Lowering::InsertPInvokeCallEpilog(GenTreeCall* call)
frameAddr->SetOperRaw(GT_LCL_VAR_ADDR);
// Insert call to CORINFO_HELP_JIT_PINVOKE_END
- GenTree* helperCall =
- comp->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_END, TYP_VOID, 0, comp->gtNewArgList(frameAddr));
+ GenTreeCall* helperCall =
+ comp->gtNewHelperCallNode(CORINFO_HELP_JIT_PINVOKE_END, TYP_VOID, comp->gtNewArgList(frameAddr));
comp->fgMorphTree(helperCall);
BlockRange().InsertAfter(call, LIR::SeqTree(comp, helperCall));
+ ContainCheckCallOperands(helperCall);
return;
}
@@ -3243,9 +3352,11 @@ void Lowering::InsertPInvokeCallEpilog(GenTreeCall* call)
GenTree* tree = SetGCState(1);
BlockRange().InsertBefore(insertionPoint, LIR::SeqTree(comp, tree));
+ ContainCheckStoreIndir(tree->AsIndir());
tree = CreateReturnTrapSeq();
BlockRange().InsertBefore(insertionPoint, LIR::SeqTree(comp, tree));
+ ContainCheckReturnTrap(tree->AsOp());
// Pop the frame if necessary. On 32-bit targets this only happens in the method epilog; on 64-bit targets thi
// happens after every PInvoke call in non-stubs. 32-bit targets instead mark the frame as inactive.
@@ -3256,6 +3367,7 @@ void Lowering::InsertPInvokeCallEpilog(GenTreeCall* call)
{
tree = CreateFrameLinkUpdate(PopFrame);
BlockRange().InsertBefore(insertionPoint, LIR::SeqTree(comp, tree));
+ ContainCheckStoreIndir(tree->AsIndir());
}
#else
const CORINFO_EE_INFO::InlinedCallFrameInfo& callFrameInfo = comp->eeGetEEInfo()->inlinedCallFrameInfo;
@@ -3270,8 +3382,10 @@ void Lowering::InsertPInvokeCallEpilog(GenTreeCall* call)
GenTreeIntCon* const constantZero = new (comp, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, 0);
storeCallSiteTracker->gtOp1 = constantZero;
+ storeCallSiteTracker->gtFlags |= GTF_VAR_DEF;
BlockRange().InsertBefore(insertionPoint, constantZero, storeCallSiteTracker);
+ ContainCheckStoreLoc(storeCallSiteTracker);
#endif // _TARGET_64BIT_
}
@@ -3439,7 +3553,7 @@ GenTree* Lowering::LowerVirtualVtableCall(GenTreeCall* call)
}
LIR::Use thisPtrUse(BlockRange(), &(argEntry->node->gtOp.gtOp1), argEntry->node);
- thisPtrUse.ReplaceWithLclVar(comp, m_block->getBBWeight(comp), vtableCallTemp);
+ ReplaceWithLclVar(thisPtrUse, vtableCallTemp);
lclNum = vtableCallTemp;
}
@@ -3447,6 +3561,13 @@ GenTree* Lowering::LowerVirtualVtableCall(GenTreeCall* call)
// We'll introduce another use of this local so increase its ref count.
comp->lvaTable[lclNum].incRefCnts(comp->compCurBB->getBBWeight(comp), comp);
+ // Get hold of the vtable offset (note: this might be expensive)
+ unsigned vtabOffsOfIndirection;
+ unsigned vtabOffsAfterIndirection;
+ bool isRelative;
+ comp->info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection,
+ &vtabOffsAfterIndirection, &isRelative);
+
// If the thisPtr is a local field, then construct a local field type node
GenTree* local;
if (thisPtr->isLclField())
@@ -3462,22 +3583,58 @@ GenTree* Lowering::LowerVirtualVtableCall(GenTreeCall* call)
// pointer to virtual table = [REG_CALL_THIS + offs]
GenTree* result = Ind(Offset(local, VPTR_OFFS));
- // Get hold of the vtable offset (note: this might be expensive)
- unsigned vtabOffsOfIndirection;
- unsigned vtabOffsAfterIndirection;
- comp->info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection,
- &vtabOffsAfterIndirection);
-
// Get the appropriate vtable chunk
if (vtabOffsOfIndirection != CORINFO_VIRTUALCALL_NO_CHUNK)
{
- // result = [REG_CALL_IND_SCRATCH + vtabOffsOfIndirection]
- result = Ind(Offset(result, vtabOffsOfIndirection));
+ if (isRelative)
+ {
+ // MethodTable offset is a relative pointer.
+ //
+ // Additional temporary variable is used to store virtual table pointer.
+ // Address of method is obtained by the next computations:
+ //
+ // Save relative offset to tmp (vtab is virtual table pointer, vtabOffsOfIndirection is offset of
+ // vtable-1st-level-indirection):
+ // tmp = [vtab + vtabOffsOfIndirection]
+ //
+ // Save address of method to result (vtabOffsAfterIndirection is offset of vtable-2nd-level-indirection):
+ // result = [vtab + vtabOffsOfIndirection + vtabOffsAfterIndirection + tmp]
+ unsigned lclNumTmp = comp->lvaGrabTemp(true DEBUGARG("lclNumTmp"));
+
+ comp->lvaTable[lclNumTmp].incRefCnts(comp->compCurBB->getBBWeight(comp), comp);
+ GenTree* lclvNodeStore = comp->gtNewTempAssign(lclNumTmp, result);
+
+ LIR::Range range = LIR::SeqTree(comp, lclvNodeStore);
+ JITDUMP("result of obtaining pointer to virtual table:\n");
+ DISPRANGE(range);
+ BlockRange().InsertBefore(call, std::move(range));
+
+ GenTree* tmpTree = comp->gtNewLclvNode(lclNumTmp, result->TypeGet());
+ tmpTree = Offset(tmpTree, vtabOffsOfIndirection);
+
+ tmpTree = comp->gtNewOperNode(GT_IND, TYP_I_IMPL, tmpTree, false);
+ GenTree* offs = comp->gtNewIconNode(vtabOffsOfIndirection + vtabOffsAfterIndirection, TYP_INT);
+ result = comp->gtNewOperNode(GT_ADD, TYP_I_IMPL, comp->gtNewLclvNode(lclNumTmp, result->TypeGet()), offs);
+
+ result = Ind(OffsetByIndex(result, tmpTree));
+ }
+ else
+ {
+ // result = [REG_CALL_IND_SCRATCH + vtabOffsOfIndirection]
+ result = Ind(Offset(result, vtabOffsOfIndirection));
+ }
+ }
+ else
+ {
+ assert(!isRelative);
}
// Load the function address
// result = [reg+vtabOffs]
- result = Ind(Offset(result, vtabOffsAfterIndirection));
+ if (!isRelative)
+ {
+ result = Ind(Offset(result, vtabOffsAfterIndirection));
+ }
return result;
}
@@ -3540,6 +3697,7 @@ GenTree* Lowering::LowerVirtualStubCall(GenTreeCall* call)
ind->gtFlags |= GTF_IND_REQ_ADDR_IN_REG;
BlockRange().InsertAfter(call->gtCallAddr, ind);
+ ContainCheckIndir(ind->AsIndir());
call->gtCallAddr = ind;
}
else
@@ -3803,6 +3961,15 @@ GenTree* Lowering::TryCreateAddrMode(LIR::Use&& use, bool isIndir)
GenTreeAddrMode* addrMode = new (comp, GT_LEA) GenTreeAddrMode(addrModeType, base, index, scale, offset);
+ // Neither the base nor the index should now be contained.
+ if (base != nullptr)
+ {
+ base->ClearContained();
+ }
+ if (index != nullptr)
+ {
+ index->ClearContained();
+ }
addrMode->gtRsvdRegs = addr->gtRsvdRegs;
addrMode->gtFlags |= (addr->gtFlags & GTF_IND_FLAGS);
addrMode->gtFlags &= ~GTF_ALL_EFFECT; // LEAs are side-effect-free.
@@ -3829,44 +3996,34 @@ GenTree* Lowering::TryCreateAddrMode(LIR::Use&& use, bool isIndir)
// node - the node we care about
//
// Returns:
-// The next node to lower.
+// The next node to lower if we have transformed the ADD; nullptr otherwise.
//
GenTree* Lowering::LowerAdd(GenTree* node)
{
GenTree* next = node->gtNext;
-#ifdef _TARGET_ARMARCH_
- // For ARM architectures we don't have the LEA instruction
- // therefore we won't get much benefit from doing this.
- return next;
-#else // _TARGET_ARMARCH_
- if (!varTypeIsIntegralOrI(node))
- {
- return next;
- }
-
- LIR::Use use;
- if (!BlockRange().TryGetUse(node, &use))
+#ifndef _TARGET_ARMARCH_
+ if (varTypeIsIntegralOrI(node))
{
- return next;
- }
-
- // if this is a child of an indir, let the parent handle it.
- GenTree* parent = use.User();
- if (parent->OperIsIndir())
- {
- return next;
- }
-
- // if there is a chain of adds, only look at the topmost one
- if (parent->gtOper == GT_ADD)
- {
- return next;
+ LIR::Use use;
+ if (BlockRange().TryGetUse(node, &use))
+ {
+ // If this is a child of an indir, let the parent handle it.
+ // If there is a chain of adds, only look at the topmost one.
+ GenTree* parent = use.User();
+ if (!parent->OperIsIndir() && (parent->gtOper != GT_ADD))
+ {
+ GenTree* addr = TryCreateAddrMode(std::move(use), false);
+ if (addr != node)
+ {
+ return addr->gtNext;
+ }
+ }
+ }
}
-
- GenTree* addr = TryCreateAddrMode(std::move(use), false);
- return addr->gtNext;
#endif // !_TARGET_ARMARCH_
+
+ return nullptr;
}
//------------------------------------------------------------------------
@@ -3875,12 +4032,16 @@ GenTree* Lowering::LowerAdd(GenTree* node)
// Arguments:
// divMod - pointer to the GT_UDIV/GT_UMOD node to be lowered
//
+// Return Value:
+// Returns a boolean indicating whether the node was transformed.
+//
// Notes:
// - Transform UDIV/UMOD by power of 2 into RSZ/AND
// - Transform UDIV by constant >= 2^(N-1) into GE
// - Transform UDIV/UMOD by constant >= 3 into "magic division"
+//
-GenTree* Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
+bool Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
{
assert(divMod->OperIs(GT_UDIV, GT_UMOD));
@@ -3891,13 +4052,13 @@ GenTree* Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
#if !defined(_TARGET_64BIT_)
if (dividend->OperIs(GT_LONG))
{
- return next;
+ return false;
}
#endif
if (!divisor->IsCnsIntOrI())
{
- return next;
+ return false;
}
if (dividend->IsCnsIntOrI())
@@ -3905,7 +4066,7 @@ GenTree* Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
// We shouldn't see a divmod with constant operands here but if we do then it's likely
// because optimizations are disabled or it's a case that's supposed to throw an exception.
// Don't optimize this.
- return next;
+ return false;
}
const var_types type = divMod->TypeGet();
@@ -3922,7 +4083,7 @@ GenTree* Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
if (divisorValue == 0)
{
- return next;
+ return false;
}
const bool isDiv = divMod->OperIs(GT_UDIV);
@@ -3943,11 +4104,10 @@ GenTree* Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
}
divMod->SetOper(newOper);
- divisor->AsIntCon()->SetIconValue(divisorValue);
-
- return next;
+ divisor->gtIntCon.SetIconValue(divisorValue);
+ ContainCheckNode(divMod);
+ return true;
}
-
if (isDiv)
{
// If the divisor is greater or equal than 2^(N - 1) then the result is 1
@@ -3957,12 +4117,13 @@ GenTree* Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
{
divMod->SetOper(GT_GE);
divMod->gtFlags |= GTF_UNSIGNED;
- return next;
+ ContainCheckNode(divMod);
+ return true;
}
}
-// TODO-ARM-CQ: Currently there's no GT_MULHI for ARM32/64
-#ifdef _TARGET_XARCH_
+// TODO-ARM-CQ: Currently there's no GT_MULHI for ARM32
+#if defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)
if (!comp->opts.MinOpts() && (divisorValue >= 3))
{
size_t magic;
@@ -3996,7 +4157,7 @@ GenTree* Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
if (requiresDividendMultiuse)
{
LIR::Use dividendUse(BlockRange(), &divMod->gtOp1, divMod);
- dividendLclNum = dividendUse.ReplaceWithLclVar(comp, curBBWeight);
+ dividendLclNum = ReplaceWithLclVar(dividendUse);
dividend = divMod->gtGetOp1();
}
@@ -4008,6 +4169,7 @@ GenTree* Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
mulhi->gtFlags |= GTF_UNSIGNED;
divisor->AsIntCon()->SetIconValue(magic);
BlockRange().InsertBefore(divMod, mulhi);
+ GenTree* firstNode = mulhi;
if (requiresAdjustment)
{
@@ -4021,7 +4183,7 @@ GenTree* Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
BlockRange().InsertBefore(divMod, one, rsz);
LIR::Use mulhiUse(BlockRange(), &sub->gtOp.gtOp2, sub);
- unsigned mulhiLclNum = mulhiUse.ReplaceWithLclVar(comp, curBBWeight);
+ unsigned mulhiLclNum = ReplaceWithLclVar(mulhiUse);
GenTree* mulhiCopy = comp->gtNewLclvNode(mulhiLclNum, type);
GenTree* add = comp->gtNewOperNode(GT_ADD, type, rsz, mulhiCopy);
@@ -4057,31 +4219,30 @@ GenTree* Lowering::LowerUnsignedDivOrMod(GenTreeOp* divMod)
BlockRange().InsertBefore(divMod, div, divisor, mul, dividend);
comp->lvaTable[dividendLclNum].incRefCnts(curBBWeight, comp);
}
+ ContainCheckRange(firstNode, divMod);
- return mulhi;
+ return true;
}
#endif
-
- return next;
+ return false;
}
-//------------------------------------------------------------------------
-// LowerSignedDivOrMod: transform integer GT_DIV/GT_MOD nodes with a power of 2
-// const divisor into equivalent but faster sequences.
+// LowerConstIntDivOrMod: Transform integer GT_DIV/GT_MOD nodes with a power of 2
+// const divisor into equivalent but faster sequences.
//
// Arguments:
-// node - pointer to node we care about
+// node - pointer to the DIV or MOD node
//
// Returns:
// The next node to lower.
//
-GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
+GenTree* Lowering::LowerConstIntDivOrMod(GenTree* node)
{
assert((node->OperGet() == GT_DIV) || (node->OperGet() == GT_MOD));
-
- GenTree* next = node->gtNext;
- GenTree* divMod = node;
- GenTree* divisor = divMod->gtGetOp2();
+ GenTree* next = node->gtNext;
+ GenTree* divMod = node;
+ GenTree* dividend = divMod->gtGetOp1();
+ GenTree* divisor = divMod->gtGetOp2();
if (!divisor->IsCnsIntOrI())
{
@@ -4091,8 +4252,6 @@ GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
const var_types type = divMod->TypeGet();
assert((type == TYP_INT) || (type == TYP_LONG));
- GenTree* dividend = divMod->gtGetOp1();
-
if (dividend->IsCnsIntOrI())
{
// We shouldn't see a divmod with constant operands here but if we do then it's likely
@@ -4126,6 +4285,7 @@ GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
// If the divisor is the minimum representable integer value then we can use a compare,
// the result is 1 iff the dividend equals divisor.
divMod->SetOper(GT_EQ);
+ ContainCheckCompare(divMod->AsOp());
return next;
}
}
@@ -4140,7 +4300,7 @@ GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
return next;
}
-#ifdef _TARGET_XARCH_
+#if defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)
ssize_t magic;
int shift;
@@ -4187,7 +4347,7 @@ GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
if (requiresDividendMultiuse)
{
LIR::Use dividendUse(BlockRange(), &mulhi->gtOp.gtOp2, mulhi);
- dividendLclNum = dividendUse.ReplaceWithLclVar(comp, curBBWeight);
+ dividendLclNum = ReplaceWithLclVar(dividendUse);
}
GenTree* adjusted;
@@ -4210,7 +4370,7 @@ GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
BlockRange().InsertBefore(divMod, shiftBy, signBit);
LIR::Use adjustedUse(BlockRange(), &signBit->gtOp.gtOp1, signBit);
- unsigned adjustedLclNum = adjustedUse.ReplaceWithLclVar(comp, curBBWeight);
+ unsigned adjustedLclNum = ReplaceWithLclVar(adjustedUse);
adjusted = comp->gtNewLclvNode(adjustedLclNum, type);
comp->lvaTable[adjustedLclNum].incRefCnts(curBBWeight, comp);
BlockRange().InsertBefore(divMod, adjusted);
@@ -4247,7 +4407,7 @@ GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
return mulhi;
#else
- // Currently there's no GT_MULHI for ARM32/64
+ // Currently there's no GT_MULHI for ARM32
return next;
#endif
}
@@ -4265,7 +4425,7 @@ GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
unsigned curBBWeight = comp->compCurBB->getBBWeight(comp);
LIR::Use opDividend(BlockRange(), &divMod->gtOp.gtOp1, divMod);
- opDividend.ReplaceWithLclVar(comp, curBBWeight);
+ ReplaceWithLclVar(opDividend);
dividend = divMod->gtGetOp1();
assert(dividend->OperGet() == GT_LCL_VAR);
@@ -4298,11 +4458,13 @@ GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
divisor->gtIntCon.SetIconValue(genLog2(absDivisorValue));
newDivMod = comp->gtNewOperNode(GT_RSH, type, adjustedDividend, divisor);
+ ContainCheckShiftRotate(newDivMod->AsOp());
if (divisorValue < 0)
{
// negate the result if the divisor is negative
newDivMod = comp->gtNewOperNode(GT_NEG, type, newDivMod);
+ ContainCheckNode(newDivMod);
}
}
else
@@ -4314,6 +4476,7 @@ GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
newDivMod = comp->gtNewOperNode(GT_SUB, type, comp->gtNewLclvNode(dividendLclNum, type),
comp->gtNewOperNode(GT_AND, type, adjustedDividend, divisor));
+ ContainCheckBinary(newDivMod->AsOp());
comp->lvaTable[dividendLclNum].incRefCnts(curBBWeight, comp);
}
@@ -4324,7 +4487,7 @@ GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
BlockRange().Remove(dividend);
// linearize and insert the new tree before the original divMod node
- BlockRange().InsertBefore(divMod, LIR::SeqTree(comp, newDivMod));
+ InsertTreeBeforeAndContainCheck(divMod, newDivMod);
BlockRange().Remove(divMod);
// replace the original divmod node with the new divmod tree
@@ -4332,24 +4495,37 @@ GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
return newDivMod->gtNext;
}
-
//------------------------------------------------------------------------
-// LowerStoreInd: attempt to transform an indirect store to use an
-// addressing mode
+// LowerSignedDivOrMod: transform integer GT_DIV/GT_MOD nodes with a power of 2
+// const divisor into equivalent but faster sequences.
//
// Arguments:
-// node - the node we care about
+// node - the DIV or MOD node
//
-void Lowering::LowerStoreInd(GenTree* node)
+// Returns:
+// The next node to lower.
+//
+GenTree* Lowering::LowerSignedDivOrMod(GenTreePtr node)
{
- assert(node != nullptr);
- assert(node->OperGet() == GT_STOREIND);
+ assert((node->OperGet() == GT_DIV) || (node->OperGet() == GT_MOD));
+ GenTree* next = node->gtNext;
+ GenTree* divMod = node;
+ GenTree* dividend = divMod->gtGetOp1();
+ GenTree* divisor = divMod->gtGetOp2();
- TryCreateAddrMode(LIR::Use(BlockRange(), &node->gtOp.gtOp1, node), true);
+#ifdef _TARGET_XARCH_
+ if (!varTypeIsFloating(node->TypeGet()))
+#endif // _TARGET_XARCH_
+ {
+ next = LowerConstIntDivOrMod(node);
+ }
- // Mark all GT_STOREIND nodes to indicate that it is not known
- // whether it represents a RMW memory op.
- node->AsStoreInd()->SetRMWStatusDefault();
+ if ((node->OperGet() == GT_DIV) || (node->OperGet() == GT_MOD))
+ {
+ ContainCheckDivOrMod(node->AsOp());
+ }
+
+ return next;
}
void Lowering::WidenSIMD12IfNecessary(GenTreeLclVarCommon* node)
@@ -4461,17 +4637,20 @@ GenTree* Lowering::LowerArrElem(GenTree* node)
if (!arrElem->gtArrObj->IsLocal())
{
LIR::Use arrObjUse(BlockRange(), &arrElem->gtArrObj, arrElem);
- arrObjUse.ReplaceWithLclVar(comp, blockWeight);
+ ReplaceWithLclVar(arrObjUse);
}
GenTree* arrObjNode = arrElem->gtArrObj;
assert(arrObjNode->IsLocal());
+ LclVarDsc* const varDsc = &comp->lvaTable[arrElem->gtArrObj->AsLclVarCommon()->gtLclNum];
+
GenTree* insertionPoint = arrElem;
// The first ArrOffs node will have 0 for the offset of the previous dimension.
GenTree* prevArrOffs = new (comp, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, 0);
BlockRange().InsertBefore(insertionPoint, prevArrOffs);
+ GenTree* nextToLower = prevArrOffs;
for (unsigned char dim = 0; dim < rank; dim++)
{
@@ -4486,6 +4665,7 @@ GenTree* Lowering::LowerArrElem(GenTree* node)
else
{
idxArrObjNode = comp->gtClone(arrObjNode);
+ varDsc->incRefCnts(blockWeight, comp);
BlockRange().InsertBefore(insertionPoint, idxArrObjNode);
}
@@ -4496,6 +4676,7 @@ GenTree* Lowering::LowerArrElem(GenTree* node)
BlockRange().InsertBefore(insertionPoint, arrMDIdx);
GenTree* offsArrObjNode = comp->gtClone(arrObjNode);
+ varDsc->incRefCnts(blockWeight, comp);
BlockRange().InsertBefore(insertionPoint, offsArrObjNode);
GenTreeArrOffs* arrOffs =
@@ -4525,6 +4706,7 @@ GenTree* Lowering::LowerArrElem(GenTree* node)
}
GenTreePtr leaBase = comp->gtClone(arrObjNode);
+ varDsc->incRefCnts(blockWeight, comp);
BlockRange().InsertBefore(insertionPoint, leaBase);
GenTreePtr leaNode = new (comp, GT_LEA) GenTreeAddrMode(arrElem->TypeGet(), leaBase, leaIndexNode, scale, offset);
@@ -4547,7 +4729,7 @@ GenTree* Lowering::LowerArrElem(GenTree* node)
DISPTREERANGE(BlockRange(), leaNode);
JITDUMP("\n\n");
- return leaNode;
+ return nextToLower;
}
void Lowering::DoPhase()
@@ -4584,7 +4766,7 @@ void Lowering::DoPhase()
}
#ifdef DEBUG
- JITDUMP("Lower has completed modifying nodes, proceeding to initialize LSRA TreeNodeInfo structs...\n");
+ JITDUMP("Lower has completed modifying nodes.\n");
if (VERBOSE)
{
comp->fgDispBasicBlocks(true);
@@ -4629,83 +4811,6 @@ void Lowering::DoPhase()
assert(LIR::AsRange(block).CheckLIR(comp, true));
}
#endif
-
- // The initialization code for the TreeNodeInfo map was initially part of a single full IR
- // traversal and it has been split because the order of traversal performed by fgWalkTreePost
- // does not necessarily lower nodes in execution order and also, it could potentially
- // add new BasicBlocks on the fly as part of the Lowering pass so the traversal won't be complete.
- //
- // Doing a new traversal guarantees we 'see' all new introduced trees and basic blocks allowing us
- // to correctly initialize all the data structures LSRA requires later on.
- // This code still has issues when it has to do with initialization of recently introduced locals by
- // lowering. The effect of this is that any temporary local variable introduced by lowering won't be
- // enregistered yielding suboptimal CQ.
- // The reason for this is because we cannot re-sort the local variables per ref-count and bump of the number of
- // tracked variables just here because then LSRA will work with mismatching BitSets (i.e. BitSets with different
- // 'epochs' that were created before and after variable resorting, that will result in different number of tracked
- // local variables).
- //
- // The fix for this is to refactor this code to be run JUST BEFORE LSRA and not as part of lowering.
- // It's also desirable to avoid initializing this code using a non-execution order traversal.
- //
- LsraLocation currentLoc = 1;
- for (BasicBlock* block = m_lsra->startBlockSequence(); block != nullptr; block = m_lsra->moveToNextBlock())
- {
- GenTreePtr stmt;
-
- // Increment the LsraLocation (currentLoc) at each BasicBlock.
- // This ensures that the block boundary (RefTypeBB, RefTypeExpUse and RefTypeDummyDef) RefPositions
- // are in increasing location order.
- currentLoc += 2;
-
- m_block = block;
- for (GenTree* node : BlockRange().NonPhiNodes())
- {
- // We increment the number position of each tree node by 2 to simplify the logic when there's the case of
- // a tree that implicitly does a dual-definition of temps (the long case). In this case it is easier to
- // already have an idle spot to handle a dual-def instead of making some messy adjustments if we only
- // increment the number position by one.
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#ifdef DEBUG
- node->gtSeqNum = currentLoc;
-#endif
-
- node->gtLsraInfo.Initialize(m_lsra, node, currentLoc);
- node->gtClearReg(comp);
-
- currentLoc += 2;
-
- TreeNodeInfoInit(node);
-
- // Only nodes that produce values should have a non-zero dstCount.
- assert((node->gtLsraInfo.dstCount == 0) || node->IsValue());
-
- // If the node produces an unused value, mark it as a local def-use
- if (node->IsValue() && node->IsUnusedValue())
- {
- node->gtLsraInfo.isLocalDefUse = true;
- node->gtLsraInfo.dstCount = 0;
- }
-
-#if 0
- // TODO-CQ: Enable this code after fixing the isContained() logic to not abort for these
- // top-level nodes that throw away their result.
- // If this is an interlocked operation that has a non-last-use lclVar as its op2,
- // make sure we allocate a target register for the interlocked operation.; otherwise we need
- // not allocate a register
- else if ((tree->OperGet() == GT_LOCKADD || tree->OperGet() == GT_XCHG || tree->OperGet() == GT_XADD))
- {
- tree->gtLsraInfo.dstCount = 0;
- if (tree->gtGetOp2()->IsLocal() && (tree->gtFlags & GTF_VAR_DEATH) == 0)
- tree->gtLsraInfo.isLocalDefUse = true;
- }
-#endif
- }
-
- assert(BlockRange().CheckLIR(comp, true));
- }
- DBEXEC(VERBOSE, DumpNodeInfoMap());
}
#ifdef DEBUG
@@ -4729,6 +4834,7 @@ void Lowering::CheckCallArg(GenTree* arg)
case GT_FIELD_LIST:
{
GenTreeFieldList* list = arg->AsFieldList();
+ assert(list->isContained());
assert(list->IsFieldListHead());
for (; list != nullptr; list = list->Rest())
@@ -4776,9 +4882,10 @@ void Lowering::CheckCall(GenTreeCall* call)
// after lowering.
//
// Arguments:
+// compiler - the compiler context.
// node - the node to check.
//
-void Lowering::CheckNode(GenTree* node)
+void Lowering::CheckNode(Compiler* compiler, GenTree* node)
{
switch (node->OperGet())
{
@@ -4788,13 +4895,19 @@ void Lowering::CheckNode(GenTree* node)
#ifdef FEATURE_SIMD
case GT_SIMD:
+ assert(node->TypeGet() != TYP_SIMD12);
+ break;
#ifdef _TARGET_64BIT_
case GT_LCL_VAR:
case GT_STORE_LCL_VAR:
+ {
+ unsigned lclNum = node->AsLclVarCommon()->GetLclNum();
+ LclVarDsc* lclVar = &compiler->lvaTable[lclNum];
+ assert(node->TypeGet() != TYP_SIMD12 || compiler->lvaIsFieldOfDependentlyPromotedStruct(lclVar));
+ }
+ break;
#endif // _TARGET_64BIT_
- assert(node->TypeGet() != TYP_SIMD12);
- break;
-#endif
+#endif // SIMD
default:
break;
@@ -4816,7 +4929,7 @@ bool Lowering::CheckBlock(Compiler* compiler, BasicBlock* block)
LIR::Range& blockRange = LIR::AsRange(block);
for (GenTree* node : blockRange)
{
- CheckNode(node);
+ CheckNode(compiler, node);
}
assert(blockRange.CheckLIR(compiler, true));
@@ -4910,7 +5023,7 @@ bool Lowering::IndirsAreEquivalent(GenTreePtr candidate, GenTreePtr storeInd)
GenTreeAddrMode* gtAddr2 = pTreeB->AsAddrMode();
return NodesAreEquivalentLeaves(gtAddr1->Base(), gtAddr2->Base()) &&
NodesAreEquivalentLeaves(gtAddr1->Index(), gtAddr2->Index()) &&
- gtAddr1->gtScale == gtAddr2->gtScale && gtAddr1->gtOffset == gtAddr2->gtOffset;
+ (gtAddr1->gtScale == gtAddr2->gtScale) && (gtAddr1->Offset() == gtAddr2->Offset());
}
default:
// We don't handle anything that is not either a constant,
@@ -4979,7 +5092,7 @@ void Lowering::getCastDescription(GenTreePtr treeNode, CastInfo* castInfo)
GenTreePtr castOp = treeNode->gtCast.CastOp();
var_types dstType = treeNode->CastToType();
- var_types srcType = castOp->TypeGet();
+ var_types srcType = genActualType(castOp->TypeGet());
castInfo->unsignedDest = varTypeIsUnsigned(dstType);
castInfo->unsignedSource = varTypeIsUnsigned(srcType);
@@ -5084,40 +5197,119 @@ void Lowering::getCastDescription(GenTreePtr treeNode, CastInfo* castInfo)
}
//------------------------------------------------------------------------
-// GetIndirSourceCount: Get the source registers for an indirection that might be contained.
-//
-// Arguments:
-// node - The node of interest
-//
-// Return Value:
-// The number of source registers used by the *parent* of this node.
-//
-int Lowering::GetIndirSourceCount(GenTreeIndir* indirTree)
+// Containment Analysis
+//------------------------------------------------------------------------
+void Lowering::ContainCheckNode(GenTree* node)
{
- GenTree* const addr = indirTree->gtOp1;
- if (!addr->isContained())
- {
- return 1;
- }
- if (!addr->OperIs(GT_LEA))
+ switch (node->gtOper)
{
- return 0;
- }
+ case GT_STORE_LCL_VAR:
+ case GT_STORE_LCL_FLD:
+ ContainCheckStoreLoc(node->AsLclVarCommon());
+ break;
- GenTreeAddrMode* const addrMode = addr->AsAddrMode();
+ case GT_EQ:
+ case GT_NE:
+ case GT_LT:
+ case GT_LE:
+ case GT_GE:
+ case GT_GT:
+ case GT_TEST_EQ:
+ case GT_TEST_NE:
+ case GT_CMP:
+ ContainCheckCompare(node->AsOp());
+ break;
- unsigned srcCount = 0;
- if ((addrMode->Base() != nullptr) && !addrMode->Base()->isContained())
- {
- srcCount++;
- }
- if (addrMode->Index() != nullptr)
- {
- // We never have a contained index.
- assert(!addrMode->Index()->isContained());
- srcCount++;
+ case GT_JTRUE:
+ ContainCheckJTrue(node->AsOp());
+ break;
+
+ case GT_ADD:
+ case GT_SUB:
+#if !defined(_TARGET_64BIT_)
+ case GT_ADD_LO:
+ case GT_ADD_HI:
+ case GT_SUB_LO:
+ case GT_SUB_HI:
+#endif
+ case GT_AND:
+ case GT_OR:
+ case GT_XOR:
+ ContainCheckBinary(node->AsOp());
+ break;
+
+#ifdef _TARGET_XARCH_
+ case GT_NEG:
+ // Codegen of this tree node sets ZF and SF flags.
+ if (!varTypeIsFloating(node))
+ {
+ node->gtFlags |= GTF_ZSF_SET;
+ }
+ break;
+#endif // _TARGET_XARCH_
+
+#if defined(_TARGET_X86_)
+ case GT_MUL_LONG:
+#endif
+ case GT_MUL:
+ case GT_MULHI:
+ ContainCheckMul(node->AsOp());
+ break;
+ case GT_DIV:
+ case GT_MOD:
+ case GT_UDIV:
+ case GT_UMOD:
+ ContainCheckDivOrMod(node->AsOp());
+ break;
+ case GT_LSH:
+ case GT_RSH:
+ case GT_RSZ:
+ case GT_ROL:
+ case GT_ROR:
+#ifndef _TARGET_64BIT_
+ case GT_LSH_HI:
+ case GT_RSH_LO:
+#endif
+ ContainCheckShiftRotate(node->AsOp());
+ break;
+ case GT_ARR_OFFSET:
+ ContainCheckArrOffset(node->AsArrOffs());
+ break;
+ case GT_LCLHEAP:
+ ContainCheckLclHeap(node->AsOp());
+ break;
+ case GT_RETURN:
+ ContainCheckRet(node->AsOp());
+ break;
+ case GT_RETURNTRAP:
+ ContainCheckReturnTrap(node->AsOp());
+ break;
+ case GT_STOREIND:
+ ContainCheckStoreIndir(node->AsIndir());
+ case GT_IND:
+ ContainCheckIndir(node->AsIndir());
+ break;
+ case GT_PUTARG_REG:
+ case GT_PUTARG_STK:
+#ifdef _TARGET_ARM_
+ case GT_PUTARG_SPLIT:
+#endif
+ // The regNum must have been set by the lowering of the call.
+ assert(node->gtRegNum != REG_NA);
+ break;
+#ifdef _TARGET_XARCH_
+ case GT_INTRINSIC:
+ ContainCheckIntrinsic(node->AsOp());
+ break;
+#endif // _TARGET_XARCH_
+#ifdef FEATURE_SIMD
+ case GT_SIMD:
+ ContainCheckSIMD(node->AsSIMD());
+ break;
+#endif // FEATURE_SIMD
+ default:
+ break;
}
- return srcCount;
}
//------------------------------------------------------------------------
@@ -5140,7 +5332,7 @@ void Lowering::ContainCheckDivOrMod(GenTreeOp* node)
// everything is made explicit by adding casts.
assert(dividend->TypeGet() == divisor->TypeGet());
- if (IsContainableMemoryOp(divisor, true) || divisor->IsCnsNonZeroFltOrDbl())
+ if (IsContainableMemoryOp(divisor) || divisor->IsCnsNonZeroFltOrDbl())
{
MakeSrcContained(node, divisor);
}
@@ -5162,7 +5354,7 @@ void Lowering::ContainCheckDivOrMod(GenTreeOp* node)
#endif
// divisor can be an r/m, but the memory indirection must be of the same size as the divide
- if (IsContainableMemoryOp(divisor, true) && (divisor->TypeGet() == node->TypeGet()))
+ if (IsContainableMemoryOp(divisor) && (divisor->TypeGet() == node->TypeGet()))
{
MakeSrcContained(node, divisor);
}
@@ -5183,12 +5375,14 @@ void Lowering::ContainCheckDivOrMod(GenTreeOp* node)
//
void Lowering::ContainCheckReturnTrap(GenTreeOp* node)
{
+#ifdef _TARGET_XARCH_
assert(node->OperIs(GT_RETURNTRAP));
// This just turns into a compare of its child with an int + a conditional call
if (node->gtOp1->isIndir())
{
MakeSrcContained(node, node->gtOp1);
}
+#endif // _TARGET_XARCH_
}
//------------------------------------------------------------------------
@@ -5262,7 +5456,6 @@ void Lowering::ContainCheckRet(GenTreeOp* ret)
#endif // FEATURE_MULTIREG_RET
}
-#ifdef FEATURE_SIMD
//------------------------------------------------------------------------
// ContainCheckJTrue: determine whether the source of a JTRUE should be contained.
//
@@ -5271,6 +5464,11 @@ void Lowering::ContainCheckRet(GenTreeOp* ret)
//
void Lowering::ContainCheckJTrue(GenTreeOp* node)
{
+ // The compare does not need to be generated into a register.
+ GenTree* cmp = node->gtGetOp1();
+ cmp->gtLsraInfo.isNoRegCompare = true;
+
+#ifdef FEATURE_SIMD
assert(node->OperIs(GT_JTRUE));
// Say we have the following IR
@@ -5280,7 +5478,6 @@ void Lowering::ContainCheckJTrue(GenTreeOp* node)
//
// In this case we don't need to generate code for GT_EQ_/NE, since SIMD (In)Equality
// intrinsic will set or clear the Zero flag.
- GenTree* cmp = node->gtGetOp1();
genTreeOps cmpOper = cmp->OperGet();
if (cmpOper == GT_EQ || cmpOper == GT_NE)
{
@@ -5291,30 +5488,35 @@ void Lowering::ContainCheckJTrue(GenTreeOp* node)
{
// We always generate code for a SIMD equality comparison, though it produces no value.
// Neither the GT_JTRUE nor the immediate need to be evaluated.
- m_lsra->clearOperandCounts(cmp);
MakeSrcContained(cmp, cmpOp2);
+ cmpOp1->gtLsraInfo.isNoRegCompare = true;
+ // We have to reverse compare oper in the following cases:
+ // 1) SIMD Equality: Sets Zero flag on equal otherwise clears it.
+ // Therefore, if compare oper is == or != against false(0), we will
+ // be checking opposite of what is required.
+ //
+ // 2) SIMD inEquality: Clears Zero flag on true otherwise sets it.
+ // Therefore, if compare oper is == or != against true(1), we will
+ // be checking opposite of what is required.
+ GenTreeSIMD* simdNode = cmpOp1->AsSIMD();
+ if (simdNode->gtSIMDIntrinsicID == SIMDIntrinsicOpEquality)
+ {
+ if (cmpOp2->IsIntegralConst(0))
+ {
+ cmp->SetOper(GenTree::ReverseRelop(cmpOper));
+ }
+ }
+ else
+ {
+ assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicOpInEquality);
+ if (cmpOp2->IsIntegralConst(1))
+ {
+ cmp->SetOper(GenTree::ReverseRelop(cmpOper));
+ }
+ }
}
}
-}
#endif // FEATURE_SIMD
-
-#ifdef DEBUG
-void Lowering::DumpNodeInfoMap()
-{
- printf("-----------------------------\n");
- printf("TREE NODE INFO DUMP\n");
- printf("-----------------------------\n");
-
- for (BasicBlock* block = comp->fgFirstBB; block != nullptr; block = block->bbNext)
- {
- for (GenTree* node : LIR::AsRange(block).NonPhiNodes())
- {
- comp->gtDispTree(node, nullptr, nullptr, true);
- printf(" +");
- node->gtLsraInfo.dump(m_lsra);
- }
- }
}
-#endif // DEBUG
#endif // !LEGACY_BACKEND
diff --git a/src/jit/lower.h b/src/jit/lower.h
index 07d194f3da..ce6bb94831 100644
--- a/src/jit/lower.h
+++ b/src/jit/lower.h
@@ -47,42 +47,84 @@ public:
static void getCastDescription(GenTreePtr treeNode, CastInfo* castInfo);
+ // This variant of LowerRange is called from outside of the main Lowering pass,
+ // so it creates its own instance of Lowering to do so.
+ void LowerRange(BasicBlock* block, LIR::ReadOnlyRange& range)
+ {
+ Lowering lowerer(comp, m_lsra);
+ lowerer.m_block = block;
+
+ lowerer.LowerRange(range);
+ }
+
+private:
// LowerRange handles new code that is introduced by or after Lowering.
- void LowerRange(LIR::Range&& range)
+ void LowerRange(LIR::ReadOnlyRange& range)
{
for (GenTree* newNode : range)
{
LowerNode(newNode);
}
}
+ void LowerRange(GenTree* firstNode, GenTree* lastNode)
+ {
+ LIR::ReadOnlyRange range(firstNode, lastNode);
+ LowerRange(range);
+ }
+
+ // ContainCheckRange handles new code that is introduced by or after Lowering,
+ // and that is known to be already in Lowered form.
+ void ContainCheckRange(LIR::ReadOnlyRange& range)
+ {
+ for (GenTree* newNode : range)
+ {
+ ContainCheckNode(newNode);
+ }
+ }
+ void ContainCheckRange(GenTree* firstNode, GenTree* lastNode)
+ {
+ LIR::ReadOnlyRange range(firstNode, lastNode);
+ ContainCheckRange(range);
+ }
+
+ void InsertTreeBeforeAndContainCheck(GenTree* insertionPoint, GenTree* tree)
+ {
+ LIR::Range range = LIR::SeqTree(comp, tree);
+ ContainCheckRange(range);
+ BlockRange().InsertBefore(insertionPoint, std::move(range));
+ }
+
+ void ContainCheckNode(GenTree* node);
-private:
void ContainCheckDivOrMod(GenTreeOp* node);
void ContainCheckReturnTrap(GenTreeOp* node);
void ContainCheckArrOffset(GenTreeArrOffs* node);
void ContainCheckLclHeap(GenTreeOp* node);
void ContainCheckRet(GenTreeOp* node);
- void ContainCheckBinary(GenTreeOp* node);
+ void ContainCheckJTrue(GenTreeOp* node);
+
+ void ContainCheckCallOperands(GenTreeCall* call);
+ void ContainCheckIndir(GenTreeIndir* indirNode);
+ void ContainCheckStoreIndir(GenTreeIndir* indirNode);
void ContainCheckMul(GenTreeOp* node);
void ContainCheckShiftRotate(GenTreeOp* node);
void ContainCheckStoreLoc(GenTreeLclVarCommon* storeLoc);
- void ContainCheckIndir(GenTreeIndir* indirNode);
void ContainCheckCast(GenTreeCast* node);
void ContainCheckCompare(GenTreeOp* node);
+ void ContainCheckBinary(GenTreeOp* node);
void ContainCheckBoundsChk(GenTreeBoundsChk* node);
#ifdef _TARGET_XARCH_
void ContainCheckFloatBinary(GenTreeOp* node);
void ContainCheckIntrinsic(GenTreeOp* node);
#endif // _TARGET_XARCH_
#ifdef FEATURE_SIMD
- void ContainCheckJTrue(GenTreeOp* node);
void ContainCheckSIMD(GenTreeSIMD* simdNode);
#endif // FEATURE_SIMD
#ifdef DEBUG
static void CheckCallArg(GenTree* arg);
static void CheckCall(GenTreeCall* call);
- static void CheckNode(GenTree* node);
+ static void CheckNode(Compiler* compiler, GenTree* node);
static bool CheckBlock(Compiler* compiler, BasicBlock* block);
#endif // DEBUG
@@ -147,25 +189,31 @@ private:
return new (comp, GT_LEA) GenTreeAddrMode(resultType, base, nullptr, 0, offset);
}
- // returns true if the tree can use the read-modify-write memory instruction form
- bool isRMWRegOper(GenTreePtr tree);
+ GenTree* OffsetByIndex(GenTree* base, GenTree* index)
+ {
+ var_types resultType = (base->TypeGet() == TYP_REF) ? TYP_BYREF : base->TypeGet();
+ return new (comp, GT_LEA) GenTreeAddrMode(resultType, base, index, 0, 0);
+ }
+
+ // Replace the definition of the given use with a lclVar, allocating a new temp
+ // if 'tempNum' is BAD_VAR_NUM.
+ unsigned ReplaceWithLclVar(LIR::Use& use, unsigned tempNum = BAD_VAR_NUM)
+ {
+ GenTree* oldUseNode = use.Def();
+ if ((oldUseNode->gtOper != GT_LCL_VAR) || (tempNum != BAD_VAR_NUM))
+ {
+ unsigned newLclNum = use.ReplaceWithLclVar(comp, m_block->getBBWeight(comp), tempNum);
+ GenTree* newUseNode = use.Def();
+ ContainCheckRange(oldUseNode->gtNext, newUseNode);
+ return newLclNum;
+ }
+ return oldUseNode->AsLclVarCommon()->gtLclNum;
+ }
// return true if this call target is within range of a pc-rel call on the machine
bool IsCallTargetInRange(void* addr);
-#ifdef _TARGET_X86_
- bool ExcludeNonByteableRegisters(GenTree* tree);
-#endif
-
- void TreeNodeInfoInit(GenTree* stmt);
-
- void TreeNodeInfoInitCheckByteable(GenTree* tree);
-
- void SetDelayFree(GenTree* delayUseSrc);
-
#if defined(_TARGET_XARCH_)
- void TreeNodeInfoInitSimple(GenTree* tree);
-
//----------------------------------------------------------------------
// SetRegOptional - sets a bit to indicate to LSRA that register
// for a given tree node is optional for codegen purpose. If no
@@ -226,57 +274,20 @@ private:
}
#endif // defined(_TARGET_XARCH_)
- // TreeNodeInfoInit methods
-
- int GetOperandSourceCount(GenTree* node);
- int GetIndirSourceCount(GenTreeIndir* indirTree);
-
- void TreeNodeInfoInitStoreLoc(GenTree* tree);
- void TreeNodeInfoInitReturn(GenTree* tree);
- void TreeNodeInfoInitShiftRotate(GenTree* tree);
- void TreeNodeInfoInitPutArgReg(
- GenTreeUnOp* node, regNumber argReg, TreeNodeInfo& info, bool isVarArgs, bool* callHasFloatRegArgs);
- void TreeNodeInfoInitCall(GenTreeCall* call);
- void TreeNodeInfoInitCmp(GenTreePtr tree);
- void TreeNodeInfoInitStructArg(GenTreePtr structArg);
- void TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode);
- void TreeNodeInfoInitModDiv(GenTree* tree);
- void TreeNodeInfoInitIntrinsic(GenTree* tree);
- void TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* tree);
- void TreeNodeInfoInitIndir(GenTreeIndir* indirTree);
- void TreeNodeInfoInitGCWriteBarrier(GenTree* tree);
-#if !CPU_LOAD_STORE_ARCH
- bool TreeNodeInfoInitIfRMWMemOp(GenTreePtr storeInd);
-#endif
-#ifdef FEATURE_SIMD
- void TreeNodeInfoInitSIMD(GenTree* tree);
-#endif // FEATURE_SIMD
- void TreeNodeInfoInitCast(GenTree* tree);
+ // Per tree node member functions
+ void LowerStoreIndir(GenTreeIndir* node);
+ GenTree* LowerAdd(GenTree* node);
+ bool LowerUnsignedDivOrMod(GenTreeOp* divMod);
+ GenTree* LowerConstIntDivOrMod(GenTree* node);
+ GenTree* LowerSignedDivOrMod(GenTree* node);
+ void LowerBlockStore(GenTreeBlk* blkNode);
#ifdef _TARGET_ARM64_
void LowerPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info);
- void TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info);
#endif // _TARGET_ARM64_
#ifdef _TARGET_ARM_
void LowerPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info);
- void TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info);
#endif // _TARGET_ARM64_
-#ifdef FEATURE_PUT_STRUCT_ARG_STK
void LowerPutArgStk(GenTreePutArgStk* tree);
- void TreeNodeInfoInitPutArgStk(GenTreePutArgStk* tree);
-#ifdef _TARGET_ARM_
- void TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* tree, TreeNodeInfo& info, fgArgTabEntryPtr argInfo);
-#endif
-#endif // FEATURE_PUT_STRUCT_ARG_STK
- void TreeNodeInfoInitLclHeap(GenTree* tree);
-
- void DumpNodeInfoMap();
-
- // Per tree node member functions
- void LowerStoreInd(GenTree* node);
- GenTree* LowerAdd(GenTree* node);
- GenTree* LowerUnsignedDivOrMod(GenTreeOp* divMod);
- GenTree* LowerSignedDivOrMod(GenTree* node);
- void LowerBlockStore(GenTreeBlk* blkNode);
GenTree* TryCreateAddrMode(LIR::Use&& use, bool isIndir);
void AddrModeCleanupHelper(GenTreeAddrMode* addrMode, GenTree* node);
@@ -284,11 +295,6 @@ private:
GenTree* LowerSwitch(GenTree* node);
void LowerCast(GenTree* node);
-#if defined(_TARGET_XARCH_)
- void TreeNodeInfoInitMul(GenTreePtr tree);
- void SetContainsAVXFlags(bool isFloatingPointType = true, unsigned sizeOfSIMDVector = 0);
-#endif // defined(_TARGET_XARCH_)
-
#if !CPU_LOAD_STORE_ARCH
bool IsRMWIndirCandidate(GenTree* operand, GenTree* storeInd);
bool IsBinOpInRMWStoreInd(GenTreePtr tree);
@@ -301,6 +307,9 @@ private:
GenTree* LowerArrElem(GenTree* node);
void LowerRotate(GenTree* tree);
void LowerShift(GenTreeOp* shift);
+#ifdef FEATURE_SIMD
+ void LowerSIMD(GenTreeSIMD* simdNode);
+#endif // FEATURE_SIMD
// Utility functions
void MorphBlkIntoHelperCall(GenTreePtr pTree, GenTreePtr treeStmt);
@@ -319,7 +328,10 @@ private:
bool IsContainableImmed(GenTree* parentNode, GenTree* childNode);
// Return true if 'node' is a containable memory op.
- bool IsContainableMemoryOp(GenTree* node, bool useTracked);
+ bool IsContainableMemoryOp(GenTree* node)
+ {
+ return m_lsra->isContainableMemoryOp(node);
+ }
// Makes 'childNode' contained in the 'parentNode'
void MakeSrcContained(GenTreePtr parentNode, GenTreePtr childNode);
diff --git a/src/jit/lowerarm.cpp b/src/jit/lowerarm.cpp
index 1d94caac5f..8cf5225388 100644
--- a/src/jit/lowerarm.cpp
+++ b/src/jit/lowerarm.cpp
@@ -29,18 +29,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "sideeffects.h"
#include "lower.h"
-//------------------------------------------------------------------------
-// isRMWRegOper: Can use the read-mofify-write memory instruction form?
-//
-// Return Value:
-// True if the tree can use the read-modify-write memory instruction form
-//
-bool Lowering::isRMWRegOper(GenTreePtr tree)
-{
- NYI_ARM("isRMWRegOper() is never used and tested for ARM");
- return false;
-}
-
#endif // _TARGET_ARM_
#endif // !LEGACY_BACKEND
diff --git a/src/jit/lowerarm64.cpp b/src/jit/lowerarm64.cpp
index 38a82c7a5a..060a3a9fbb 100644
--- a/src/jit/lowerarm64.cpp
+++ b/src/jit/lowerarm64.cpp
@@ -29,12 +29,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "sideeffects.h"
#include "lower.h"
-// returns true if the tree can use the read-modify-write memory instruction form
-bool Lowering::isRMWRegOper(GenTreePtr tree)
-{
- return false;
-}
-
#endif // _TARGET_ARM64_
#endif // !LEGACY_BACKEND
diff --git a/src/jit/lowerarmarch.cpp b/src/jit/lowerarmarch.cpp
index 7104577839..64c7886a64 100644
--- a/src/jit/lowerarmarch.cpp
+++ b/src/jit/lowerarmarch.cpp
@@ -126,6 +126,8 @@ bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode)
case GT_AND:
case GT_OR:
case GT_XOR:
+ case GT_TEST_EQ:
+ case GT_TEST_NE:
return emitter::emitIns_valid_imm_for_alu(immVal, size);
break;
#elif defined(_TARGET_ARM_)
@@ -215,6 +217,21 @@ void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc)
}
}
}
+ ContainCheckStoreLoc(storeLoc);
+}
+
+//------------------------------------------------------------------------
+// LowerStoreIndir: Determine addressing mode for an indirection, and whether operands are contained.
+//
+// Arguments:
+// node - The indirect store node (GT_STORE_IND) of interest
+//
+// Return Value:
+// None.
+//
+void Lowering::LowerStoreIndir(GenTreeIndir* node)
+{
+ ContainCheckStoreIndir(node);
}
//------------------------------------------------------------------------
@@ -255,6 +272,7 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
GenTreePtr initVal = source;
if (initVal->OperIsInitVal())
{
+ initVal->SetContained();
initVal = initVal->gtGetOp1();
}
srcAddrOrFill = initVal;
@@ -276,7 +294,11 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
// the largest width store of the desired inline expansion.
ssize_t fill = initVal->gtIntCon.gtIconVal & 0xFF;
- if (size < REGSIZE_BYTES)
+ if (fill == 0)
+ {
+ MakeSrcContained(blkNode, source);
+ }
+ else if (size < REGSIZE_BYTES)
{
initVal->gtIntCon.gtIconVal = 0x01010101 * fill;
}
@@ -348,6 +370,16 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper;
}
}
+ // CopyObj or CopyBlk
+ if (source->gtOper == GT_IND)
+ {
+ MakeSrcContained(blkNode, source);
+ }
+ else if (!source->IsMultiRegCall() && !source->OperIsSIMD())
+ {
+ assert(source->IsLocal());
+ MakeSrcContained(blkNode, source);
+ }
}
}
@@ -418,6 +450,9 @@ void Lowering::LowerCast(GenTree* tree)
tree->gtOp.gtOp1 = tmp;
BlockRange().InsertAfter(op1, tmp);
}
+
+ // Now determine if we have operands that should be contained.
+ ContainCheckCast(tree->AsCast());
}
//------------------------------------------------------------------------
@@ -453,6 +488,7 @@ void Lowering::LowerRotate(GenTreePtr tree)
}
tree->ChangeOper(GT_ROR);
}
+ ContainCheckShiftRotate(tree->AsOp());
}
//------------------------------------------------------------------------
@@ -460,31 +496,67 @@ void Lowering::LowerRotate(GenTreePtr tree)
//------------------------------------------------------------------------
//------------------------------------------------------------------------
-// ContainCheckIndir: Determine whether operands of an indir should be contained.
+// ContainCheckCallOperands: Determine whether operands of a call should be contained.
//
// Arguments:
-// node - The indirection node of interest
-//
-// Notes:
-// This is called for both store and load indirections.
+// call - The call node of interest
//
// Return Value:
// None.
//
-void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
+void Lowering::ContainCheckCallOperands(GenTreeCall* call)
{
-#ifdef _TARGET_ARM64_
- if (indirNode->OperIs(GT_STOREIND))
+ GenTree* ctrlExpr = call->gtControlExpr;
+ // If there is an explicit this pointer, we don't want that node to produce anything
+ // as it is redundant
+ if (call->gtCallObjp != nullptr)
{
- GenTree* src = indirNode->gtOp.gtOp2;
- if (!varTypeIsFloating(src->TypeGet()) && src->IsIntegralConst(0))
+ GenTreePtr thisPtrNode = call->gtCallObjp;
+
+ if (thisPtrNode->canBeContained())
{
- // an integer zero for 'src' can be contained.
- MakeSrcContained(indirNode, src);
+ MakeSrcContained(call, thisPtrNode);
+ if (thisPtrNode->gtOper == GT_PUTARG_REG)
+ {
+ MakeSrcContained(call, thisPtrNode->gtOp.gtOp1);
+ }
}
}
+}
+
+//------------------------------------------------------------------------
+// ContainCheckStoreIndir: determine whether the sources of a STOREIND node should be contained.
+//
+// Arguments:
+// node - pointer to the node
+//
+void Lowering::ContainCheckStoreIndir(GenTreeIndir* node)
+{
+#ifdef _TARGET_ARM64_
+ GenTree* src = node->gtOp.gtOp2;
+ if (!varTypeIsFloating(src->TypeGet()) && src->IsIntegralConst(0))
+ {
+ // an integer zero for 'src' can be contained.
+ MakeSrcContained(node, src);
+ }
#endif // _TARGET_ARM64_
+ ContainCheckIndir(node);
+}
+//------------------------------------------------------------------------
+// ContainCheckIndir: Determine whether operands of an indir should be contained.
+//
+// Arguments:
+// indirNode - The indirection node of interest
+//
+// Notes:
+// This is called for both store and load indirections.
+//
+// Return Value:
+// None.
+//
+void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
+{
// If this is the rhs of a block copy it will be handled when we handle the store.
if (indirNode->TypeGet() == TYP_STRUCT)
{
@@ -498,7 +570,7 @@ void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
GenTreeAddrMode* lea = addr->AsAddrMode();
GenTree* base = lea->Base();
GenTree* index = lea->Index();
- unsigned cns = lea->gtOffset;
+ int cns = lea->Offset();
#ifdef _TARGET_ARM_
// ARM floating-point load/store doesn't support a form similar to integer
diff --git a/src/jit/lowerxarch.cpp b/src/jit/lowerxarch.cpp
index 1c679478aa..20a08750a6 100644
--- a/src/jit/lowerxarch.cpp
+++ b/src/jit/lowerxarch.cpp
@@ -30,8 +30,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "lower.h"
// xarch supports both ROL and ROR instructions so no lowering is required.
-void Lowering::LowerRotate(GenTreePtr tree)
+void Lowering::LowerRotate(GenTree* tree)
{
+ ContainCheckShiftRotate(tree->AsOp());
}
//------------------------------------------------------------------------
@@ -75,7 +76,10 @@ void Lowering::LowerShift(GenTreeOp* shift)
shift->gtOp2 = andOp->gtGetOp1();
BlockRange().Remove(andOp);
BlockRange().Remove(maskOp);
+ // The parent was replaced, clear contain and regOpt flag.
+ shift->gtOp2->ClearContained();
}
+ ContainCheckShiftRotate(shift);
}
//------------------------------------------------------------------------
@@ -86,12 +90,11 @@ void Lowering::LowerShift(GenTreeOp* shift)
//
// Notes:
// This involves:
+// - Handling of contained immediates.
// - Widening operations of unsigneds.
void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc)
{
- GenTree* op1 = storeLoc->gtGetOp1();
-
// Try to widen the ops if they are going into a local var.
if ((storeLoc->gtOper == GT_STORE_LCL_VAR) && (storeLoc->gtOp1->gtOper == GT_CNS_INT))
{
@@ -140,6 +143,39 @@ void Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc)
}
}
}
+ ContainCheckStoreLoc(storeLoc);
+}
+
+//------------------------------------------------------------------------
+// LowerStoreIndir: Determine addressing mode for an indirection, and whether operands are contained.
+//
+// Arguments:
+// node - The indirect store node (GT_STORE_IND) of interest
+//
+// Return Value:
+// None.
+//
+void Lowering::LowerStoreIndir(GenTreeIndir* node)
+{
+ // Mark all GT_STOREIND nodes to indicate that it is not known
+ // whether it represents a RMW memory op.
+ node->AsStoreInd()->SetRMWStatusDefault();
+
+ if (!varTypeIsFloating(node))
+ {
+ // Perform recognition of trees with the following structure:
+ // StoreInd(addr, BinOp(expr, GT_IND(addr)))
+ // to be able to fold this into an instruction of the form
+ // BINOP [addr], register
+ // where register is the actual place where 'expr' is computed.
+ //
+ // SSE2 doesn't support RMW form of instructions.
+ if (LowerRMWMemOp(node))
+ {
+ return;
+ }
+ }
+ ContainCheckStoreIndir(node);
}
//------------------------------------------------------------------------
@@ -178,6 +214,7 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
GenTree* initVal = source;
if (initVal->OperIsInitVal())
{
+ initVal->SetContained();
initVal = initVal->gtGetOp1();
}
srcAddrOrFill = initVal;
@@ -218,11 +255,19 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
{
initVal->gtIntCon.gtIconVal = 0x0101010101010101LL * fill;
initVal->gtType = TYP_LONG;
+ if ((fill == 0) && ((size & 0xf) == 0))
+ {
+ MakeSrcContained(blkNode, source);
+ }
}
#else // !_TARGET_AMD64_
initVal->gtIntCon.gtIconVal = 0x01010101 * fill;
#endif // !_TARGET_AMD64_
+ if ((fill == 0) && ((size & 0xf) == 0))
+ {
+ MakeSrcContained(blkNode, source);
+ }
blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
}
else
@@ -239,134 +284,165 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
#endif // !_TARGET_AMD64_
}
}
- else if (blkNode->gtOper == GT_STORE_OBJ)
+ else
{
- // CopyObj
+ if (blkNode->gtOper == GT_STORE_OBJ)
+ {
+ // CopyObj
- GenTreeObj* cpObjNode = blkNode->AsObj();
+ GenTreeObj* cpObjNode = blkNode->AsObj();
- unsigned slots = cpObjNode->gtSlots;
+ unsigned slots = cpObjNode->gtSlots;
#ifdef DEBUG
- // CpObj must always have at least one GC-Pointer as a member.
- assert(cpObjNode->gtGcPtrCount > 0);
-
- assert(dstAddr->gtType == TYP_BYREF || dstAddr->gtType == TYP_I_IMPL);
-
- CORINFO_CLASS_HANDLE clsHnd = cpObjNode->gtClass;
- size_t classSize = comp->info.compCompHnd->getClassSize(clsHnd);
- size_t blkSize = roundUp(classSize, TARGET_POINTER_SIZE);
-
- // Currently, the EE always round up a class data structure so
- // we are not handling the case where we have a non multiple of pointer sized
- // struct. This behavior may change in the future so in order to keeps things correct
- // let's assert it just to be safe. Going forward we should simply
- // handle this case.
- assert(classSize == blkSize);
- assert((blkSize / TARGET_POINTER_SIZE) == slots);
- assert(cpObjNode->HasGCPtr());
+ // CpObj must always have at least one GC-Pointer as a member.
+ assert(cpObjNode->gtGcPtrCount > 0);
+
+ assert(dstAddr->gtType == TYP_BYREF || dstAddr->gtType == TYP_I_IMPL);
+
+ CORINFO_CLASS_HANDLE clsHnd = cpObjNode->gtClass;
+ size_t classSize = comp->info.compCompHnd->getClassSize(clsHnd);
+ size_t blkSize = roundUp(classSize, TARGET_POINTER_SIZE);
+
+ // Currently, the EE always round up a class data structure so
+ // we are not handling the case where we have a non multiple of pointer sized
+ // struct. This behavior may change in the future so in order to keeps things correct
+ // let's assert it just to be safe. Going forward we should simply
+ // handle this case.
+ assert(classSize == blkSize);
+ assert((blkSize / TARGET_POINTER_SIZE) == slots);
+ assert(cpObjNode->HasGCPtr());
#endif
- bool IsRepMovsProfitable = false;
+ bool IsRepMovsProfitable = false;
- // If the destination is not on the stack, let's find out if we
- // can improve code size by using rep movsq instead of generating
- // sequences of movsq instructions.
- if (!dstAddr->OperIsLocalAddr())
- {
- // Let's inspect the struct/class layout and determine if it's profitable
- // to use rep movsq for copying non-gc memory instead of using single movsq
- // instructions for each memory slot.
- unsigned i = 0;
- BYTE* gcPtrs = cpObjNode->gtGcPtrs;
-
- do
+ // If the destination is not on the stack, let's find out if we
+ // can improve code size by using rep movsq instead of generating
+ // sequences of movsq instructions.
+ if (!dstAddr->OperIsLocalAddr())
{
- unsigned nonGCSlots = 0;
- // Measure a contiguous non-gc area inside the struct and note the maximum.
- while (i < slots && gcPtrs[i] == TYPE_GC_NONE)
- {
- nonGCSlots++;
- i++;
- }
+ // Let's inspect the struct/class layout and determine if it's profitable
+ // to use rep movsq for copying non-gc memory instead of using single movsq
+ // instructions for each memory slot.
+ unsigned i = 0;
+ BYTE* gcPtrs = cpObjNode->gtGcPtrs;
- while (i < slots && gcPtrs[i] != TYPE_GC_NONE)
+ do
{
- i++;
- }
+ unsigned nonGCSlots = 0;
+ // Measure a contiguous non-gc area inside the struct and note the maximum.
+ while (i < slots && gcPtrs[i] == TYPE_GC_NONE)
+ {
+ nonGCSlots++;
+ i++;
+ }
- if (nonGCSlots >= CPOBJ_NONGC_SLOTS_LIMIT)
- {
- IsRepMovsProfitable = true;
- break;
- }
- } while (i < slots);
- }
- else if (slots >= CPOBJ_NONGC_SLOTS_LIMIT)
- {
- IsRepMovsProfitable = true;
- }
+ while (i < slots && gcPtrs[i] != TYPE_GC_NONE)
+ {
+ i++;
+ }
- // There are two cases in which we need to materialize the
- // struct size:
- // a) When the destination is on the stack we don't need to use the
- // write barrier, we can just simply call rep movsq and get a win in codesize.
- // b) If we determine we have contiguous non-gc regions in the struct where it's profitable
- // to use rep movsq instead of a sequence of single movsq instructions. According to the
- // Intel Manual, the sweet spot for small structs is between 4 to 12 slots of size where
- // the entire operation takes 20 cycles and encodes in 5 bytes (moving RCX, and calling rep movsq).
- if (IsRepMovsProfitable)
- {
- // We need the size of the contiguous Non-GC-region to be in RCX to call rep movsq.
- blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindRepInstr;
+ if (nonGCSlots >= CPOBJ_NONGC_SLOTS_LIMIT)
+ {
+ IsRepMovsProfitable = true;
+ break;
+ }
+ } while (i < slots);
+ }
+ else if (slots >= CPOBJ_NONGC_SLOTS_LIMIT)
+ {
+ IsRepMovsProfitable = true;
+ }
+
+ // There are two cases in which we need to materialize the
+ // struct size:
+ // a) When the destination is on the stack we don't need to use the
+ // write barrier, we can just simply call rep movsq and get a win in codesize.
+ // b) If we determine we have contiguous non-gc regions in the struct where it's profitable
+ // to use rep movsq instead of a sequence of single movsq instructions. According to the
+ // Intel Manual, the sweet spot for small structs is between 4 to 12 slots of size where
+ // the entire operation takes 20 cycles and encodes in 5 bytes (moving RCX, and calling rep movsq).
+ if (IsRepMovsProfitable)
+ {
+ // We need the size of the contiguous Non-GC-region to be in RCX to call rep movsq.
+ blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindRepInstr;
+ }
+ else
+ {
+ blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
+ }
}
else
{
- blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
- }
- }
- else
- {
- assert((blkNode->OperGet() == GT_STORE_BLK) || (blkNode->OperGet() == GT_STORE_DYN_BLK));
- // CopyBlk
- // In case of a CpBlk with a constant size and less than CPBLK_MOVS_LIMIT size
- // we can use rep movs to generate code instead of the helper call.
+ assert((blkNode->OperGet() == GT_STORE_BLK) || (blkNode->OperGet() == GT_STORE_DYN_BLK));
+ // CopyBlk
+ // In case of a CpBlk with a constant size and less than CPBLK_MOVS_LIMIT size
+ // we can use rep movs to generate code instead of the helper call.
- // This threshold will decide between using the helper or let the JIT decide to inline
- // a code sequence of its choice.
- unsigned helperThreshold = max(CPBLK_MOVS_LIMIT, CPBLK_UNROLL_LIMIT);
+ // This threshold will decide between using the helper or let the JIT decide to inline
+ // a code sequence of its choice.
+ unsigned helperThreshold = max(CPBLK_MOVS_LIMIT, CPBLK_UNROLL_LIMIT);
- // TODO-X86-CQ: Investigate whether a helper call would be beneficial on x86
- if ((size != 0) && (size <= helperThreshold))
- {
- // If we have a buffer between XMM_REGSIZE_BYTES and CPBLK_UNROLL_LIMIT bytes, we'll use SSE2.
- // Structs and buffer with sizes <= CPBLK_UNROLL_LIMIT bytes are occurring in more than 95% of
- // our framework assemblies, so this is the main code generation scheme we'll use.
- if (size <= CPBLK_UNROLL_LIMIT)
+ // TODO-X86-CQ: Investigate whether a helper call would be beneficial on x86
+ if ((size != 0) && (size <= helperThreshold))
{
- blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
+ // If we have a buffer between XMM_REGSIZE_BYTES and CPBLK_UNROLL_LIMIT bytes, we'll use SSE2.
+ // Structs and buffer with sizes <= CPBLK_UNROLL_LIMIT bytes are occurring in more than 95% of
+ // our framework assemblies, so this is the main code generation scheme we'll use.
+ if (size <= CPBLK_UNROLL_LIMIT)
+ {
+ blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindUnroll;
+
+ // If src or dst are on stack, we don't have to generate the address
+ // into a register because it's just some constant+SP.
+ if ((srcAddrOrFill != nullptr) && srcAddrOrFill->OperIsLocalAddr())
+ {
+ MakeSrcContained(blkNode, srcAddrOrFill);
+ }
+
+ if (dstAddr->OperIsLocalAddr())
+ {
+ MakeSrcContained(blkNode, dstAddr);
+ }
+ }
+ else
+ {
+ blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindRepInstr;
+ }
+ }
+#ifdef _TARGET_AMD64_
+ else
+ {
+ blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper;
}
+#elif defined(_TARGET_X86_)
else
{
blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindRepInstr;
}
+#endif // _TARGET_X86_
+ assert(blkNode->gtBlkOpKind != GenTreeBlk::BlkOpKindInvalid);
}
-#ifdef _TARGET_AMD64_
- else
+
+ // CopyObj or CopyBlk
+ if (source->gtOper == GT_IND)
{
- blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper;
+ // The GT_IND is contained, but the address must be in a register unless it is local.
+ MakeSrcContained(blkNode, source);
+ GenTree* addr = source->AsIndir()->Addr();
+ if (!addr->OperIsLocalAddr())
+ {
+ addr->ClearContained();
+ }
}
-#elif defined(_TARGET_X86_)
- else
+ else if (!source->IsMultiRegCall() && !source->OperIsSIMD())
{
- blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindRepInstr;
+ assert(source->IsLocal());
+ MakeSrcContained(blkNode, source);
}
-#endif // _TARGET_X86_
- assert(blkNode->gtBlkOpKind != GenTreeBlk::BlkOpKindInvalid);
}
}
-#ifdef FEATURE_PUT_STRUCT_ARG_STK
//------------------------------------------------------------------------
// LowerPutArgStk: Lower a GT_PUTARG_STK.
//
@@ -441,7 +517,6 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
#endif // DEBUG
head->gtLsraInfo = fieldList->gtLsraInfo;
- head->gtClearReg(comp);
BlockRange().InsertAfter(fieldList, head);
BlockRange().Remove(fieldList);
@@ -473,6 +548,38 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
putArgStk->gtNumberReferenceSlots++;
}
+ // For x86 we must mark all integral fields as contained or reg-optional, and handle them
+ // accordingly in code generation, since we may have up to 8 fields, which cannot all be in
+ // registers to be consumed atomically by the call.
+ if (varTypeIsIntegralOrI(fieldNode))
+ {
+ if (fieldNode->OperGet() == GT_LCL_VAR)
+ {
+ LclVarDsc* varDsc = &(comp->lvaTable[fieldNode->AsLclVarCommon()->gtLclNum]);
+ if (!varDsc->lvDoNotEnregister)
+ {
+ SetRegOptional(fieldNode);
+ }
+ else
+ {
+ MakeSrcContained(putArgStk, fieldNode);
+ }
+ }
+ else if (fieldNode->IsIntCnsFitsInI32())
+ {
+ MakeSrcContained(putArgStk, fieldNode);
+ }
+ else
+ {
+ // For the case where we cannot directly push the value, if we run out of registers,
+ // it would be better to defer computation until we are pushing the arguments rather
+ // than spilling, but this situation is not all that common, as most cases of promoted
+ // structs do not have a large number of fields, and of those most are lclVars or
+ // copy-propagated constants.
+ SetRegOptional(fieldNode);
+ }
+ }
+
prevOffset = fieldOffset;
}
@@ -494,15 +601,55 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
}
#endif // _TARGET_X86_
+ GenTreePtr src = putArgStk->gtOp1;
+
+#ifdef FEATURE_PUT_STRUCT_ARG_STK
if (putArgStk->TypeGet() != TYP_STRUCT)
+#endif // FEATURE_PUT_STRUCT_ARG_STK
{
+ // If the child of GT_PUTARG_STK is a constant, we don't need a register to
+ // move it to memory (stack location).
+ //
+ // On AMD64, we don't want to make 0 contained, because we can generate smaller code
+ // by zeroing a register and then storing it. E.g.:
+ // xor rdx, rdx
+ // mov gword ptr [rsp+28H], rdx
+ // is 2 bytes smaller than:
+ // mov gword ptr [rsp+28H], 0
+ //
+ // On x86, we push stack arguments; we don't use 'mov'. So:
+ // push 0
+ // is 1 byte smaller than:
+ // xor rdx, rdx
+ // push rdx
+
+ if (IsContainableImmed(putArgStk, src)
+#if defined(_TARGET_AMD64_)
+ && !src->IsIntegralConst(0)
+#endif // _TARGET_AMD64_
+ )
+ {
+ MakeSrcContained(putArgStk, src);
+ }
return;
}
+#ifdef FEATURE_PUT_STRUCT_ARG_STK
GenTreePtr dst = putArgStk;
- GenTreePtr src = putArgStk->gtOp1;
GenTreePtr srcAddr = nullptr;
+ bool haveLocalAddr = false;
+ if ((src->OperGet() == GT_OBJ) || (src->OperGet() == GT_IND))
+ {
+ srcAddr = src->gtOp.gtOp1;
+ assert(srcAddr != nullptr);
+ haveLocalAddr = srcAddr->OperIsLocalAddr();
+ }
+ else
+ {
+ assert(varTypeIsSIMD(putArgStk));
+ }
+
// In case of a CpBlk we could use a helper call. In case of putarg_stk we
// can't do that since the helper call could kill some already set up outgoing args.
// TODO-Amd64-Unix: converge the code for putarg_stk with cpyblk/cpyobj.
@@ -545,8 +692,17 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk)
{
putArgStk->gtPutArgStkKind = GenTreePutArgStk::Kind::RepInstr;
}
-}
+ // Always mark the OBJ and ADDR as contained trees by the putarg_stk. The codegen will deal with this tree.
+ MakeSrcContained(putArgStk, src);
+ if (haveLocalAddr)
+ {
+ // If the source address is the address of a lclVar, make the source address contained to avoid unnecessary
+ // copies.
+ //
+ MakeSrcContained(putArgStk, srcAddr);
+ }
#endif // FEATURE_PUT_STRUCT_ARG_STK
+}
/* Lower GT_CAST(srcType, DstType) nodes.
*
@@ -587,10 +743,10 @@ void Lowering::LowerCast(GenTree* tree)
{
assert(tree->OperGet() == GT_CAST);
- GenTreePtr op1 = tree->gtOp.gtOp1;
- var_types dstType = tree->CastToType();
- var_types srcType = op1->TypeGet();
- var_types tmpType = TYP_UNDEF;
+ GenTreePtr castOp = tree->gtCast.CastOp();
+ var_types castToType = tree->CastToType();
+ var_types srcType = castOp->TypeGet();
+ var_types tmpType = TYP_UNDEF;
// force the srcType to unsigned if GT_UNSIGNED flag is set
if (tree->gtFlags & GTF_UNSIGNED)
@@ -600,51 +756,95 @@ void Lowering::LowerCast(GenTree* tree)
// We should never see the following casts as they are expected to be lowered
// apropriately or converted into helper calls by front-end.
- // srcType = float/double dstType = * and overflow detecting cast
+ // srcType = float/double castToType = * and overflow detecting cast
// Reason: must be converted to a helper call
- // srcType = float/double, dstType = ulong
+ // srcType = float/double, castToType = ulong
// Reason: must be converted to a helper call
- // srcType = uint dstType = float/double
+ // srcType = uint castToType = float/double
// Reason: uint -> float/double = uint -> long -> float/double
- // srcType = ulong dstType = float
+ // srcType = ulong castToType = float
// Reason: ulong -> float = ulong -> double -> float
if (varTypeIsFloating(srcType))
{
noway_assert(!tree->gtOverflow());
- noway_assert(dstType != TYP_ULONG);
+ noway_assert(castToType != TYP_ULONG);
}
else if (srcType == TYP_UINT)
{
- noway_assert(!varTypeIsFloating(dstType));
+ noway_assert(!varTypeIsFloating(castToType));
}
else if (srcType == TYP_ULONG)
{
- noway_assert(dstType != TYP_FLOAT);
+ noway_assert(castToType != TYP_FLOAT);
}
// Case of src is a small type and dst is a floating point type.
- if (varTypeIsSmall(srcType) && varTypeIsFloating(dstType))
+ if (varTypeIsSmall(srcType) && varTypeIsFloating(castToType))
{
// These conversions can never be overflow detecting ones.
noway_assert(!tree->gtOverflow());
tmpType = TYP_INT;
}
// case of src is a floating point type and dst is a small type.
- else if (varTypeIsFloating(srcType) && varTypeIsSmall(dstType))
+ else if (varTypeIsFloating(srcType) && varTypeIsSmall(castToType))
{
tmpType = TYP_INT;
}
if (tmpType != TYP_UNDEF)
{
- GenTreePtr tmp = comp->gtNewCastNode(tmpType, op1, tmpType);
+ GenTreePtr tmp = comp->gtNewCastNode(tmpType, castOp, tmpType);
tmp->gtFlags |= (tree->gtFlags & (GTF_UNSIGNED | GTF_OVERFLOW | GTF_EXCEPT));
tree->gtFlags &= ~GTF_UNSIGNED;
tree->gtOp.gtOp1 = tmp;
- BlockRange().InsertAfter(op1, tmp);
+ BlockRange().InsertAfter(castOp, tmp);
+ ContainCheckCast(tmp->AsCast());
+ }
+
+ // Now determine if we have operands that should be contained.
+ ContainCheckCast(tree->AsCast());
+}
+
+#ifdef FEATURE_SIMD
+//----------------------------------------------------------------------------------------------
+// Lowering::LowerSIMD: Perform containment analysis for a SIMD intrinsic node.
+//
+// Arguments:
+// simdNode - The SIMD intrinsic node.
+//
+void Lowering::LowerSIMD(GenTreeSIMD* simdNode)
+{
+ if (simdNode->TypeGet() == TYP_SIMD12)
+ {
+ // GT_SIMD node requiring to produce TYP_SIMD12 in fact
+ // produces a TYP_SIMD16 result
+ simdNode->gtType = TYP_SIMD16;
+ }
+
+#ifdef _TARGET_XARCH_
+ if ((simdNode->gtSIMDIntrinsicID == SIMDIntrinsicGetItem) && (simdNode->gtGetOp1()->OperGet() == GT_IND))
+ {
+ // If SIMD vector is already in memory, we force its
+ // addr to be evaluated into a reg. This would allow
+ // us to generate [regBase] or [regBase+offset] or
+ // [regBase+sizeOf(SIMD vector baseType)*regIndex]
+ // to access the required SIMD vector element directly
+ // from memory.
+ //
+ // TODO-CQ-XARCH: If addr of GT_IND is GT_LEA, we
+ // might be able update GT_LEA to fold the regIndex
+ // or offset in some cases. Instead with this
+ // approach we always evaluate GT_LEA into a reg.
+ // Ideally, we should be able to lower GetItem intrinsic
+ // into GT_IND(newAddr) where newAddr combines
+ // the addr of SIMD vector with the given index.
+ simdNode->gtOp1->gtFlags |= GTF_IND_REQ_ADDR_IN_REG;
}
+#endif
+ ContainCheckSIMD(simdNode);
}
+#endif // FEATURE_SIMD
//----------------------------------------------------------------------------------------------
// Lowering::IsRMWIndirCandidate:
@@ -905,8 +1105,7 @@ bool Lowering::IsRMWMemOpRootedAtStoreInd(GenTreePtr tree, GenTreePtr* outIndirC
if (GenTree::OperIsBinary(oper))
{
// Return if binary op is not one of the supported operations for RMW of memory.
- if (oper != GT_ADD && oper != GT_SUB && oper != GT_AND && oper != GT_OR && oper != GT_XOR &&
- !GenTree::OperIsShiftOrRotate(oper))
+ if (!GenTree::OperIsRMWMemOp(oper))
{
storeInd->SetRMWStatus(STOREIND_RMW_UNSUPPORTED_OPER);
return false;
@@ -994,48 +1193,6 @@ bool Lowering::IsRMWMemOpRootedAtStoreInd(GenTreePtr tree, GenTreePtr* outIndirC
return true;
}
-//------------------------------------------------------------------------------
-// isRMWRegOper: Can this binary tree node be used in a Read-Modify-Write format
-//
-// Arguments:
-// tree - a binary tree node
-//
-// Return Value:
-// Returns true if we can use the read-modify-write instruction form
-//
-// Notes:
-// This is used to determine whether to preference the source to the destination register.
-//
-bool Lowering::isRMWRegOper(GenTreePtr tree)
-{
- // TODO-XArch-CQ: Make this more accurate.
- // For now, We assume that most binary operators are of the RMW form.
- assert(tree->OperIsBinary());
-
- if (tree->OperIsCompare() || tree->OperIs(GT_CMP))
- {
- return false;
- }
-
- switch (tree->OperGet())
- {
- // These Opers either support a three op form (i.e. GT_LEA), or do not read/write their first operand
- case GT_LEA:
- case GT_STOREIND:
- case GT_ARR_INDEX:
- case GT_STORE_BLK:
- case GT_STORE_OBJ:
- return false;
-
- // x86/x64 does support a three op multiply when op2|op1 is a contained immediate
- case GT_MUL:
- return (!IsContainableImmed(tree, tree->gtOp.gtOp2) && !IsContainableImmed(tree, tree->gtOp.gtOp1));
-
- default:
- return true;
- }
-}
-
// anything is in range for AMD64
bool Lowering::IsCallTargetInRange(void* addr)
{
@@ -1088,15 +1245,19 @@ GenTree* Lowering::PreferredRegOptionalOperand(GenTree* tree)
assert(GenTree::OperIsBinary(tree->OperGet()));
assert(tree->OperIsCommutative() || tree->OperIsCompare() || tree->OperIs(GT_CMP));
- GenTree* op1 = tree->gtGetOp1();
- GenTree* op2 = tree->gtGetOp2();
- GenTree* preferredOp = nullptr;
+ GenTree* op1 = tree->gtGetOp1();
+ GenTree* op2 = tree->gtGetOp2();
+ assert(!op1->IsRegOptional() && !op2->IsRegOptional());
+
+ // We default to op1, as op2 is likely to have the shorter lifetime.
+ GenTree* preferredOp = op1;
// This routine uses the following heuristics:
//
// a) If both are register candidates, marking the one with lower weighted
// ref count as reg-optional would likely be beneficial as it has
- // higher probability of not getting a register.
+ // higher probability of not getting a register. Note that we use !lvDoNotEnregister
+ // here because this is being done while we are adding lclVars for Lowering.
//
// b) op1 = tracked local and op2 = untracked local: LSRA creates two
// ref positions for op2: a def and use position. op2's def position
@@ -1131,51 +1292,25 @@ GenTree* Lowering::PreferredRegOptionalOperand(GenTree* tree)
LclVarDsc* v1 = comp->lvaTable + op1->AsLclVarCommon()->GetLclNum();
LclVarDsc* v2 = comp->lvaTable + op2->AsLclVarCommon()->GetLclNum();
- bool v1IsRegCandidate = !v1->lvDoNotEnregister && v1->lvTracked;
- bool v2IsRegCandidate = !v2->lvDoNotEnregister && v2->lvTracked;
+ bool v1IsRegCandidate = !v1->lvDoNotEnregister;
+ bool v2IsRegCandidate = !v2->lvDoNotEnregister;
if (v1IsRegCandidate && v2IsRegCandidate)
{
- // Both are tracked enregisterable locals. The one with lower weight is less likely
+ // Both are enregisterable locals. The one with lower weight is less likely
// to get a register and hence beneficial to mark the one with lower
// weight as reg optional.
- if (v1->lvRefCntWtd < v2->lvRefCntWtd)
- {
- preferredOp = op1;
- }
- else
+ // If either is not tracked, it may be that it was introduced after liveness
+ // was run, in which case we will always prefer op1 (should we use raw refcnt??).
+ if (v1->lvTracked && v2->lvTracked && (v1->lvRefCntWtd >= v2->lvRefCntWtd))
{
preferredOp = op2;
}
}
- else if (v2IsRegCandidate)
- {
- // v1 is not a reg candidate and its use position is less likely to get a register.
- preferredOp = op1;
- }
- else if (v1IsRegCandidate)
- {
- // v2 is not a reg candidate and its def position always
- // needs a reg. Hence it is better to mark v1 as
- // reg optional.
- preferredOp = op1;
- }
- else
- {
- preferredOp = op1;
- }
- }
- else if (op1->OperGet() == GT_LCL_VAR)
- {
- preferredOp = op1;
}
- else if (op2->OperGet() == GT_LCL_VAR)
+ else if (!(op1->OperGet() == GT_LCL_VAR) && (op2->OperGet() == GT_LCL_VAR))
{
preferredOp = op2;
}
- else
- {
- preferredOp = op1;
- }
return preferredOp;
}
@@ -1185,121 +1320,102 @@ GenTree* Lowering::PreferredRegOptionalOperand(GenTree* tree)
//------------------------------------------------------------------------
//------------------------------------------------------------------------
-// LowerRMWMemOp: Determine if this is a valid RMW mem op, and if so lower it accordingly
+// ContainCheckCallOperands: Determine whether operands of a call should be contained.
//
// Arguments:
-// node - The indirect store node (GT_STORE_IND) of interest
+// call - The call node of interest
//
// Return Value:
-// Returns true if 'node' is a valid RMW mem op; false otherwise.
+// None.
//
-bool Lowering::LowerRMWMemOp(GenTreeIndir* storeInd)
+void Lowering::ContainCheckCallOperands(GenTreeCall* call)
{
- assert(storeInd->OperGet() == GT_STOREIND);
-
- // SSE2 doesn't support RMW on float values
- assert(!varTypeIsFloating(storeInd));
-
- // Terminology:
- // indirDst = memory write of an addr mode (i.e. storeind destination)
- // indirSrc = value being written to memory (i.e. storeind source which could a binary/unary op)
- // indirCandidate = memory read i.e. a gtInd of an addr mode
- // indirOpSource = source operand used in binary/unary op (i.e. source operand of indirSrc node)
-
- GenTreePtr indirCandidate = nullptr;
- GenTreePtr indirOpSource = nullptr;
-
- if (!IsRMWMemOpRootedAtStoreInd(storeInd, &indirCandidate, &indirOpSource))
+ GenTree* ctrlExpr = call->gtControlExpr;
+ if (call->gtCallType == CT_INDIRECT)
{
- JITDUMP("Lower of StoreInd didn't mark the node as self contained for reason: %d\n",
- storeInd->AsStoreInd()->GetRMWStatus());
- DISPTREERANGE(BlockRange(), storeInd);
- return false;
- }
+ // either gtControlExpr != null or gtCallAddr != null.
+ // Both cannot be non-null at the same time.
+ assert(ctrlExpr == nullptr);
+ assert(call->gtCallAddr != nullptr);
+ ctrlExpr = call->gtCallAddr;
- GenTreePtr indirDst = storeInd->gtGetOp1();
- GenTreePtr indirSrc = storeInd->gtGetOp2();
- genTreeOps oper = indirSrc->OperGet();
-
- // At this point we have successfully detected a RMW memory op of one of the following forms
- // storeInd(indirDst, indirSrc(indirCandidate, indirOpSource)) OR
- // storeInd(indirDst, indirSrc(indirOpSource, indirCandidate) in case of commutative operations OR
- // storeInd(indirDst, indirSrc(indirCandidate) in case of unary operations
- //
- // Here indirSrc = one of the supported binary or unary operation for RMW of memory
- // indirCandidate = a GT_IND node
- // indirCandidateChild = operand of GT_IND indirCandidate
- //
- // The logic below does the following
- // Make indirOpSource contained.
- // Make indirSrc contained.
- // Make indirCandidate contained.
- // Make indirCandidateChild contained.
- // Make indirDst contained except when it is a GT_LCL_VAR or GT_CNS_INT that doesn't fit within addr
- // base.
- //
+#ifdef _TARGET_X86_
+ // Fast tail calls aren't currently supported on x86, but if they ever are, the code
+ // below that handles indirect VSD calls will need to be fixed.
+ assert(!call->IsFastTailCall() || !call->IsVirtualStub());
+#endif // _TARGET_X86_
+ }
- if (GenTree::OperIsBinary(oper))
+ // set reg requirements on call target represented as control sequence.
+ if (ctrlExpr != nullptr)
{
- // On Xarch RMW operations require the source to be an immediate or in a register.
- // Therefore, if we have previously marked the indirOpSource as contained while lowering
- // the binary node, we need to reset that now.
- if (IsContainableMemoryOp(indirOpSource, true))
+ // we should never see a gtControlExpr whose type is void.
+ assert(ctrlExpr->TypeGet() != TYP_VOID);
+
+ // In case of fast tail implemented as jmp, make sure that gtControlExpr is
+ // computed into a register.
+ if (!call->IsFastTailCall())
{
- indirOpSource->ClearContained();
+#ifdef _TARGET_X86_
+ // On x86, we need to generate a very specific pattern for indirect VSD calls:
+ //
+ // 3-byte nop
+ // call dword ptr [eax]
+ //
+ // Where EAX is also used as an argument to the stub dispatch helper. Make
+ // sure that the call target address is computed into EAX in this case.
+ if (call->IsVirtualStub() && (call->gtCallType == CT_INDIRECT))
+ {
+ assert(ctrlExpr->isIndir());
+ MakeSrcContained(call, ctrlExpr);
+ }
+ else
+#endif // _TARGET_X86_
+ if (ctrlExpr->isIndir())
+ {
+ MakeSrcContained(call, ctrlExpr);
+ // We may have cases where we have set a register target on the ctrlExpr, but if it
+ // contained we must clear it.
+ ctrlExpr->gtRegNum = REG_NA;
+ }
}
- JITDUMP("Lower succesfully detected an assignment of the form: *addrMode BinOp= source\n");
}
- else
+ // If there is an explicit this pointer, we don't want that node to produce anything
+ // as it is redundant
+ if (call->gtCallObjp != nullptr)
{
- assert(GenTree::OperIsUnary(oper));
- JITDUMP("Lower succesfully detected an assignment of the form: *addrMode = UnaryOp(*addrMode)\n");
- }
- DISPTREERANGE(BlockRange(), storeInd);
-
- indirSrc->SetContained();
- indirCandidate->SetContained();
-
- GenTreePtr indirCandidateChild = indirCandidate->gtGetOp1();
- indirCandidateChild->SetContained();
+ GenTreePtr thisPtrNode = call->gtCallObjp;
- if (indirCandidateChild->OperGet() == GT_LEA)
- {
- GenTreeAddrMode* addrMode = indirCandidateChild->AsAddrMode();
-
- if (addrMode->HasBase())
+ if (thisPtrNode->canBeContained())
{
- assert(addrMode->Base()->OperIsLeaf());
- addrMode->Base()->SetContained();
+ MakeSrcContained(call, thisPtrNode);
+ if (thisPtrNode->gtOper == GT_PUTARG_REG)
+ {
+ MakeSrcContained(call, thisPtrNode->gtOp.gtOp1);
+ }
}
+ }
- if (addrMode->HasIndex())
+ GenTree* args = call->gtCallArgs;
+ while (args)
+ {
+ GenTree* arg = args->gtOp.gtOp1;
+ if (arg->gtOper == GT_PUTARG_STK)
{
- assert(addrMode->Index()->OperIsLeaf());
- addrMode->Index()->SetContained();
+ LowerPutArgStk(arg->AsPutArgStk());
}
-
- indirDst->SetContained();
+ args = args->gtOp.gtOp2;
}
- else
+ args = call->gtCallLateArgs;
+ while (args)
{
- assert(indirCandidateChild->OperGet() == GT_LCL_VAR || indirCandidateChild->OperGet() == GT_LCL_VAR_ADDR ||
- indirCandidateChild->OperGet() == GT_CLS_VAR_ADDR || indirCandidateChild->OperGet() == GT_CNS_INT);
-
- // If it is a GT_LCL_VAR, it still needs the reg to hold the address.
- // We would still need a reg for GT_CNS_INT if it doesn't fit within addressing mode base.
- // For GT_CLS_VAR_ADDR, we don't need a reg to hold the address, because field address value is known at jit
- // time. Also, we don't need a reg for GT_CLS_VAR_ADDR.
- if (indirCandidateChild->OperGet() == GT_LCL_VAR_ADDR || indirCandidateChild->OperGet() == GT_CLS_VAR_ADDR)
+ GenTree* arg = args->gtOp.gtOp1;
+ if (arg->gtOper == GT_PUTARG_STK)
{
- indirDst->SetContained();
- }
- else if (indirCandidateChild->IsCnsIntOrI() && indirCandidateChild->AsIntConCommon()->FitsInAddrBase(comp))
- {
- indirDst->SetContained();
+ LowerPutArgStk(arg->AsPutArgStk());
}
+ args = args->gtOp.gtOp2;
}
- return true;
}
//------------------------------------------------------------------------
@@ -1380,74 +1496,23 @@ void Lowering::ContainCheckIndir(GenTreeIndir* node)
}
//------------------------------------------------------------------------
-// ContainCheckBinary: Determine whether a binary op's operands should be contained.
+// ContainCheckStoreIndir: determine whether the sources of a STOREIND node should be contained.
//
// Arguments:
-// node - the node we care about
+// node - pointer to the node
//
-void Lowering::ContainCheckBinary(GenTreeOp* node)
+void Lowering::ContainCheckStoreIndir(GenTreeIndir* node)
{
- assert(node->OperIsBinary() && !varTypeIsFloating(node));
-
- // We're not marking a constant hanging on the left of an add
- // as containable so we assign it to a register having CQ impact.
- // TODO-XArch-CQ: Detect this case and support both generating a single instruction
- // for GT_ADD(Constant, SomeTree)
-
- GenTree* op1 = node->gtOp1;
- GenTree* op2 = node->gtOp2;
-
- // We can directly encode the second operand if it is either a containable constant or a memory-op.
- // In case of memory-op, we can encode it directly provided its type matches with 'tree' type.
- // This is because during codegen, type of 'tree' is used to determine emit Type size. If the types
- // do not match, they get normalized (i.e. sign/zero extended) on load into a register.
- bool directlyEncodable = false;
- bool binOpInRMW = false;
- GenTreePtr operand = nullptr;
-
- if (IsContainableImmed(node, op2))
- {
- directlyEncodable = true;
- operand = op2;
- }
- else
- {
- binOpInRMW = IsBinOpInRMWStoreInd(node);
- if (!binOpInRMW)
- {
- const unsigned operatorSize = genTypeSize(node->TypeGet());
- if (IsContainableMemoryOp(op2, true) && (genTypeSize(op2->TypeGet()) == operatorSize))
- {
- directlyEncodable = true;
- operand = op2;
- }
- else if (node->OperIsCommutative())
- {
- if (IsContainableImmed(node, op1) ||
- (IsContainableMemoryOp(op1, true) && (genTypeSize(op1->TypeGet()) == operatorSize) &&
- IsSafeToContainMem(node, op1)))
- {
- // If it is safe, we can reverse the order of operands of commutative operations for efficient
- // codegen
- directlyEncodable = true;
- operand = op1;
- }
- }
- }
- }
-
- if (directlyEncodable)
- {
- assert(operand != nullptr);
- MakeSrcContained(node, operand);
- }
- else if (!binOpInRMW)
+ // If the source is a containable immediate, make it contained, unless it is
+ // an int-size or larger store of zero to memory, because we can generate smaller code
+ // by zeroing a register and then storing it.
+ GenTree* src = node->gtOp.gtOp2;
+ if (IsContainableImmed(node, src) &&
+ (!src->IsIntegralConst(0) || varTypeIsSmall(node) || node->gtGetOp1()->OperGet() == GT_CLS_VAR_ADDR))
{
- // If this binary op neither has contained operands, nor is a
- // Read-Modify-Write (RMW) operation, we can mark its operands
- // as reg optional.
- SetRegOptionalForBinOp(node);
+ MakeSrcContained(node, src);
}
+ ContainCheckIndir(node);
}
//------------------------------------------------------------------------
@@ -1471,11 +1536,11 @@ void Lowering::ContainCheckMul(GenTreeOp* node)
{
assert(node->OperGet() == GT_MUL);
- if (IsContainableMemoryOp(op2, true) || op2->IsCnsNonZeroFltOrDbl())
+ if (IsContainableMemoryOp(op2) || op2->IsCnsNonZeroFltOrDbl())
{
MakeSrcContained(node, op2);
}
- else if (op1->IsCnsNonZeroFltOrDbl() || (IsContainableMemoryOp(op1, true) && IsSafeToContainMem(node, op1)))
+ else if (op1->IsCnsNonZeroFltOrDbl() || (IsContainableMemoryOp(op1) && IsSafeToContainMem(node, op1)))
{
// Since GT_MUL is commutative, we will try to re-order operands if it is safe to
// generate more efficient code sequence for the case of GT_MUL(op1=memOp, op2=non-memOp)
@@ -1539,7 +1604,7 @@ void Lowering::ContainCheckMul(GenTreeOp* node)
}
MakeSrcContained(node, imm); // The imm is always contained
- if (IsContainableMemoryOp(other, true))
+ if (IsContainableMemoryOp(other))
{
memOp = other; // memOp may be contained below
}
@@ -1552,12 +1617,11 @@ void Lowering::ContainCheckMul(GenTreeOp* node)
//
if (memOp == nullptr)
{
- if (IsContainableMemoryOp(op2, true) && (op2->TypeGet() == node->TypeGet()) && IsSafeToContainMem(node, op2))
+ if (IsContainableMemoryOp(op2) && (op2->TypeGet() == node->TypeGet()) && IsSafeToContainMem(node, op2))
{
memOp = op2;
}
- else if (IsContainableMemoryOp(op1, true) && (op1->TypeGet() == node->TypeGet()) &&
- IsSafeToContainMem(node, op1))
+ else if (IsContainableMemoryOp(op1) && (op1->TypeGet() == node->TypeGet()) && IsSafeToContainMem(node, op1))
{
memOp = op1;
}
@@ -1699,7 +1763,7 @@ void Lowering::ContainCheckCast(GenTreeCast* node)
// U8 -> R8 conversion requires that the operand be in a register.
if (srcType != TYP_ULONG)
{
- if (IsContainableMemoryOp(castOp, true) || castOp->IsCnsNonZeroFltOrDbl())
+ if (IsContainableMemoryOp(castOp) || castOp->IsCnsNonZeroFltOrDbl())
{
MakeSrcContained(node, castOp);
}
@@ -1774,7 +1838,7 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp)
{
MakeSrcContained(cmp, otherOp);
}
- else if (IsContainableMemoryOp(otherOp, true) && ((otherOp == op2) || IsSafeToContainMem(cmp, otherOp)))
+ else if (IsContainableMemoryOp(otherOp) && ((otherOp == op2) || IsSafeToContainMem(cmp, otherOp)))
{
MakeSrcContained(cmp, otherOp);
}
@@ -1797,7 +1861,7 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp)
// we can treat the MemoryOp as contained.
if (op1Type == op2Type)
{
- if (IsContainableMemoryOp(op1, true))
+ if (IsContainableMemoryOp(op1))
{
MakeSrcContained(cmp, op1);
}
@@ -1834,6 +1898,7 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp)
// Require codegen of op1 to set the flags.
assert(!op1->gtSetFlags());
op1->gtFlags |= GTF_SET_FLAGS;
+ cmp->gtFlags |= GTF_USE_FLAGS;
}
else
{
@@ -1846,11 +1911,11 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp)
// Note that TEST does not have a r,rm encoding like CMP has but we can still
// contain the second operand because the emitter maps both r,rm and rm,r to
// the same instruction code. This avoids the need to special case TEST here.
- if (IsContainableMemoryOp(op2, true))
+ if (IsContainableMemoryOp(op2))
{
MakeSrcContained(cmp, op2);
}
- else if (IsContainableMemoryOp(op1, true) && IsSafeToContainMem(cmp, op1))
+ else if (IsContainableMemoryOp(op1) && IsSafeToContainMem(cmp, op1))
{
MakeSrcContained(cmp, op1);
}
@@ -1872,72 +1937,206 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp)
}
//------------------------------------------------------------------------
-// ContainCheckFloatBinary: determine whether the sources of a floating point binary node should be contained.
+// LowerRMWMemOp: Determine if this is a valid RMW mem op, and if so lower it accordingly
//
// Arguments:
-// node - pointer to the node
+// node - The indirect store node (GT_STORE_IND) of interest
//
-void Lowering::ContainCheckFloatBinary(GenTreeOp* node)
+// Return Value:
+// Returns true if 'node' is a valid RMW mem op; false otherwise.
+//
+bool Lowering::LowerRMWMemOp(GenTreeIndir* storeInd)
{
- assert(node->OperIsBinary() && varTypeIsFloating(node));
+ assert(storeInd->OperGet() == GT_STOREIND);
- // overflow operations aren't supported on float/double types.
- assert(!node->gtOverflow());
+ // SSE2 doesn't support RMW on float values
+ assert(!varTypeIsFloating(storeInd));
- GenTree* op1 = node->gtGetOp1();
- GenTree* op2 = node->gtGetOp2();
+ // Terminology:
+ // indirDst = memory write of an addr mode (i.e. storeind destination)
+ // indirSrc = value being written to memory (i.e. storeind source which could a binary/unary op)
+ // indirCandidate = memory read i.e. a gtInd of an addr mode
+ // indirOpSource = source operand used in binary/unary op (i.e. source operand of indirSrc node)
- // No implicit conversions at this stage as the expectation is that
- // everything is made explicit by adding casts.
- assert(op1->TypeGet() == op2->TypeGet());
+ GenTreePtr indirCandidate = nullptr;
+ GenTreePtr indirOpSource = nullptr;
- if (IsContainableMemoryOp(op2, true) || op2->IsCnsNonZeroFltOrDbl())
+ if (!IsRMWMemOpRootedAtStoreInd(storeInd, &indirCandidate, &indirOpSource))
{
- MakeSrcContained(node, op2);
+ JITDUMP("Lower of StoreInd didn't mark the node as self contained for reason: %d\n",
+ storeInd->AsStoreInd()->GetRMWStatus());
+ DISPTREERANGE(BlockRange(), storeInd);
+ return false;
}
- else if (node->OperIsCommutative() &&
- (op1->IsCnsNonZeroFltOrDbl() || (IsContainableMemoryOp(op1, true) && IsSafeToContainMem(node, op1))))
+
+ GenTreePtr indirDst = storeInd->gtGetOp1();
+ GenTreePtr indirSrc = storeInd->gtGetOp2();
+ genTreeOps oper = indirSrc->OperGet();
+
+ // At this point we have successfully detected a RMW memory op of one of the following forms
+ // storeInd(indirDst, indirSrc(indirCandidate, indirOpSource)) OR
+ // storeInd(indirDst, indirSrc(indirOpSource, indirCandidate) in case of commutative operations OR
+ // storeInd(indirDst, indirSrc(indirCandidate) in case of unary operations
+ //
+ // Here indirSrc = one of the supported binary or unary operation for RMW of memory
+ // indirCandidate = a GT_IND node
+ // indirCandidateChild = operand of GT_IND indirCandidate
+ //
+ // The logic below does the following
+ // Make indirOpSource contained.
+ // Make indirSrc contained.
+ // Make indirCandidate contained.
+ // Make indirCandidateChild contained.
+ // Make indirDst contained except when it is a GT_LCL_VAR or GT_CNS_INT that doesn't fit within addr
+ // base.
+ //
+
+ // We have already done containment analysis on the indirSrc op.
+ // If any of its operands are marked regOptional, reset that now.
+ indirSrc->AsOp()->gtOp1->ClearRegOptional();
+ if (GenTree::OperIsBinary(oper))
{
- // Though we have GT_ADD(op1=memOp, op2=non-memOp, we try to reorder the operands
- // as long as it is safe so that the following efficient code sequence is generated:
- // addss/sd targetReg, memOp (if op1Reg == targetReg) OR
- // movaps targetReg, op2Reg; addss/sd targetReg, [memOp]
- //
- // Instead of
- // movss op1Reg, [memOp]; addss/sd targetReg, Op2Reg (if op1Reg == targetReg) OR
- // movss op1Reg, [memOp]; movaps targetReg, op1Reg, addss/sd targetReg, Op2Reg
- MakeSrcContained(node, op1);
+ // On Xarch RMW operations require the source to be an immediate or in a register.
+ // Therefore, if we have previously marked the indirOpSource as contained while lowering
+ // the binary node, we need to reset that now.
+ if (IsContainableMemoryOp(indirOpSource))
+ {
+ indirOpSource->ClearContained();
+ }
+ indirSrc->AsOp()->gtOp2->ClearRegOptional();
+ JITDUMP("Lower succesfully detected an assignment of the form: *addrMode BinOp= source\n");
}
else
{
- // If there are no containable operands, we can make an operand reg optional.
- SetRegOptionalForBinOp(node);
+ assert(GenTree::OperIsUnary(oper));
+ JITDUMP("Lower succesfully detected an assignment of the form: *addrMode = UnaryOp(*addrMode)\n");
+ }
+ DISPTREERANGE(BlockRange(), storeInd);
+
+ indirSrc->SetContained();
+ indirCandidate->SetContained();
+
+ GenTreePtr indirCandidateChild = indirCandidate->gtGetOp1();
+ indirCandidateChild->SetContained();
+
+ if (indirCandidateChild->OperGet() == GT_LEA)
+ {
+ GenTreeAddrMode* addrMode = indirCandidateChild->AsAddrMode();
+
+ if (addrMode->HasBase())
+ {
+ assert(addrMode->Base()->OperIsLeaf());
+ addrMode->Base()->SetContained();
+ }
+
+ if (addrMode->HasIndex())
+ {
+ assert(addrMode->Index()->OperIsLeaf());
+ addrMode->Index()->SetContained();
+ }
+
+ indirDst->SetContained();
+ }
+ else
+ {
+ assert(indirCandidateChild->OperGet() == GT_LCL_VAR || indirCandidateChild->OperGet() == GT_LCL_VAR_ADDR ||
+ indirCandidateChild->OperGet() == GT_CLS_VAR_ADDR || indirCandidateChild->OperGet() == GT_CNS_INT);
+
+ // If it is a GT_LCL_VAR, it still needs the reg to hold the address.
+ // We would still need a reg for GT_CNS_INT if it doesn't fit within addressing mode base.
+ // For GT_CLS_VAR_ADDR, we don't need a reg to hold the address, because field address value is known at jit
+ // time. Also, we don't need a reg for GT_CLS_VAR_ADDR.
+ if (indirCandidateChild->OperGet() == GT_LCL_VAR_ADDR || indirCandidateChild->OperGet() == GT_CLS_VAR_ADDR)
+ {
+ indirDst->SetContained();
+ }
+ else if (indirCandidateChild->IsCnsIntOrI() && indirCandidateChild->AsIntConCommon()->FitsInAddrBase(comp))
+ {
+ indirDst->SetContained();
+ }
}
+ return true;
}
//------------------------------------------------------------------------
-// ContainCheckIntrinsic: determine whether the source of an INTRINSIC node should be contained.
+// ContainCheckBinary: Determine whether a binary op's operands should be contained.
//
// Arguments:
-// node - pointer to the node
+// node - the node we care about
//
-void Lowering::ContainCheckIntrinsic(GenTreeOp* node)
+void Lowering::ContainCheckBinary(GenTreeOp* node)
{
- assert(node->OperIs(GT_INTRINSIC));
- if (node->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Sqrt)
+ assert(node->OperIsBinary());
+
+ if (varTypeIsFloating(node))
{
- GenTree* op1 = node->gtGetOp1();
- if (IsContainableMemoryOp(op1, true) || op1->IsCnsNonZeroFltOrDbl())
- {
- MakeSrcContained(node, op1);
- }
- else
+ assert(node->OperIs(GT_ADD, GT_SUB));
+ ContainCheckFloatBinary(node);
+ return;
+ }
+
+ // Codegen of these tree nodes sets ZF and SF flags.
+ node->gtFlags |= GTF_ZSF_SET;
+
+ // We're not marking a constant hanging on the left of an add
+ // as containable so we assign it to a register having CQ impact.
+ // TODO-XArch-CQ: Detect this case and support both generating a single instruction
+ // for GT_ADD(Constant, SomeTree)
+
+ GenTree* op1 = node->gtOp1;
+ GenTree* op2 = node->gtOp2;
+
+ // We can directly encode the second operand if it is either a containable constant or a memory-op.
+ // In case of memory-op, we can encode it directly provided its type matches with 'tree' type.
+ // This is because during codegen, type of 'tree' is used to determine emit Type size. If the types
+ // do not match, they get normalized (i.e. sign/zero extended) on load into a register.
+ bool directlyEncodable = false;
+ bool binOpInRMW = false;
+ GenTreePtr operand = nullptr;
+
+ if (IsContainableImmed(node, op2))
+ {
+ directlyEncodable = true;
+ operand = op2;
+ }
+ else
+ {
+ binOpInRMW = IsBinOpInRMWStoreInd(node);
+ if (!binOpInRMW)
{
- // Mark the operand as reg optional since codegen can still
- // generate code if op1 is on stack.
- SetRegOptional(op1);
+ const unsigned operatorSize = genTypeSize(node->TypeGet());
+ if (IsContainableMemoryOp(op2) && (genTypeSize(op2->TypeGet()) == operatorSize))
+ {
+ directlyEncodable = true;
+ operand = op2;
+ }
+ else if (node->OperIsCommutative())
+ {
+ if (IsContainableImmed(node, op1) ||
+ (IsContainableMemoryOp(op1) && (genTypeSize(op1->TypeGet()) == operatorSize) &&
+ IsSafeToContainMem(node, op1)))
+ {
+ // If it is safe, we can reverse the order of operands of commutative operations for efficient
+ // codegen
+ directlyEncodable = true;
+ operand = op1;
+ }
+ }
}
}
+
+ if (directlyEncodable)
+ {
+ assert(operand != nullptr);
+ MakeSrcContained(node, operand);
+ }
+ else if (!binOpInRMW)
+ {
+ // If this binary op neither has contained operands, nor is a
+ // Read-Modify-Write (RMW) operation, we can mark its operands
+ // as reg optional.
+ SetRegOptionalForBinOp(node);
+ }
}
//------------------------------------------------------------------------
@@ -1958,7 +2157,7 @@ void Lowering::ContainCheckBoundsChk(GenTreeBoundsChk* node)
{
other = node->gtIndex;
}
- else if (IsContainableMemoryOp(node->gtIndex, true))
+ else if (IsContainableMemoryOp(node->gtIndex))
{
other = node->gtIndex;
}
@@ -1969,7 +2168,7 @@ void Lowering::ContainCheckBoundsChk(GenTreeBoundsChk* node)
if (node->gtIndex->TypeGet() == node->gtArrLen->TypeGet())
{
- if (IsContainableMemoryOp(other, true))
+ if (IsContainableMemoryOp(other))
{
MakeSrcContained(node, other);
}
@@ -1981,6 +2180,31 @@ void Lowering::ContainCheckBoundsChk(GenTreeBoundsChk* node)
}
}
+//------------------------------------------------------------------------
+// ContainCheckIntrinsic: determine whether the source of an INTRINSIC node should be contained.
+//
+// Arguments:
+// node - pointer to the node
+//
+void Lowering::ContainCheckIntrinsic(GenTreeOp* node)
+{
+ assert(node->OperIs(GT_INTRINSIC));
+ if (node->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Sqrt)
+ {
+ GenTree* op1 = node->gtGetOp1();
+ if (IsContainableMemoryOp(op1) || op1->IsCnsNonZeroFltOrDbl())
+ {
+ MakeSrcContained(node, op1);
+ }
+ else
+ {
+ // Mark the operand as reg optional since codegen can still
+ // generate code if op1 is on stack.
+ SetRegOptional(op1);
+ }
+ }
+}
+
#ifdef FEATURE_SIMD
//----------------------------------------------------------------------------------------------
// ContainCheckSIMD: Perform containment analysis for a SIMD intrinsic node.
@@ -2066,7 +2290,7 @@ void Lowering::ContainCheckSIMD(GenTreeSIMD* simdNode)
// If the index is a constant, mark it as contained.
CheckImmedAndMakeContained(simdNode, op2);
- if (IsContainableMemoryOp(op1, true))
+ if (IsContainableMemoryOp(op1))
{
MakeSrcContained(simdNode, op1);
if (op1->OperGet() == GT_IND)
@@ -2089,6 +2313,50 @@ void Lowering::ContainCheckSIMD(GenTreeSIMD* simdNode)
}
#endif // FEATURE_SIMD
+//------------------------------------------------------------------------
+// ContainCheckFloatBinary: determine whether the sources of a floating point binary node should be contained.
+//
+// Arguments:
+// node - pointer to the node
+//
+void Lowering::ContainCheckFloatBinary(GenTreeOp* node)
+{
+ assert(node->OperIsBinary() && varTypeIsFloating(node));
+
+ // overflow operations aren't supported on float/double types.
+ assert(!node->gtOverflow());
+
+ GenTree* op1 = node->gtGetOp1();
+ GenTree* op2 = node->gtGetOp2();
+
+ // No implicit conversions at this stage as the expectation is that
+ // everything is made explicit by adding casts.
+ assert(op1->TypeGet() == op2->TypeGet());
+
+ if (IsContainableMemoryOp(op2) || op2->IsCnsNonZeroFltOrDbl())
+ {
+ MakeSrcContained(node, op2);
+ }
+ else if (node->OperIsCommutative() &&
+ (op1->IsCnsNonZeroFltOrDbl() || (IsContainableMemoryOp(op1) && IsSafeToContainMem(node, op1))))
+ {
+ // Though we have GT_ADD(op1=memOp, op2=non-memOp, we try to reorder the operands
+ // as long as it is safe so that the following efficient code sequence is generated:
+ // addss/sd targetReg, memOp (if op1Reg == targetReg) OR
+ // movaps targetReg, op2Reg; addss/sd targetReg, [memOp]
+ //
+ // Instead of
+ // movss op1Reg, [memOp]; addss/sd targetReg, Op2Reg (if op1Reg == targetReg) OR
+ // movss op1Reg, [memOp]; movaps targetReg, op1Reg, addss/sd targetReg, Op2Reg
+ MakeSrcContained(node, op1);
+ }
+ else
+ {
+ // If there are no containable operands, we can make an operand reg optional.
+ SetRegOptionalForBinOp(node);
+ }
+}
+
#endif // _TARGET_XARCH_
#endif // !LEGACY_BACKEND
diff --git a/src/jit/lsra.cpp b/src/jit/lsra.cpp
index 28617b9583..47e6d327e3 100644
--- a/src/jit/lsra.cpp
+++ b/src/jit/lsra.cpp
@@ -367,6 +367,7 @@ bool RegRecord::isFree()
*****************************************************************************/
RegRecord* LinearScan::getRegisterRecord(regNumber regNum)
{
+ assert((unsigned)regNum < ArrLen(physRegs));
return &physRegs[regNum];
}
@@ -1939,6 +1940,44 @@ void LinearScan::identifyCandidatesExceptionDataflow()
}
}
+//------------------------------------------------------------------------
+// IsContainableMemoryOp: Checks whether this is a memory op that can be contained.
+//
+// Arguments:
+// node - the node of interest.
+//
+// Return value:
+// True if this will definitely be a memory reference that could be contained.
+//
+// Notes:
+// This differs from the isMemoryOp() method on GenTree because it checks for
+// the case of doNotEnregister local. This won't include locals that
+// for some other reason do not become register candidates, nor those that get
+// spilled.
+// Also, because we usually call this before we redo dataflow, any new lclVars
+// introduced after the last dataflow analysis will not yet be marked lvTracked,
+// so we don't use that.
+//
+bool LinearScan::isContainableMemoryOp(GenTree* node)
+{
+#ifdef _TARGET_XARCH_
+ if (node->isMemoryOp())
+ {
+ return true;
+ }
+ if (node->IsLocal())
+ {
+ if (!enregisterLocalVars)
+ {
+ return true;
+ }
+ LclVarDsc* varDsc = &compiler->lvaTable[node->AsLclVar()->gtLclNum];
+ return varDsc->lvDoNotEnregister;
+ }
+#endif // _TARGET_XARCH_
+ return false;
+}
+
bool LinearScan::isRegCandidate(LclVarDsc* varDsc)
{
// We shouldn't be called if opt settings do not permit register variables.
@@ -3517,6 +3556,15 @@ static int ComputeOperandDstCount(GenTree* operand)
// pointers to argument setup stores.
return 0;
}
+#ifdef _TARGET_ARMARCH_
+ else if (operand->OperIsPutArgStk())
+ {
+ // A PUTARG_STK argument is an operand of a call, but is neither contained, nor does it produce
+ // a result.
+ assert(!operand->isContained());
+ return 0;
+ }
+#endif // _TARGET_ARMARCH_
else
{
// If a field list or non-void-typed operand is not an unused value and does not have source registers,
@@ -3935,6 +3983,10 @@ void LinearScan::buildRefPositionsForNode(GenTree* tree,
assert(removed);
assert(!operandDefs.IsEmpty());
+#ifdef _TARGET_ARM_
+ regMaskTP currCandidates = RBM_NONE;
+#endif // _TARGET_ARM_
+
LocationInfoListNode* const operandDefsEnd = operandDefs.End();
for (LocationInfoListNode* operandDefsIterator = operandDefs.Begin(); operandDefsIterator != operandDefsEnd;
operandDefsIterator = operandDefsIterator->Next())
@@ -3985,6 +4037,15 @@ void LinearScan::buildRefPositionsForNode(GenTree* tree,
#endif // DEBUG
regMaskTP candidates = getUseCandidates(useNode);
+#ifdef _TARGET_ARM_
+ if (useNode->OperIsPutArgSplit() || (compiler->opts.compUseSoftFP && useNode->OperIsPutArgReg()))
+ {
+ // get i-th candidate, set bits in useCandidates must be in sequential order.
+ candidates = genFindLowestReg(candidates & ~currCandidates);
+ currCandidates |= candidates;
+ }
+#endif // _TARGET_ARM_
+
assert((candidates & allRegs(i->registerType)) != 0);
// For non-localVar uses we record nothing, as nothing needs to be written back to the tree.
@@ -4081,7 +4142,7 @@ void LinearScan::buildRefPositionsForNode(GenTree* tree,
}
#ifdef ARM_SOFTFP
// If oper is GT_PUTARG_REG, set bits in useCandidates must be in sequential order.
- else if (tree->OperGet() == GT_PUTARG_REG || tree->OperGet() == GT_COPY)
+ else if (tree->OperIsMultiRegOp() || tree->OperGet() == GT_COPY)
{
useCandidates = genFindLowestReg(remainingUseCandidates);
remainingUseCandidates &= ~useCandidates;
@@ -4134,6 +4195,9 @@ void LinearScan::buildRefPositionsForNode(GenTree* tree,
(unsigned)i DEBUG_ARG(minRegCount));
if (info.isLocalDefUse)
{
+ // This must be an unused value, OR it is a special node for which we allocate
+ // a target register even though it produces no value.
+ assert(defNode->IsUnusedValue() || (defNode->gtOper == GT_LOCKADD));
pos->isLocalDefUse = true;
pos->lastUse = true;
}
@@ -4470,9 +4534,6 @@ void LinearScan::buildIntervals()
{
BasicBlock* block;
- // start numbering at 1; 0 is the entry
- LsraLocation currentLoc = 1;
-
JITDUMP("\nbuildIntervals ========\n");
// Now build (empty) records for all of the physical registers
@@ -4517,7 +4578,18 @@ void LinearScan::buildIntervals()
// second part:
JITDUMP("\nbuildIntervals second part ========\n");
- currentLoc = 0;
+ LsraLocation currentLoc = 0;
+ // TODO-Cleanup: This duplicates prior behavior where entry (ParamDef) RefPositions were
+ // being assigned the bbNum of the last block traversed in the 2nd phase of Lowering.
+ // Previously, the block sequencing was done for the (formerly separate) TreeNodeInfoInit pass,
+ // and the curBBNum was left as the last block sequenced. This block was then used to set the
+ // weight for the entry (ParamDef) RefPositions. It would be logical to set this to the
+ // normalized entry weight (compiler->fgCalledCount), but that results in a net regression.
+ if (!blockSequencingDone)
+ {
+ setBlockSequence();
+ }
+ curBBNum = blockSequence[bbSeqCount - 1]->bbNum;
// Next, create ParamDef RefPositions for all the tracked parameters,
// in order of their varIndex
@@ -4654,6 +4726,8 @@ void LinearScan::buildIntervals()
if (block == compiler->fgFirstBB)
{
insertZeroInitRefPositions();
+ // The first real location is at 1; 0 is for the entry.
+ currentLoc = 1;
}
// Any lclVars live-in to a block are resolution candidates.
@@ -4708,15 +4782,49 @@ void LinearScan::buildIntervals()
// this point.
RefPosition* pos = newRefPosition((Interval*)nullptr, currentLoc, RefTypeBB, nullptr, RBM_NONE);
+ currentLoc += 2;
JITDUMP("\n");
LIR::Range& blockRange = LIR::AsRange(block);
for (GenTree* node : blockRange.NonPhiNodes())
{
- assert(node->gtLsraInfo.loc >= currentLoc);
- assert(!node->IsValue() || !node->IsUnusedValue() || node->gtLsraInfo.isLocalDefUse);
+ // We increment the number position of each tree node by 2 to simplify the logic when there's the case of
+ // a tree that implicitly does a dual-definition of temps (the long case). In this case it is easier to
+ // already have an idle spot to handle a dual-def instead of making some messy adjustments if we only
+ // increment the number position by one.
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#ifdef DEBUG
+ node->gtSeqNum = currentLoc;
+ // In DEBUG, we want to set the gtRegTag to GT_REGTAG_REG, so that subsequent dumps will so the register
+ // value.
+ // Although this looks like a no-op it sets the tag.
+ node->gtRegNum = node->gtRegNum;
+#endif
+
+ node->gtLsraInfo.Initialize(this, node, currentLoc);
+
+ TreeNodeInfoInit(node);
+
+ // If the node produces an unused value, mark it as a local def-use
+ if (node->IsValue() && node->IsUnusedValue())
+ {
+ node->gtLsraInfo.isLocalDefUse = true;
+ node->gtLsraInfo.dstCount = 0;
+ }
+
+#ifdef DEBUG
+ if (VERBOSE)
+ {
+ compiler->gtDispTree(node, nullptr, nullptr, true);
+ printf(" +");
+ node->gtLsraInfo.dump(this);
+ }
+#endif // DEBUG
+
+ // Only nodes that produce values should have a non-zero dstCount.
+ assert((node->gtLsraInfo.dstCount == 0) || node->IsValue());
- currentLoc = node->gtLsraInfo.loc;
buildRefPositionsForNode(node, block, listNodePool, operandToLocationInfoMap, currentLoc);
#ifdef DEBUG
@@ -4725,12 +4833,9 @@ void LinearScan::buildIntervals()
maxNodeLocation = currentLoc;
}
#endif // DEBUG
+ currentLoc += 2;
}
- // Increment the LsraLocation at this point, so that the dummy RefPositions
- // will not have the same LsraLocation as any "real" RefPosition.
- currentLoc += 2;
-
// Note: the visited set is cleared in LinearScan::doLinearScan()
markBlockVisited(block);
@@ -5162,8 +5267,7 @@ bool LinearScan::registerIsAvailable(RegRecord* physRegRecord,
if (regType == TYP_DOUBLE)
{
// Recurse, but check the other half this time (TYP_FLOAT)
- if (!registerIsAvailable(getRegisterRecord(REG_NEXT(physRegRecord->regNum)), currentLoc, nextRefLocationPtr,
- TYP_FLOAT))
+ if (!registerIsAvailable(findAnotherHalfRegRec(physRegRecord), currentLoc, nextRefLocationPtr, TYP_FLOAT))
return false;
nextRefLocation = *nextRefLocationPtr;
}
@@ -5415,7 +5519,7 @@ regNumber LinearScan::tryAllocateFreeReg(Interval* currentInterval, RefPosition*
}
RegRecord* availablePhysRegInterval = nullptr;
- Interval* intervalToUnassign = nullptr;
+ bool unassignInterval = false;
// Each register will receive a score which is the sum of the scoring criteria below.
// These were selected on the assumption that they will have an impact on the "goodness"
@@ -5493,7 +5597,7 @@ regNumber LinearScan::tryAllocateFreeReg(Interval* currentInterval, RefPosition*
if (physRegRecord->assignedInterval == currentInterval)
{
availablePhysRegInterval = physRegRecord;
- intervalToUnassign = nullptr;
+ unassignInterval = false;
break;
}
@@ -5605,8 +5709,7 @@ regNumber LinearScan::tryAllocateFreeReg(Interval* currentInterval, RefPosition*
// The register is considered unassigned if it has no assignedInterval, OR
// if its next reference is beyond the range of this interval.
- if (physRegRecord->assignedInterval == nullptr ||
- physRegRecord->assignedInterval->getNextRefLocation() > lastLocation)
+ if (!isAssigned(physRegRecord, lastLocation ARM_ARG(currentInterval->registerType)))
{
score |= UNASSIGNED;
}
@@ -5654,7 +5757,7 @@ regNumber LinearScan::tryAllocateFreeReg(Interval* currentInterval, RefPosition*
{
bestLocation = nextPhysRefLocation;
availablePhysRegInterval = physRegRecord;
- intervalToUnassign = physRegRecord->assignedInterval;
+ unassignInterval = true;
bestScore = score;
}
@@ -5667,17 +5770,12 @@ regNumber LinearScan::tryAllocateFreeReg(Interval* currentInterval, RefPosition*
if (availablePhysRegInterval != nullptr)
{
- if (intervalToUnassign != nullptr)
+ if (unassignInterval && isAssigned(availablePhysRegInterval ARM_ARG(currentInterval->registerType)))
{
- RegRecord* physRegToUnassign = availablePhysRegInterval;
-#ifdef _TARGET_ARM_
- // We should unassign a double register if availablePhysRegInterval is part of the double register
- if (availablePhysRegInterval->assignedInterval->registerType == TYP_DOUBLE &&
- !genIsValidDoubleReg(availablePhysRegInterval->regNum))
- physRegToUnassign = findAnotherHalfRegRec(availablePhysRegInterval);
-#endif
- unassignPhysReg(physRegToUnassign, intervalToUnassign->recentRefPosition);
- if (bestScore & VALUE_AVAILABLE)
+ Interval* const intervalToUnassign = availablePhysRegInterval->assignedInterval;
+ unassignPhysReg(availablePhysRegInterval ARM_ARG(currentInterval->registerType));
+
+ if ((bestScore & VALUE_AVAILABLE) != 0 && intervalToUnassign != nullptr)
{
assert(intervalToUnassign->isConstant);
refPosition->treeNode->SetReuseRegVal();
@@ -5686,7 +5784,7 @@ regNumber LinearScan::tryAllocateFreeReg(Interval* currentInterval, RefPosition*
// the next ref, remember it.
else if ((bestScore & UNASSIGNED) != 0 && intervalToUnassign != nullptr)
{
- updatePreviousInterval(physRegToUnassign, intervalToUnassign, intervalToUnassign->registerType);
+ updatePreviousInterval(availablePhysRegInterval, intervalToUnassign, intervalToUnassign->registerType);
}
}
else
@@ -5705,6 +5803,250 @@ regNumber LinearScan::tryAllocateFreeReg(Interval* currentInterval, RefPosition*
return foundReg;
}
+//------------------------------------------------------------------------
+// canSpillReg: Determine whether we can spill physRegRecord
+//
+// Arguments:
+// physRegRecord - reg to spill
+// refLocation - Location of RefPosition where this register will be spilled
+// recentAssignedRefWeight - Weight of recent assigned RefPosition which will be determined in this function
+// farthestRefPosWeight - Current farthestRefPosWeight at allocateBusyReg()
+//
+// Return Value:
+// True - if we can spill physRegRecord
+// False - otherwise
+//
+// Note: This helper is designed to be used only from allocateBusyReg() and canSpillDoubleReg()
+//
+bool LinearScan::canSpillReg(RegRecord* physRegRecord,
+ LsraLocation refLocation,
+ unsigned* recentAssignedRefWeight,
+ unsigned farthestRefPosWeight)
+{
+ assert(physRegRecord->assignedInterval != nullptr);
+ RefPosition* recentAssignedRef = physRegRecord->assignedInterval->recentRefPosition;
+
+ if (recentAssignedRef != nullptr)
+ {
+ if (recentAssignedRef->nodeLocation == refLocation)
+ {
+ // We can't spill a register that's being used at the current location
+ return false;
+ }
+
+ // If the current position has the candidate register marked to be delayed,
+ // check if the previous location is using this register, if that's the case we have to skip
+ // since we can't spill this register.
+ if (recentAssignedRef->delayRegFree && (refLocation == recentAssignedRef->nodeLocation + 1))
+ {
+ return false;
+ }
+
+ // We don't prefer to spill a register if the weight of recentAssignedRef > weight
+ // of the spill candidate found so far. We would consider spilling a greater weight
+ // ref position only if the refPosition being allocated must need a reg.
+ *recentAssignedRefWeight = getWeight(recentAssignedRef);
+ if (*recentAssignedRefWeight > farthestRefPosWeight)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+#ifdef _TARGET_ARM_
+bool LinearScan::canSpillDoubleReg(RegRecord* physRegRecord,
+ LsraLocation refLocation,
+ unsigned* recentAssignedRefWeight,
+ unsigned farthestRefPosWeight)
+{
+ bool retVal = true;
+ unsigned weight = BB_ZERO_WEIGHT;
+ unsigned weight2 = BB_ZERO_WEIGHT;
+
+ RegRecord* physRegRecord2 = findAnotherHalfRegRec(physRegRecord);
+
+ if (physRegRecord->assignedInterval != nullptr)
+ retVal &= canSpillReg(physRegRecord, refLocation, &weight, farthestRefPosWeight);
+
+ if (physRegRecord2->assignedInterval != nullptr)
+ retVal &= canSpillReg(physRegRecord2, refLocation, &weight2, farthestRefPosWeight);
+
+ if (!(weight == BB_ZERO_WEIGHT && weight2 == BB_ZERO_WEIGHT))
+ {
+ // weight and/or weight2 have been updated.
+ *recentAssignedRefWeight = (weight > weight2) ? weight : weight2;
+ }
+
+ return retVal;
+}
+#endif
+
+//----------------------------------------------------------------------------
+// checkActiveInterval: Test activness of an interval
+// and check assertions if the interval is not active
+//
+// Arguments:
+// interval - An interval to be tested
+// refLocation - Location where the interval is being tested
+//
+// Return Value:
+// True - iff the interval is active
+// False - otherwise
+//
+// Note: This helper is designed to be used only from checkActiveIntervals()
+//
+bool LinearScan::checkActiveInterval(Interval* interval, LsraLocation refLocation)
+{
+ if (!interval->isActive)
+ {
+ RefPosition* recentAssignedRef = interval->recentRefPosition;
+ // Note that we may or may not have actually handled the reference yet, so it could either
+ // be recentAssignedRef, or the next reference.
+ assert(recentAssignedRef != nullptr);
+ if (recentAssignedRef->nodeLocation != refLocation)
+ {
+ if (recentAssignedRef->nodeLocation + 1 == refLocation)
+ {
+ assert(recentAssignedRef->delayRegFree);
+ }
+ else
+ {
+ RefPosition* nextAssignedRef = recentAssignedRef->nextRefPosition;
+ assert(nextAssignedRef != nullptr);
+ assert(nextAssignedRef->nodeLocation == refLocation ||
+ (nextAssignedRef->nodeLocation + 1 == refLocation && nextAssignedRef->delayRegFree));
+ }
+ }
+ return false;
+ }
+ return true;
+}
+
+//----------------------------------------------------------------------------------------
+// checkActiveIntervals: Test activness of a interval assinged to a register
+// and check assertions if the interval is not active.
+// We look into all intervals of two float registers consisting
+// a double regsiter for ARM32.
+//
+// Arguments:
+// physRegRecord - A register
+// refLocation - Location where intervsl is being tested
+// registerType - Type of regsiter
+//
+// Return Value:
+// True - iff the all intervals are active
+// False - otherwise
+//
+// Note: This helper is designed to be used only from allocateBusyReg()
+//
+bool LinearScan::checkActiveIntervals(RegRecord* physRegRecord, LsraLocation refLocation, RegisterType registerType)
+{
+ Interval* assignedInterval = physRegRecord->assignedInterval;
+
+#ifdef _TARGET_ARM_
+ // Check two intervals for a double register in ARM32
+ Interval* assignedInterval2 = nullptr;
+ if (registerType == TYP_DOUBLE)
+ assignedInterval2 = findAnotherHalfRegRec(physRegRecord)->assignedInterval;
+
+ // Both intervals should not be nullptr at the same time, becasue we already handle this case before.
+ assert(!(assignedInterval == nullptr && assignedInterval2 == nullptr));
+
+ if (assignedInterval != nullptr && !checkActiveInterval(assignedInterval, refLocation))
+ return false;
+
+ if (assignedInterval2 != nullptr && !checkActiveInterval(assignedInterval2, refLocation))
+ return false;
+
+ return true;
+#else
+ return checkActiveInterval(assignedInterval, refLocation);
+#endif
+}
+
+#ifdef _TARGET_ARM_
+void LinearScan::unassignDoublePhysReg(RegRecord* doubleRegRecord)
+{
+ assert(genIsValidDoubleReg(doubleRegRecord->regNum));
+
+ RegRecord* doubleRegRecordLo = doubleRegRecord;
+ RegRecord* doubleRegRecordHi = findAnotherHalfRegRec(doubleRegRecordLo);
+ // For a double register, we has following four cases.
+ // Case 1: doubleRegRecLo is assigned to TYP_DOUBLE interval
+ // Case 2: doubleRegRecLo and doubleRegRecHi are assigned to different TYP_FLOAT intervals
+ // Case 3: doubelRegRecLo is assgined to TYP_FLOAT interval and doubleRegRecHi is nullptr
+ // Case 4: doubleRegRecordLo is nullptr, and doubleRegRecordHi is assigned to a TYP_FLOAT interval
+ if (doubleRegRecordLo->assignedInterval != nullptr)
+ {
+ if (doubleRegRecordLo->assignedInterval->registerType == TYP_DOUBLE)
+ {
+ // Case 1: doubleRegRecLo is assigned to TYP_DOUBLE interval
+ unassignPhysReg(doubleRegRecordLo, doubleRegRecordLo->assignedInterval->recentRefPosition);
+ }
+ else
+ {
+ // Case 2: doubleRegRecLo and doubleRegRecHi are assigned to different TYP_FLOAT intervals
+ // Case 3: doubelRegRecLo is assgined to TYP_FLOAT interval and doubleRegRecHi is nullptr
+ assert(doubleRegRecordLo->assignedInterval->registerType == TYP_FLOAT);
+ unassignPhysReg(doubleRegRecordLo, doubleRegRecordLo->assignedInterval->recentRefPosition);
+
+ if (doubleRegRecordHi != nullptr)
+ {
+ if (doubleRegRecordHi->assignedInterval != nullptr)
+ {
+ assert(doubleRegRecordHi->assignedInterval->registerType == TYP_FLOAT);
+ unassignPhysReg(doubleRegRecordHi, doubleRegRecordHi->assignedInterval->recentRefPosition);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Case 4: doubleRegRecordLo is nullptr, and doubleRegRecordHi is assigned to a TYP_FLOAT interval
+ assert(doubleRegRecordHi->assignedInterval != nullptr);
+ assert(doubleRegRecordHi->assignedInterval->registerType == TYP_FLOAT);
+ unassignPhysReg(doubleRegRecordHi, doubleRegRecordHi->assignedInterval->recentRefPosition);
+ }
+}
+
+#endif // _TARGET_ARM_
+
+//----------------------------------------------------------------------------------------
+// isRegInUse: Test whether regRec is being used at the refPosition
+//
+// Arguments:
+// regRec - A register to be tested
+// refPosition - RefPosition where regRec is tested
+// nextLocation - next RefPosition of interval assigned to regRec
+//
+// Return Value:
+// True - if regRec is beding used
+// False - otherwise
+//
+// Note: This helper is designed to be used only from allocateBusyReg()
+//
+bool LinearScan::isRegInUse(RegRecord* regRec, RefPosition* refPosition, LsraLocation* nextLocation)
+{
+ Interval* assignedInterval = regRec->assignedInterval;
+ if (assignedInterval != nullptr)
+ {
+ LsraLocation refLocation = refPosition->nodeLocation;
+ RefPosition* nextRefPosition = assignedInterval->getNextRefPosition();
+ *nextLocation = assignedInterval->getNextRefLocation();
+
+ // We should never spill a register that's occupied by an Interval with its next use at the current
+ // location.
+ // Normally this won't occur (unless we actually had more uses in a single node than there are registers),
+ // because we'll always find something with a later nextLocation, but it can happen in stress when
+ // we have LSRA_SELECT_NEAREST.
+ if ((*nextLocation == refLocation) && !refPosition->isFixedRegRef && nextRefPosition->RequiresRegister())
+ {
+ return true;
+ }
+ }
+ return false;
+}
//------------------------------------------------------------------------
// allocateBusyReg: Find a busy register that satisfies the requirements for refPosition,
@@ -5757,9 +6099,12 @@ regNumber LinearScan::allocateBusyReg(Interval* current, RefPosition* refPositio
// TODO-CQ: Determine whether/how to take preferences into account in addition to
// prefering the one with the furthest ref position when considering
// a candidate to spill
- RegRecord* farthestRefPhysRegRecord = nullptr;
- LsraLocation farthestLocation = MinLocation;
- LsraLocation refLocation = refPosition->nodeLocation;
+ RegRecord* farthestRefPhysRegRecord = nullptr;
+#ifdef _TARGET_ARM_
+ RegRecord* farthestRefPhysRegRecord2 = nullptr;
+#endif
+ LsraLocation farthestLocation = MinLocation;
+ LsraLocation refLocation = refPosition->nodeLocation;
unsigned farthestRefPosWeight;
if (allocateIfProfitable)
{
@@ -5786,12 +6131,25 @@ regNumber LinearScan::allocateBusyReg(Interval* current, RefPosition* refPositio
continue;
}
RegRecord* physRegRecord = getRegisterRecord(regNum);
+#ifdef _TARGET_ARM_
+ RegRecord* physRegRecord2 = nullptr;
+ // For ARM32, let's consider two float registers consisting a double reg together,
+ // when allocaing a double register.
+ if (current->registerType == TYP_DOUBLE)
+ {
+ assert(genIsValidDoubleReg(regNum));
+ physRegRecord2 = findAnotherHalfRegRec(physRegRecord);
+ }
+#endif
if (physRegRecord->isBusyUntilNextKill)
{
continue;
}
Interval* assignedInterval = physRegRecord->assignedInterval;
+#ifdef _TARGET_ARM_
+ Interval* assignedInterval2 = (physRegRecord2 == nullptr) ? nullptr : physRegRecord2->assignedInterval;
+#endif
// If there is a fixed reference at the same location (and it's not due to this reference),
// don't use it.
@@ -5845,8 +6203,13 @@ regNumber LinearScan::allocateBusyReg(Interval* current, RefPosition* refPositio
// - it has a FixedReg reference at the current location that is not this reference, OR
// - this is the special case of a fixed loReg, where this interval has a use at the same location
// In either case, we cannot use it
+ CLANG_FORMAT_COMMENT_ANCHOR;
+#ifdef _TARGET_ARM_
+ if (assignedInterval == nullptr && assignedInterval2 == nullptr)
+#else
if (assignedInterval == nullptr)
+#endif
{
RefPosition* nextPhysRegPosition = physRegRecord->getNextRefPosition();
@@ -5858,29 +6221,16 @@ regNumber LinearScan::allocateBusyReg(Interval* current, RefPosition* refPositio
continue;
}
+#ifdef _TARGET_ARM_
+ RefPosition* recentAssignedRef = (assignedInterval == nullptr) ? nullptr : assignedInterval->recentRefPosition;
+ RefPosition* recentAssignedRef2 =
+ (assignedInterval2 == nullptr) ? nullptr : assignedInterval2->recentRefPosition;
+#else
RefPosition* recentAssignedRef = assignedInterval->recentRefPosition;
+#endif
- if (!assignedInterval->isActive)
+ if (!checkActiveIntervals(physRegRecord, refLocation, current->registerType))
{
- // The assigned interval has a reference at this location - otherwise, we would have found
- // this in tryAllocateFreeReg().
- // Note that we may or may not have actually handled the reference yet, so it could either
- // be recentAssigedRef, or the next reference.
- assert(recentAssignedRef != nullptr);
- if (recentAssignedRef->nodeLocation != refLocation)
- {
- if (recentAssignedRef->nodeLocation + 1 == refLocation)
- {
- assert(recentAssignedRef->delayRegFree);
- }
- else
- {
- RefPosition* nextAssignedRef = recentAssignedRef->nextRefPosition;
- assert(nextAssignedRef != nullptr);
- assert(nextAssignedRef->nodeLocation == refLocation ||
- (nextAssignedRef->nodeLocation + 1 == refLocation && nextAssignedRef->delayRegFree));
- }
- }
continue;
}
@@ -5888,45 +6238,40 @@ regNumber LinearScan::allocateBusyReg(Interval* current, RefPosition* refPositio
//
// TODO-Review: Under what conditions recentAssginedRef would be null?
unsigned recentAssignedRefWeight = BB_ZERO_WEIGHT;
- if (recentAssignedRef != nullptr)
- {
- if (recentAssignedRef->nodeLocation == refLocation)
- {
- // We can't spill a register that's being used at the current location
- RefPosition* physRegRef = physRegRecord->recentRefPosition;
- continue;
- }
- // If the current position has the candidate register marked to be delayed,
- // check if the previous location is using this register, if that's the case we have to skip
- // since we can't spill this register.
- if (recentAssignedRef->delayRegFree && (refLocation == recentAssignedRef->nodeLocation + 1))
- {
- continue;
- }
-
- // We don't prefer to spill a register if the weight of recentAssignedRef > weight
- // of the spill candidate found so far. We would consider spilling a greater weight
- // ref position only if the refPosition being allocated must need a reg.
- recentAssignedRefWeight = getWeight(recentAssignedRef);
- if (recentAssignedRefWeight > farthestRefPosWeight)
- {
+#ifdef _TARGET_ARM_
+ if (current->registerType == TYP_DOUBLE)
+ {
+ if (!canSpillDoubleReg(physRegRecord, refLocation, &recentAssignedRefWeight, farthestRefPosWeight))
continue;
- }
+ }
+ else
+#endif
+ // This if-stmt is associated with the above else
+ if (!canSpillReg(physRegRecord, refLocation, &recentAssignedRefWeight, farthestRefPosWeight))
+ {
+ continue;
}
- RefPosition* nextRefPosition = assignedInterval->getNextRefPosition();
- LsraLocation nextLocation = assignedInterval->getNextRefLocation();
+ LsraLocation nextLocation = MinLocation;
- // We should never spill a register that's occupied by an Interval with its next use at the current location.
- // Normally this won't occur (unless we actually had more uses in a single node than there are registers),
- // because we'll always find something with a later nextLocation, but it can happen in stress when
- // we have LSRA_SELECT_NEAREST.
- if ((nextLocation == refLocation) && !refPosition->isFixedRegRef && nextRefPosition->RequiresRegister())
+ if (isRegInUse(physRegRecord, refPosition, &nextLocation))
{
continue;
}
+#ifdef _TARGET_ARM_
+ if (current->registerType == TYP_DOUBLE)
+ {
+ LsraLocation nextLocation2 = MinLocation;
+ if (isRegInUse(physRegRecord2, refPosition, &nextLocation2))
+ {
+ continue;
+ }
+ nextLocation = (nextLocation > nextLocation2) ? nextLocation : nextLocation2;
+ }
+#endif
+
if (nextLocation > physRegNextLocation)
{
nextLocation = physRegNextLocation;
@@ -5979,8 +6324,19 @@ regNumber LinearScan::allocateBusyReg(Interval* current, RefPosition* refPositio
// allocate if profitable. These ref positions don't need
// need to be spilled as they are already in memory and
// codegen considers them as contained memory operands.
- isBetterLocation = (recentAssignedRef != nullptr) && recentAssignedRef->reload &&
+ CLANG_FORMAT_COMMENT_ANCHOR;
+#ifdef _TARGET_ARM
+ // TODO-CQ-ARM: Just conservatively "and" two condition. We may implement better condision later.
+ isBetterLocation = true;
+ if (recentAssignedRef != nullptr)
+ isBetterLocation &= (recentAssignedRef->reload && recentAssignedRef->AllocateIfProfitable());
+
+ if (recentAssignedRef2 != nullptr)
+ isBetterLocation &= (recentAssignedRef2->reload && recentAssignedRef2->AllocateIfProfitable());
+#else
+ isBetterLocation = (recentAssignedRef != nullptr) && recentAssignedRef->reload &&
recentAssignedRef->AllocateIfProfitable();
+#endif
}
else
{
@@ -5993,7 +6349,10 @@ regNumber LinearScan::allocateBusyReg(Interval* current, RefPosition* refPositio
{
farthestLocation = nextLocation;
farthestRefPhysRegRecord = physRegRecord;
- farthestRefPosWeight = recentAssignedRefWeight;
+#ifdef _TARGET_ARM_
+ farthestRefPhysRegRecord2 = physRegRecord2;
+#endif
+ farthestRefPosWeight = recentAssignedRefWeight;
}
}
@@ -6010,21 +6369,59 @@ regNumber LinearScan::allocateBusyReg(Interval* current, RefPosition* refPositio
assert(farthestRefPhysRegRecord != nullptr);
if ((farthestLocation == refLocation) && !refPosition->isFixedRegRef)
{
+#ifdef _TARGET_ARM_
+ Interval* assignedInterval =
+ (farthestRefPhysRegRecord == nullptr) ? nullptr : farthestRefPhysRegRecord->assignedInterval;
+ Interval* assignedInterval2 =
+ (farthestRefPhysRegRecord2 == nullptr) ? nullptr : farthestRefPhysRegRecord2->assignedInterval;
+ RefPosition* nextRefPosition =
+ (assignedInterval == nullptr) ? nullptr : assignedInterval->getNextRefPosition();
+ RefPosition* nextRefPosition2 =
+ (assignedInterval2 == nullptr) ? nullptr : assignedInterval2->getNextRefPosition();
+ if (nextRefPosition != nullptr)
+ {
+ if (nextRefPosition2 != nullptr)
+ {
+ assert(!nextRefPosition->RequiresRegister() || !nextRefPosition2->RequiresRegister());
+ }
+ else
+ {
+ assert(!nextRefPosition->RequiresRegister());
+ }
+ }
+ else
+ {
+ assert(nextRefPosition2 != nullptr && !nextRefPosition2->RequiresRegister());
+ }
+#else // !_TARGET_ARM_
Interval* assignedInterval = farthestRefPhysRegRecord->assignedInterval;
RefPosition* nextRefPosition = assignedInterval->getNextRefPosition();
assert(!nextRefPosition->RequiresRegister());
+#endif // !_TARGET_ARM_
}
else
{
assert(farthestLocation > refLocation || refPosition->isFixedRegRef);
}
}
-#endif
+#endif // DEBUG
if (farthestRefPhysRegRecord != nullptr)
{
foundReg = farthestRefPhysRegRecord->regNum;
- unassignPhysReg(farthestRefPhysRegRecord, farthestRefPhysRegRecord->assignedInterval->recentRefPosition);
+
+#ifdef _TARGET_ARM_
+ if (current->registerType == TYP_DOUBLE)
+ {
+ assert(genIsValidDoubleReg(foundReg));
+ unassignDoublePhysReg(farthestRefPhysRegRecord);
+ }
+ else
+#endif
+ {
+ unassignPhysReg(farthestRefPhysRegRecord, farthestRefPhysRegRecord->assignedInterval->recentRefPosition);
+ }
+
assignPhysReg(farthestRefPhysRegRecord, current);
refPosition->registerAssignment = genRegMask(foundReg);
}
@@ -6083,6 +6480,73 @@ regNumber LinearScan::assignCopyReg(RefPosition* refPosition)
return allocatedReg;
}
+//------------------------------------------------------------------------
+// isAssigned: This is the function to check if the given RegRecord has an assignedInterval
+// regardless of lastLocation.
+// So it would be call isAssigned() with Maxlocation value.
+//
+// Arguments:
+// regRec - The RegRecord to check that it is assigned.
+// newRegType - There are elements to judge according to the upcoming register type.
+//
+// Return Value:
+// Returns true if the given RegRecord has an assignedInterval.
+//
+// Notes:
+// There is the case to check if the RegRecord has an assignedInterval regardless of Lastlocation.
+//
+bool LinearScan::isAssigned(RegRecord* regRec ARM_ARG(RegisterType newRegType))
+{
+ return isAssigned(regRec, MaxLocation ARM_ARG(newRegType));
+}
+
+//------------------------------------------------------------------------
+// isAssigned: Check whether the given RegRecord has an assignedInterval
+// that has a reference prior to the given location.
+//
+// Arguments:
+// regRec - The RegRecord of interest
+// lastLocation - The LsraLocation up to which we want to check
+// newRegType - The `RegisterType` of interval we want to check
+// (this is for the purposes of checking the other half of a TYP_DOUBLE RegRecord)
+//
+// Return value:
+// Returns true if the given RegRecord (and its other half, if TYP_DOUBLE) has an assignedInterval
+// that is referenced prior to the given location
+//
+// Notes:
+// The register is not considered to be assigned if it has no assignedInterval, or that Interval's
+// next reference is beyond lastLocation
+//
+bool LinearScan::isAssigned(RegRecord* regRec, LsraLocation lastLocation ARM_ARG(RegisterType newRegType))
+{
+ Interval* assignedInterval = regRec->assignedInterval;
+
+ if ((assignedInterval == nullptr) || assignedInterval->getNextRefLocation() > lastLocation)
+ {
+#ifdef _TARGET_ARM_
+ if (newRegType == TYP_DOUBLE)
+ {
+ RegRecord* anotherRegRec = findAnotherHalfRegRec(regRec);
+
+ if ((anotherRegRec->assignedInterval == nullptr) ||
+ (anotherRegRec->assignedInterval->getNextRefLocation() > lastLocation))
+ {
+ // In case the newRegType is a double register,
+ // the score would be set UNASSIGNED if another register is also not set.
+ return false;
+ }
+ }
+ else
+#endif
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
// Check if the interval is already assigned and if it is then unassign the physical record
// then set the assignedInterval to 'interval'
//
@@ -6316,6 +6780,58 @@ void LinearScan::checkAndClearInterval(RegRecord* regRec, RefPosition* spillRefP
// assignedInterval at the given spillRefPosition, if any.
//
// Arguments:
+// regRec - The RegRecord to be unasssigned
+// newRegType - The RegisterType of interval that would be assigned
+//
+// Return Value:
+// None.
+//
+// Notes:
+// On ARM architecture, Intervals have to be unassigned considering
+// with the register type of interval that would be assigned.
+//
+void LinearScan::unassignPhysReg(RegRecord* regRec ARM_ARG(RegisterType newRegType))
+{
+ RegRecord* regRecToUnassign = regRec;
+#ifdef _TARGET_ARM_
+ RegRecord* anotherRegRec = nullptr;
+
+ if ((regRecToUnassign->assignedInterval != nullptr) &&
+ (regRecToUnassign->assignedInterval->registerType == TYP_DOUBLE))
+ {
+ // If the register type of interval(being unassigned or new) is TYP_DOUBLE,
+ // It should have to be valid double register (even register)
+ if (!genIsValidDoubleReg(regRecToUnassign->regNum))
+ {
+ regRecToUnassign = findAnotherHalfRegRec(regRec);
+ }
+ }
+ else
+ {
+ if (newRegType == TYP_DOUBLE)
+ {
+ anotherRegRec = findAnotherHalfRegRec(regRecToUnassign);
+ }
+ }
+#endif
+
+ if (regRecToUnassign->assignedInterval != nullptr)
+ {
+ unassignPhysReg(regRecToUnassign, regRecToUnassign->assignedInterval->recentRefPosition);
+ }
+#ifdef _TARGET_ARM_
+ if ((anotherRegRec != nullptr) && (anotherRegRec->assignedInterval != nullptr))
+ {
+ unassignPhysReg(anotherRegRec, anotherRegRec->assignedInterval->recentRefPosition);
+ }
+#endif
+}
+
+//------------------------------------------------------------------------
+// unassignPhysReg: Unassign the given physical register record, and spill the
+// assignedInterval at the given spillRefPosition, if any.
+//
+// Arguments:
// regRec - the RegRecord to be unasssigned
// spillRefPosition - The RefPosition at which the assignedInterval is to be spilled
//
@@ -6336,20 +6852,17 @@ void LinearScan::unassignPhysReg(RegRecord* regRec, RefPosition* spillRefPositio
regNumber thisRegNum = regRec->regNum;
#ifdef _TARGET_ARM_
- regNumber nextRegNum = REG_NA;
- RegRecord* nextRegRec = nullptr;
+ RegRecord* anotherRegRec = nullptr;
// Prepare second half RegRecord of a double register for TYP_DOUBLE
if (assignedInterval->registerType == TYP_DOUBLE)
{
assert(isFloatRegType(regRec->registerType));
- assert(genIsValidDoubleReg(regRec->regNum));
- nextRegNum = REG_NEXT(regRec->regNum);
- nextRegRec = getRegisterRecord(nextRegNum);
+ anotherRegRec = findAnotherHalfRegRec(regRec);
// Both two RegRecords should have been assigned to the same interval.
- assert(assignedInterval == nextRegRec->assignedInterval);
+ assert(assignedInterval == anotherRegRec->assignedInterval);
}
#endif // _TARGET_ARM_
@@ -6360,7 +6873,7 @@ void LinearScan::unassignPhysReg(RegRecord* regRec, RefPosition* spillRefPositio
{
// Both two RegRecords should have been unassigned together.
assert(regRec->assignedInterval == nullptr);
- assert(nextRegRec->assignedInterval == nullptr);
+ assert(anotherRegRec->assignedInterval == nullptr);
}
#endif // _TARGET_ARM_
@@ -7965,8 +8478,6 @@ void LinearScan::updateAssignedInterval(RegRecord* reg, Interval* interval, Regi
// Update overlapping floating point register for TYP_DOUBLE
if (regType == TYP_DOUBLE)
{
- assert(genIsValidDoubleReg(reg->regNum));
-
RegRecord* anotherHalfReg = findAnotherHalfRegRec(reg);
anotherHalfReg->assignedInterval = interval;
@@ -8001,8 +8512,6 @@ void LinearScan::updatePreviousInterval(RegRecord* reg, Interval* interval, Regi
// Update overlapping floating point register for TYP_DOUBLE
if (regType == TYP_DOUBLE)
{
- assert(genIsValidDoubleReg(reg->regNum));
-
RegRecord* anotherHalfReg = findAnotherHalfRegRec(reg);
anotherHalfReg->previousInterval = interval;
@@ -8613,6 +9122,21 @@ void LinearScan::updateMaxSpill(RefPosition* refPosition)
ReturnTypeDesc* retTypeDesc = treeNode->AsCall()->GetReturnTypeDesc();
typ = retTypeDesc->GetReturnRegType(refPosition->getMultiRegIdx());
}
+#ifdef _TARGET_ARM_
+ else if (treeNode->OperIsPutArgSplit())
+ {
+ typ = treeNode->AsPutArgSplit()->GetRegType(refPosition->getMultiRegIdx());
+ }
+#endif
+#ifdef ARM_SOFTFP
+ else if (treeNode->OperIsPutArgReg())
+ {
+ // For double arg regs, the type is changed to long since they must be passed via `r0-r3`.
+ // However when they get spilled, they should be treated as separated int registers.
+ var_types typNode = treeNode->TypeGet();
+ typ = (typNode == TYP_LONG) ? TYP_INT : typNode;
+ }
+#endif // ARM_SOFTFP
else
{
typ = treeNode->TypeGet();
@@ -8936,6 +9460,11 @@ void LinearScan::resolveRegisters()
GenTreePutArgSplit* splitArg = treeNode->AsPutArgSplit();
splitArg->SetRegSpillFlagByIdx(GTF_SPILL, currentRefPosition->getMultiRegIdx());
}
+ else if (treeNode->OperIsMultiRegOp())
+ {
+ GenTreeMultiRegOp* multiReg = treeNode->AsMultiRegOp();
+ multiReg->SetRegSpillFlagByIdx(GTF_SPILL, currentRefPosition->getMultiRegIdx());
+ }
#endif
}
@@ -9247,6 +9776,7 @@ void LinearScan::insertMove(
dst->gtLsraInfo.isLsraAdded = true;
}
dst->gtLsraInfo.isLocalDefUse = true;
+ dst->SetUnusedValue();
LIR::Range treeRange = LIR::SeqTree(compiler, dst);
LIR::Range& blockRange = LIR::AsRange(block);
@@ -9985,7 +10515,7 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock,
tempRegFlt = getTempRegForResolution(fromBlock, toBlock, TYP_FLOAT);
}
#else
- tempRegFlt = getTempRegForResolution(fromBlock, toBlock, TYP_FLOAT);
+ tempRegFlt = getTempRegForResolution(fromBlock, toBlock, TYP_FLOAT);
#endif
}
@@ -10266,6 +10796,43 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock,
}
}
+//------------------------------------------------------------------------
+// GetIndirSourceCount: Get the source registers for an indirection that might be contained.
+//
+// Arguments:
+// node - The node of interest
+//
+// Return Value:
+// The number of source registers used by the *parent* of this node.
+//
+int LinearScan::GetIndirSourceCount(GenTreeIndir* indirTree)
+{
+ GenTree* const addr = indirTree->gtOp1;
+ if (!addr->isContained())
+ {
+ return 1;
+ }
+ if (!addr->OperIs(GT_LEA))
+ {
+ return 0;
+ }
+
+ GenTreeAddrMode* const addrMode = addr->AsAddrMode();
+
+ unsigned srcCount = 0;
+ if ((addrMode->Base() != nullptr) && !addrMode->Base()->isContained())
+ {
+ srcCount++;
+ }
+ if (addrMode->Index() != nullptr)
+ {
+ // We never have a contained index.
+ assert(!addrMode->Index()->isContained());
+ srcCount++;
+ }
+ return srcCount;
+}
+
void TreeNodeInfo::Initialize(LinearScan* lsra, GenTree* node, LsraLocation location)
{
regMaskTP dstCandidates;
@@ -10273,7 +10840,11 @@ void TreeNodeInfo::Initialize(LinearScan* lsra, GenTree* node, LsraLocation loca
// if there is a reg indicated on the tree node, use that for dstCandidates
// the exception is the NOP, which sometimes show up around late args.
// TODO-Cleanup: get rid of those NOPs.
- if (node->gtRegNum == REG_NA || node->gtOper == GT_NOP)
+ if (node->gtRegNum == REG_STK)
+ {
+ dstCandidates = RBM_NONE;
+ }
+ else if (node->gtRegNum == REG_NA || node->gtOper == GT_NOP)
{
#ifdef ARM_SOFTFP
if (node->OperGet() == GT_PUTARG_REG)
diff --git a/src/jit/lsra.h b/src/jit/lsra.h
index dd9604f31b..767dff16a8 100644
--- a/src/jit/lsra.h
+++ b/src/jit/lsra.h
@@ -672,6 +672,8 @@ public:
// Used by Lowering when considering whether to split Longs, as well as by identifyCandidates().
bool isRegCandidate(LclVarDsc* varDsc);
+ bool isContainableMemoryOp(GenTree* node);
+
private:
// Determine which locals are candidates for allocation
void identifyCandidates();
@@ -697,11 +699,23 @@ private:
#ifdef _TARGET_ARM_
bool isSecondHalfReg(RegRecord* regRec, Interval* interval);
RegRecord* findAnotherHalfRegRec(RegRecord* regRec);
+ bool canSpillDoubleReg(RegRecord* physRegRecord,
+ LsraLocation refLocation,
+ unsigned* recentAssignedRefWeight,
+ unsigned farthestRefPosWeight);
+ void unassignDoublePhysReg(RegRecord* doubleRegRecord);
#endif
void updateAssignedInterval(RegRecord* reg, Interval* interval, RegisterType regType);
void updatePreviousInterval(RegRecord* reg, Interval* interval, RegisterType regType);
bool canRestorePreviousInterval(RegRecord* regRec, Interval* assignedInterval);
bool isAssignedToInterval(Interval* interval, RegRecord* regRec);
+ bool checkActiveInterval(Interval* interval, LsraLocation refLocation);
+ bool checkActiveIntervals(RegRecord* physRegRecord, LsraLocation refLocation, RegisterType registerType);
+ bool canSpillReg(RegRecord* physRegRecord,
+ LsraLocation refLocation,
+ unsigned* recentAssignedRefWeight,
+ unsigned farthestRefPosWeight);
+ bool isRegInUse(RegRecord* regRec, RefPosition* refPosition, LsraLocation* nextLocation);
RefType CheckBlockType(BasicBlock* block, BasicBlock* prevBlock);
@@ -737,29 +751,6 @@ private:
// Update reg state for an incoming register argument
void updateRegStateForArg(LclVarDsc* argDsc);
- inline void setTreeNodeInfo(GenTree* tree, TreeNodeInfo info)
- {
- tree->gtLsraInfo = info;
- tree->gtClearReg(compiler);
-
- DBEXEC(VERBOSE, info.dump(this));
- }
-
- inline void clearDstCount(GenTree* tree)
- {
- tree->gtLsraInfo.dstCount = 0;
- }
-
- inline void clearOperandCounts(GenTree* tree)
- {
- TreeNodeInfo& info = tree->gtLsraInfo;
- info.srcCount = 0;
- info.dstCount = 0;
-
- info.internalIntCount = 0;
- info.internalFloatCount = 0;
- }
-
inline bool isLocalDefUse(GenTree* tree)
{
return tree->gtLsraInfo.isLocalDefUse;
@@ -909,7 +900,10 @@ private:
assignPhysReg(getRegisterRecord(reg), interval);
}
+ bool isAssigned(RegRecord* regRec ARM_ARG(RegisterType newRegType));
+ bool isAssigned(RegRecord* regRec, LsraLocation lastLocation ARM_ARG(RegisterType newRegType));
void checkAndClearInterval(RegRecord* regRec, RefPosition* spillRefPosition);
+ void unassignPhysReg(RegRecord* regRec ARM_ARG(RegisterType newRegType));
void unassignPhysReg(RegRecord* regRec, RefPosition* spillRefPosition);
void unassignPhysRegNoSpill(RegRecord* reg);
void unassignPhysReg(regNumber reg)
@@ -1220,6 +1214,57 @@ private:
// Set of large vector (TYP_SIMD32 on AVX) variables to consider for callee-save registers.
VARSET_TP largeVectorCalleeSaveCandidateVars;
#endif // FEATURE_PARTIAL_SIMD_CALLEE_SAVE
+
+ //-----------------------------------------------------------------------
+ // TreeNodeInfo methods
+ //-----------------------------------------------------------------------
+
+ void TreeNodeInfoInit(GenTree* stmt);
+
+ void TreeNodeInfoInitCheckByteable(GenTree* tree);
+
+ void SetDelayFree(GenTree* delayUseSrc);
+
+ void TreeNodeInfoInitSimple(GenTree* tree);
+ int GetOperandSourceCount(GenTree* node);
+ int GetIndirSourceCount(GenTreeIndir* indirTree);
+ void HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs);
+
+ void TreeNodeInfoInitStoreLoc(GenTree* tree);
+ void TreeNodeInfoInitReturn(GenTree* tree);
+ void TreeNodeInfoInitShiftRotate(GenTree* tree);
+ void TreeNodeInfoInitPutArgReg(GenTreeUnOp* node);
+ void TreeNodeInfoInitCall(GenTreeCall* call);
+ void TreeNodeInfoInitCmp(GenTreePtr tree);
+ void TreeNodeInfoInitStructArg(GenTreePtr structArg);
+ void TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode);
+ void TreeNodeInfoInitModDiv(GenTree* tree);
+ void TreeNodeInfoInitIntrinsic(GenTree* tree);
+ void TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* tree);
+ void TreeNodeInfoInitIndir(GenTreeIndir* indirTree);
+ void TreeNodeInfoInitGCWriteBarrier(GenTree* tree);
+ void TreeNodeInfoInitCast(GenTree* tree);
+
+#ifdef _TARGET_X86_
+ bool ExcludeNonByteableRegisters(GenTree* tree);
+#endif
+
+#if defined(_TARGET_XARCH_)
+ // returns true if the tree can use the read-modify-write memory instruction form
+ bool isRMWRegOper(GenTreePtr tree);
+ void TreeNodeInfoInitMul(GenTreePtr tree);
+ void SetContainsAVXFlags(bool isFloatingPointType = true, unsigned sizeOfSIMDVector = 0);
+#endif // defined(_TARGET_XARCH_)
+
+#ifdef FEATURE_SIMD
+ void TreeNodeInfoInitSIMD(GenTreeSIMD* tree);
+#endif // FEATURE_SIMD
+
+ void TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode);
+#ifdef _TARGET_ARM_
+ void TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* tree);
+#endif
+ void TreeNodeInfoInitLclHeap(GenTree* tree);
};
/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
diff --git a/src/jit/lsraarm.cpp b/src/jit/lsraarm.cpp
index 053b593e20..09246b6e93 100644
--- a/src/jit/lsraarm.cpp
+++ b/src/jit/lsraarm.cpp
@@ -38,31 +38,26 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
+void LinearScan::TreeNodeInfoInitReturn(GenTree* tree)
{
- ContainCheckRet(tree->AsOp());
-
- TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
- GenTree* op1 = tree->gtGetOp1();
+ TreeNodeInfo* info = &(tree->gtLsraInfo);
+ GenTree* op1 = tree->gtGetOp1();
+ assert(info->dstCount == 0);
if (tree->TypeGet() == TYP_LONG)
{
assert((op1->OperGet() == GT_LONG) && op1->isContained());
GenTree* loVal = op1->gtGetOp1();
GenTree* hiVal = op1->gtGetOp2();
info->srcCount = 2;
- loVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_LO);
- hiVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_HI);
- info->dstCount = 0;
+ loVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_LO);
+ hiVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_HI);
}
else
{
regMaskTP useCandidates = RBM_NONE;
info->srcCount = ((tree->TypeGet() == TYP_VOID) || op1->isContained()) ? 0 : 1;
- info->dstCount = 0;
if (varTypeIsStruct(tree))
{
@@ -101,20 +96,16 @@ void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
if (useCandidates != RBM_NONE)
{
- tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, useCandidates);
+ tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, useCandidates);
}
}
}
-void Lowering::TreeNodeInfoInitLclHeap(GenTree* tree)
+void LinearScan::TreeNodeInfoInitLclHeap(GenTree* tree)
{
- ContainCheckLclHeap(tree->AsOp());
-
- TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
+ TreeNodeInfo* info = &(tree->gtLsraInfo);
- info->dstCount = 1;
+ assert(info->dstCount == 1);
// Need a variable number of temp regs (see genLclHeap() in codegenarm.cpp):
// Here '-' means don't care.
@@ -209,17 +200,21 @@ void Lowering::TreeNodeInfoInitLclHeap(GenTree* tree)
// requirements needed by LSRA to build the Interval Table (source,
// destination and internal [temp] register counts).
//
-void Lowering::TreeNodeInfoInit(GenTree* tree)
+void LinearScan::TreeNodeInfoInit(GenTree* tree)
{
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
-
unsigned kind = tree->OperKind();
TreeNodeInfo* info = &(tree->gtLsraInfo);
RegisterType registerType = TypeGet(tree);
- JITDUMP("TreeNodeInfoInit for: ");
- DISPNODE(tree);
+ if (tree->isContained())
+ {
+ info->dstCount = 0;
+ assert(info->srcCount == 0);
+ return;
+ }
+
+ // Set the default dstCount. This may be modified below.
+ info->dstCount = tree->IsValue() ? 1 : 0;
switch (tree->OperGet())
{
@@ -238,11 +233,11 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
info->srcCount = 0;
if (tree->TypeGet() != TYP_VOID && tree->gtOp.gtOp1 == nullptr)
{
- info->dstCount = 1;
+ assert(info->dstCount == 1);
}
else
{
- info->dstCount = 0;
+ assert(info->dstCount == 0);
}
break;
@@ -259,10 +254,10 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case CORINFO_INTRINSIC_Abs:
case CORINFO_INTRINSIC_Sqrt:
info->srcCount = 1;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
default:
- NYI_ARM("Lowering::TreeNodeInfoInit for GT_INTRINSIC");
+ NYI_ARM("LinearScan::TreeNodeInfoInit for GT_INTRINSIC");
break;
}
}
@@ -270,9 +265,8 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_CAST:
{
- ContainCheckCast(tree->AsCast());
info->srcCount = 1;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
// Non-overflow casts to/from float/double are done using SSE2 instructions
// and that allow the source operand to be either a reg or memop. Given the
@@ -306,15 +300,15 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// FloatToIntCast needs a temporary register
if (varTypeIsFloating(castOpType) && varTypeIsIntOrI(tree))
{
- info->setInternalCandidates(m_lsra, RBM_ALLFLOAT);
+ info->setInternalCandidates(this, RBM_ALLFLOAT);
info->internalFloatCount = 1;
info->isInternalRegDelayFree = true;
}
- CastInfo castInfo;
+ Lowering::CastInfo castInfo;
// Get information about the cast.
- getCastDescription(tree, &castInfo);
+ Lowering::getCastDescription(tree, &castInfo);
if (castInfo.requiresOverflowCheck)
{
@@ -355,31 +349,29 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_JTRUE:
info->srcCount = 0;
- info->dstCount = 0;
- l->clearDstCount(tree->gtOp.gtOp1);
+ assert(info->dstCount == 0);
break;
case GT_JMP:
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_SWITCH:
// This should never occur since switch nodes must not be visible at this
// point in the JIT.
info->srcCount = 0;
- info->dstCount = 0; // To avoid getting uninit errors.
noway_assert(!"Switch must be lowered at this point");
break;
case GT_JMPTABLE:
info->srcCount = 0;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
case GT_SWITCH_TABLE:
info->srcCount = 2;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_ASG:
@@ -387,7 +379,6 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_ASG_SUB:
noway_assert(!"We should never hit any assignment operator in lowering");
info->srcCount = 0;
- info->dstCount = 0;
break;
case GT_ADD_LO:
@@ -406,7 +397,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
assert(tree->gtOp.gtOp1->TypeGet() == tree->gtOp.gtOp2->TypeGet());
info->srcCount = 2;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
}
@@ -416,16 +407,15 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_AND:
case GT_OR:
case GT_XOR:
- ContainCheckBinary(tree->AsOp());
info->srcCount = tree->gtOp.gtOp2->isContained() ? 1 : 2;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
case GT_RETURNTRAP:
// this just turns into a compare of its child with an int
// + a conditional call
info->srcCount = 1;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_MUL:
@@ -442,7 +432,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_UDIV:
{
info->srcCount = 2;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
}
break;
@@ -458,7 +448,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_START_NONGC:
case GT_PROF_HOOK:
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_LONG:
@@ -466,19 +456,20 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
{
// An unused GT_LONG node needs to consume its sources.
info->srcCount = 2;
+ info->dstCount = 0;
}
else
{
- // Passthrough
+ // Passthrough. Should have been marked contained.
info->srcCount = 0;
+ assert(info->dstCount == 0);
}
- info->dstCount = 0;
break;
case GT_CNS_DBL:
info->srcCount = 0;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
if (tree->TypeGet() == TYP_FLOAT)
{
// An int register for float constant
@@ -499,20 +490,18 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
break;
case GT_RETFILT:
+ assert(info->dstCount == 0);
if (tree->TypeGet() == TYP_VOID)
{
info->srcCount = 0;
- info->dstCount = 0;
}
else
{
assert(tree->TypeGet() == TYP_INT);
info->srcCount = 1;
- info->dstCount = 0;
-
- info->setSrcCandidates(l, RBM_INTRET);
- tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, RBM_INTRET);
+ info->setSrcCandidates(this, RBM_INTRET);
+ tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, RBM_INTRET);
}
break;
@@ -523,7 +512,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
{
// Consumes arrLen & index - has no result
info->srcCount = 2;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
}
break;
@@ -531,12 +520,12 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// These must have been lowered to GT_ARR_INDEX
noway_assert(!"We should never see a GT_ARR_ELEM in lowering");
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_ARR_INDEX:
- info->srcCount = 2;
- info->dstCount = 1;
+ info->srcCount = 2;
+ assert(info->dstCount == 1);
info->internalIntCount = 1;
info->isInternalRegDelayFree = true;
@@ -547,10 +536,9 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
break;
case GT_ARR_OFFSET:
- ContainCheckArrOffset(tree->AsArrOffs());
// This consumes the offset, if any, the arrObj and the effective index,
// and produces the flattened offset for this dimension.
- info->dstCount = 1;
+ assert(info->dstCount == 1);
if (tree->gtArrOffs.gtOffset->isContained())
{
@@ -568,7 +556,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_LEA:
{
GenTreeAddrMode* lea = tree->AsAddrMode();
- unsigned offset = lea->gtOffset;
+ int offset = lea->Offset();
// This LEA is instantiating an address, so we set up the srcCount and dstCount here.
info->srcCount = 0;
@@ -580,7 +568,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
{
info->srcCount++;
}
- info->dstCount = 1;
+ assert(info->dstCount == 1);
// An internal register may be needed too; the logic here should be in sync with the
// genLeaInstruction()'s requirements for a such register.
@@ -605,12 +593,12 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_NEG:
info->srcCount = 1;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
case GT_NOT:
info->srcCount = 1;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
case GT_LSH:
@@ -633,8 +621,8 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
break;
case GT_CKFINITE:
- info->srcCount = 1;
- info->dstCount = 1;
+ info->srcCount = 1;
+ assert(info->dstCount == 1);
info->internalIntCount = 1;
break;
@@ -642,17 +630,26 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
TreeNodeInfoInitCall(tree->AsCall());
break;
+ case GT_ADDR:
+ {
+ // For a GT_ADDR, the child node should not be evaluated into a register
+ GenTreePtr child = tree->gtOp.gtOp1;
+ assert(!isCandidateLocalRef(child));
+ assert(child->isContained());
+ assert(info->dstCount == 1);
+ info->srcCount = 0;
+ }
+ break;
+
case GT_STORE_BLK:
case GT_STORE_OBJ:
case GT_STORE_DYN_BLK:
- LowerBlockStore(tree->AsBlk());
TreeNodeInfoInitBlockStore(tree->AsBlk());
break;
case GT_INIT_VAL:
// Always a passthrough of its child's value.
- info->srcCount = 0;
- info->dstCount = 0;
+ assert(!"INIT_VAL should always be contained");
break;
case GT_LCLHEAP:
@@ -661,8 +658,8 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_STOREIND:
{
- info->dstCount = 0;
- GenTree* src = tree->gtOp.gtOp2;
+ assert(info->dstCount == 0);
+ GenTree* src = tree->gtOp.gtOp2;
if (compiler->codeGen->gcInfo.gcIsWriteBarrierAsgNode(tree))
{
@@ -678,23 +675,23 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
break;
case GT_NULLCHECK:
- info->dstCount = 0;
- info->srcCount = 1;
- info->isLocalDefUse = true;
- // null check is an indirection on an addr
- TreeNodeInfoInitIndir(tree->AsIndir());
+ // It requires a internal register on ARM, as it is implemented as a load
+ assert(info->dstCount == 0);
+ assert(!tree->gtGetOp1()->isContained());
+ info->srcCount = 1;
+ info->internalIntCount = 1;
break;
case GT_IND:
- info->dstCount = 1;
+ assert(info->dstCount == 1);
info->srcCount = 1;
TreeNodeInfoInitIndir(tree->AsIndir());
break;
case GT_CATCH_ARG:
info->srcCount = 0;
- info->dstCount = 1;
- info->setDstCandidates(l, RBM_EXCEPTION_OBJECT);
+ assert(info->dstCount == 1);
+ info->setDstCandidates(this, RBM_EXCEPTION_OBJECT);
break;
case GT_CLS_VAR:
@@ -704,14 +701,13 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// It will produce a result of the type of the
// node, and use an internal register for the address.
- info->dstCount = 1;
+ assert(info->dstCount == 1);
assert((tree->gtFlags & (GTF_VAR_DEF | GTF_VAR_USEASG)) == 0);
info->internalIntCount = 1;
break;
case GT_COPY:
info->srcCount = 1;
- info->dstCount = 1;
#ifdef ARM_SOFTFP
// This case currently only occurs for double types that are passed as TYP_LONG;
// actual long types would have been decomposed by now.
@@ -719,23 +715,23 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
{
info->dstCount = 2;
}
-#endif
- break;
-
- case GT_PUTARG_REG:
-#ifdef ARM_SOFTFP
- // This case currently only occurs for double types that are passed as TYP_LONG;
- // actual long types would have been decomposed by now.
- if (tree->TypeGet() == TYP_LONG)
- {
- info->srcCount = 2;
- }
else
#endif
{
- info->srcCount = 1;
+ assert(info->dstCount == 1);
}
- info->dstCount = info->srcCount;
+ break;
+
+ case GT_PUTARG_SPLIT:
+ TreeNodeInfoInitPutArgSplit(tree->AsPutArgSplit());
+ break;
+
+ case GT_PUTARG_STK:
+ TreeNodeInfoInitPutArgStk(tree->AsPutArgStk());
+ break;
+
+ case GT_PUTARG_REG:
+ TreeNodeInfoInitPutArgReg(tree->AsUnOp());
break;
default:
@@ -755,15 +751,13 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_CLS_VAR_ADDR:
case GT_IL_OFFSET:
case GT_CNS_INT:
- case GT_PUTARG_STK:
case GT_LABEL:
case GT_PINVOKE_PROLOG:
case GT_JCC:
case GT_SETCC:
case GT_MEMORYBARRIER:
case GT_OBJ:
- case GT_PUTARG_SPLIT:
- info->dstCount = tree->IsValue() ? 1 : 0;
+ assert(info->dstCount == (tree->IsValue() ? 1 : 0));
if (kind & (GTK_CONST | GTK_LEAF))
{
info->srcCount = 0;
@@ -784,8 +778,18 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
unreached();
}
break;
+
+ case GT_INDEX_ADDR:
+ info->srcCount = 2;
+ info->dstCount = 1;
+ info->internalIntCount = 1;
+ break;
} // end switch (tree->OperGet())
+ if (tree->IsUnusedValue() && (info->dstCount != 0))
+ {
+ info->isLocalDefUse = true;
+ }
// We need to be sure that we've set info->srcCount and info->dstCount appropriately
assert((info->dstCount < 2) || tree->IsMultiRegNode());
}
diff --git a/src/jit/lsraarm64.cpp b/src/jit/lsraarm64.cpp
index 37391675b0..cea80e2093 100644
--- a/src/jit/lsraarm64.cpp
+++ b/src/jit/lsraarm64.cpp
@@ -44,18 +44,21 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// requirements needed by LSRA to build the Interval Table (source,
// destination and internal [temp] register counts).
//
-void Lowering::TreeNodeInfoInit(GenTree* tree)
+void LinearScan::TreeNodeInfoInit(GenTree* tree)
{
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
-
unsigned kind = tree->OperKind();
TreeNodeInfo* info = &(tree->gtLsraInfo);
RegisterType registerType = TypeGet(tree);
- JITDUMP("TreeNodeInfoInit for: ");
- DISPNODE(tree);
- JITDUMP("\n");
+ if (tree->isContained())
+ {
+ info->dstCount = 0;
+ assert(info->srcCount == 0);
+ return;
+ }
+
+ // Set the default dstCount. This may be modified below.
+ info->dstCount = tree->IsValue() ? 1 : 0;
switch (tree->OperGet())
{
@@ -63,7 +66,6 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
GenTree* op2;
default:
- info->dstCount = tree->IsValue() ? 1 : 0;
if (kind & (GTK_CONST | GTK_LEAF))
{
info->srcCount = 0;
@@ -88,7 +90,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_STORE_LCL_FLD:
case GT_STORE_LCL_VAR:
info->srcCount = 1;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
TreeNodeInfoInitStoreLoc(tree->AsLclVarCommon());
break;
@@ -99,12 +101,12 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_START_NONGC:
case GT_PROF_HOOK:
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_CNS_DBL:
info->srcCount = 0;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
{
GenTreeDblCon* dblConst = tree->AsDblCon();
double constValue = dblConst->gtDblCon.gtDconVal;
@@ -126,7 +128,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_QMARK:
case GT_COLON:
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
unreached();
break;
@@ -138,17 +140,17 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
if (tree->TypeGet() == TYP_VOID)
{
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
}
else
{
assert(tree->TypeGet() == TYP_INT);
info->srcCount = 1;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
- info->setSrcCandidates(l, RBM_INTRET);
- tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, RBM_INTRET);
+ info->setSrcCandidates(this, RBM_INTRET);
+ tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, RBM_INTRET);
}
break;
@@ -159,42 +161,40 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
info->srcCount = 0;
if (tree->TypeGet() != TYP_VOID && tree->gtOp.gtOp1 == nullptr)
{
- info->dstCount = 1;
+ assert(info->dstCount == 1);
}
else
{
- info->dstCount = 0;
+ assert(info->dstCount == 0);
}
break;
case GT_JTRUE:
info->srcCount = 0;
- info->dstCount = 0;
- l->clearDstCount(tree->gtOp.gtOp1);
+ assert(info->dstCount == 0);
break;
case GT_JMP:
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_SWITCH:
// This should never occur since switch nodes must not be visible at this
// point in the JIT.
info->srcCount = 0;
- info->dstCount = 0; // To avoid getting uninit errors.
noway_assert(!"Switch must be lowered at this point");
break;
case GT_JMPTABLE:
info->srcCount = 0;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
case GT_SWITCH_TABLE:
info->srcCount = 2;
info->internalIntCount = 1;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_ASG:
@@ -202,7 +202,6 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_ASG_SUB:
noway_assert(!"We should never hit any assignment operator in lowering");
info->srcCount = 0;
- info->dstCount = 0;
break;
case GT_ADD:
@@ -217,9 +216,6 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
assert(tree->gtOp.gtOp1->TypeGet() == tree->gtOp.gtOp2->TypeGet());
info->srcCount = 2;
- info->dstCount = 1;
-
- break;
}
__fallthrough;
@@ -227,16 +223,15 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_AND:
case GT_OR:
case GT_XOR:
- ContainCheckBinary(tree->AsOp());
info->srcCount = tree->gtOp.gtOp2->isContained() ? 1 : 2;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
case GT_RETURNTRAP:
// this just turns into a compare of its child with an int
// + a conditional call
info->srcCount = 1;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_MOD:
@@ -259,7 +254,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_UDIV:
{
info->srcCount = 2;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
}
break;
@@ -277,7 +272,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
assert(op1->TypeGet() == tree->TypeGet());
info->srcCount = 1;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
}
break;
@@ -294,7 +289,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// see CodeGen::genIntToIntCast()
info->srcCount = 1;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
// Non-overflow casts to/from float/double are done using SSE2 instructions
// and that allow the source operand to be either a reg or memop. Given the
@@ -320,10 +315,9 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
#endif // DEBUG
// Some overflow checks need a temp reg
- CastInfo castInfo;
-
+ Lowering::CastInfo castInfo;
// Get information about the cast.
- getCastDescription(tree, &castInfo);
+ Lowering::getCastDescription(tree, &castInfo);
if (castInfo.requiresOverflowCheck)
{
@@ -347,12 +341,12 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_NEG:
info->srcCount = 1;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
case GT_NOT:
info->srcCount = 1;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
case GT_LSH:
@@ -368,27 +362,36 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_LE:
case GT_GE:
case GT_GT:
+ case GT_TEST_EQ:
+ case GT_TEST_NE:
TreeNodeInfoInitCmp(tree);
break;
case GT_CKFINITE:
- info->srcCount = 1;
- info->dstCount = 1;
+ info->srcCount = 1;
+ assert(info->dstCount == 1);
info->internalIntCount = 1;
break;
case GT_CMPXCHG:
info->srcCount = 3;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
// TODO-ARM64-NYI
NYI("CMPXCHG");
break;
case GT_LOCKADD:
- ContainCheckBinary(tree->AsOp());
info->srcCount = tree->gtOp.gtOp2->isContained() ? 1 : 2;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
+ break;
+
+ case GT_PUTARG_STK:
+ TreeNodeInfoInitPutArgStk(tree->AsPutArgStk());
+ break;
+
+ case GT_PUTARG_REG:
+ TreeNodeInfoInitPutArgReg(tree->AsUnOp());
break;
case GT_CALL:
@@ -399,10 +402,10 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
{
// For a GT_ADDR, the child node should not be evaluated into a register
GenTreePtr child = tree->gtOp.gtOp1;
- assert(!l->isCandidateLocalRef(child));
- MakeSrcContained(tree, child);
+ assert(!isCandidateLocalRef(child));
+ assert(child->isContained());
+ assert(info->dstCount == 1);
info->srcCount = 0;
- info->dstCount = 1;
}
break;
@@ -411,26 +414,22 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// These should all be eliminated prior to Lowering.
assert(!"Non-store block node in Lowering");
info->srcCount = 0;
- info->dstCount = 0;
break;
case GT_STORE_BLK:
case GT_STORE_OBJ:
case GT_STORE_DYN_BLK:
- LowerBlockStore(tree->AsBlk());
TreeNodeInfoInitBlockStore(tree->AsBlk());
break;
case GT_INIT_VAL:
// Always a passthrough of its child's value.
- info->srcCount = 0;
- info->dstCount = 0;
+ assert(!"INIT_VAL should always be contained");
break;
case GT_LCLHEAP:
{
- ContainCheckLclHeap(tree->AsOp());
- info->dstCount = 1;
+ assert(info->dstCount == 1);
// Need a variable number of temp regs (see genLclHeap() in codegenamd64.cpp):
// Here '-' means don't care.
@@ -536,7 +535,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
GenTreeBoundsChk* node = tree->AsBoundsChk();
// Consumes arrLen & index - has no result
info->srcCount = 2;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
GenTree* intCns = nullptr;
GenTree* other = nullptr;
@@ -555,12 +554,12 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// These must have been lowered to GT_ARR_INDEX
noway_assert(!"We should never see a GT_ARR_ELEM in lowering");
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_ARR_INDEX:
- info->srcCount = 2;
- info->dstCount = 1;
+ info->srcCount = 2;
+ assert(info->dstCount == 1);
info->internalIntCount = 1;
info->isInternalRegDelayFree = true;
@@ -571,11 +570,10 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
break;
case GT_ARR_OFFSET:
- ContainCheckArrOffset(tree->AsArrOffs());
// This consumes the offset, if any, the arrObj and the effective index,
// and produces the flattened offset for this dimension.
- info->srcCount = tree->gtArrOffs.gtOffset->isContained() ? 2 : 3;
- info->dstCount = 1;
+ info->srcCount = tree->gtArrOffs.gtOffset->isContained() ? 2 : 3;
+ assert(info->dstCount == 1);
info->internalIntCount = 1;
break;
@@ -585,10 +583,9 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
GenTree* base = lea->Base();
GenTree* index = lea->Index();
- unsigned cns = lea->gtOffset;
+ int cns = lea->Offset();
- // This LEA is instantiating an address,
- // so we set up the srcCount and dstCount here.
+ // This LEA is instantiating an address, so we set up the srcCount here.
info->srcCount = 0;
if (base != nullptr)
{
@@ -598,7 +595,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
{
info->srcCount++;
}
- info->dstCount = 1;
+ assert(info->dstCount == 1);
// On ARM64 we may need a single internal register
// (when both conditions are true then we still only need a single internal register)
@@ -617,7 +614,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_STOREIND:
{
- info->dstCount = 0;
+ assert(info->dstCount == 0);
if (compiler->codeGen->gcInfo.gcIsWriteBarrierAsgNode(tree))
{
@@ -635,23 +632,23 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
break;
case GT_NULLCHECK:
- info->dstCount = 0;
- info->srcCount = 1;
- info->isLocalDefUse = true;
- // null check is an indirection on an addr
- TreeNodeInfoInitIndir(tree->AsIndir());
+ // Unlike ARM, ARM64 implements NULLCHECK as a load to REG_ZR, so no internal register
+ // is required, and it is not a localDefUse.
+ assert(info->dstCount == 0);
+ assert(!tree->gtGetOp1()->isContained());
+ info->srcCount = 1;
break;
case GT_IND:
- info->dstCount = 1;
+ assert(info->dstCount == 1);
info->srcCount = 1;
TreeNodeInfoInitIndir(tree->AsIndir());
break;
case GT_CATCH_ARG:
info->srcCount = 0;
- info->dstCount = 1;
- info->setDstCandidates(l, RBM_EXCEPTION_OBJECT);
+ assert(info->dstCount == 1);
+ info->setDstCandidates(this, RBM_EXCEPTION_OBJECT);
break;
case GT_CLS_VAR:
@@ -661,12 +658,22 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// It will produce a result of the type of the
// node, and use an internal register for the address.
- info->dstCount = 1;
+ assert(info->dstCount == 1);
assert((tree->gtFlags & (GTF_VAR_DEF | GTF_VAR_USEASG)) == 0);
info->internalIntCount = 1;
break;
+
+ case GT_INDEX_ADDR:
+ info->srcCount = 2;
+ info->dstCount = 1;
+ info->internalIntCount = 1;
+ break;
} // end switch (tree->OperGet())
+ if (tree->IsUnusedValue() && (info->dstCount != 0))
+ {
+ info->isLocalDefUse = true;
+ }
// We need to be sure that we've set info->srcCount and info->dstCount appropriately
assert((info->dstCount < 2) || tree->IsMultiRegCall());
}
@@ -680,19 +687,15 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
+void LinearScan::TreeNodeInfoInitReturn(GenTree* tree)
{
- ContainCheckRet(tree->AsOp());
-
- TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
+ TreeNodeInfo* info = &(tree->gtLsraInfo);
GenTree* op1 = tree->gtGetOp1();
regMaskTP useCandidates = RBM_NONE;
info->srcCount = ((tree->TypeGet() == TYP_VOID) || op1->isContained()) ? 0 : 1;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
if (varTypeIsStruct(tree))
{
@@ -731,7 +734,7 @@ void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
if (useCandidates != RBM_NONE)
{
- tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, useCandidates);
+ tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, useCandidates);
}
}
diff --git a/src/jit/lsraarmarch.cpp b/src/jit/lsraarmarch.cpp
index d6c372f3e8..ac722e27bf 100644
--- a/src/jit/lsraarmarch.cpp
+++ b/src/jit/lsraarmarch.cpp
@@ -39,13 +39,12 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// - Setting the appropriate candidates for a store of a multi-reg call return value.
// - Handling of contained immediates.
//
-void Lowering::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
+void LinearScan::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
{
- ContainCheckStoreLoc(storeLoc);
TreeNodeInfo* info = &(storeLoc->gtLsraInfo);
GenTree* op1 = storeLoc->gtGetOp1();
- info->dstCount = 0;
+ assert(info->dstCount == 0);
#ifdef _TARGET_ARM_
if (varTypeIsLong(op1))
{
@@ -71,8 +70,8 @@ void Lowering::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
info->srcCount = retTypeDesc->GetReturnRegCount();
// Call node srcCandidates = Bitwise-OR(allregs(GetReturnRegType(i))) for all i=0..RetRegCount-1
- regMaskTP srcCandidates = m_lsra->allMultiRegCallNodeRegs(call);
- op1->gtLsraInfo.setSrcCandidates(m_lsra, srcCandidates);
+ regMaskTP srcCandidates = allMultiRegCallNodeRegs(call);
+ op1->gtLsraInfo.setSrcCandidates(this, srcCandidates);
}
else
{
@@ -89,17 +88,22 @@ void Lowering::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCmp(GenTreePtr tree)
+void LinearScan::TreeNodeInfoInitCmp(GenTreePtr tree)
{
- ContainCheckCompare(tree->AsOp());
-
TreeNodeInfo* info = &(tree->gtLsraInfo);
info->srcCount = tree->gtOp.gtOp2->isContained() ? 1 : 2;
- info->dstCount = tree->OperIs(GT_CMP) ? 0 : 1;
+ if (info->isNoRegCompare)
+ {
+ info->dstCount = 0;
+ }
+ else
+ {
+ assert((info->dstCount == 1) || tree->OperIs(GT_CMP, GT_TEST_EQ, GT_TEST_NE));
+ }
}
-void Lowering::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
+void LinearScan::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
{
GenTreePtr dst = tree;
GenTreePtr addr = tree->gtOp.gtOp1;
@@ -133,15 +137,15 @@ void Lowering::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
// the 'addr' goes into x14 (REG_WRITE_BARRIER_DST_BYREF)
// the 'src' goes into x15 (REG_WRITE_BARRIER)
//
- addr->gtLsraInfo.setSrcCandidates(m_lsra, RBM_WRITE_BARRIER_DST_BYREF);
- src->gtLsraInfo.setSrcCandidates(m_lsra, RBM_WRITE_BARRIER);
+ addr->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_DST_BYREF);
+ src->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER);
#else
// For the standard JIT Helper calls
// op1 goes into REG_ARG_0 and
// op2 goes into REG_ARG_1
//
- addr->gtLsraInfo.setSrcCandidates(m_lsra, RBM_ARG_0);
- src->gtLsraInfo.setSrcCandidates(m_lsra, RBM_ARG_1);
+ addr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
+ src->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
#endif // NOGC_WRITE_BARRIERS
// Both src and dst must reside in a register, which they should since we haven't set
@@ -155,12 +159,10 @@ void Lowering::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
// of an indirection operation.
//
// Arguments:
-// indirTree - GT_IND, GT_STOREIND, block node or GT_NULLCHECK gentree node
+// indirTree - GT_IND, GT_STOREIND or block gentree node
//
-void Lowering::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
+void LinearScan::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
{
- ContainCheckIndir(indirTree);
-
// If this is the rhs of a block copy (i.e. non-enregisterable struct),
// it has no register requirements.
if (indirTree->TypeGet() == TYP_STRUCT)
@@ -174,7 +176,7 @@ void Lowering::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
GenTree* addr = indirTree->Addr();
GenTree* index = nullptr;
- unsigned cns = 0;
+ int cns = 0;
#ifdef _TARGET_ARM_
// Unaligned loads/stores for floating point values must first be loaded into integer register(s)
@@ -206,7 +208,7 @@ void Lowering::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
assert(addr->OperGet() == GT_LEA);
GenTreeAddrMode* lea = addr->AsAddrMode();
index = lea->Index();
- cns = lea->gtOffset;
+ cns = lea->Offset();
// On ARM we may need a single internal register
// (when both conditions are true then we still only need a single internal register)
@@ -232,12 +234,9 @@ void Lowering::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitShiftRotate(GenTree* tree)
+void LinearScan::TreeNodeInfoInitShiftRotate(GenTree* tree)
{
- ContainCheckShiftRotate(tree->AsOp());
-
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
GenTreePtr shiftBy = tree->gtOp.gtOp2;
info->srcCount = shiftBy->isContained() ? 1 : 2;
@@ -284,16 +283,14 @@ void Lowering::TreeNodeInfoInitShiftRotate(GenTree* tree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitPutArgReg(
- GenTreeUnOp* node, regNumber argReg, TreeNodeInfo& info, bool isVarArgs, bool* callHasFloatRegArgs)
+void LinearScan::TreeNodeInfoInitPutArgReg(GenTreeUnOp* node)
{
assert(node != nullptr);
assert(node->OperIsPutArgReg());
+ node->gtLsraInfo.srcCount = 1;
+ regNumber argReg = node->gtRegNum;
assert(argReg != REG_NA);
- // Each register argument corresponds to one source.
- info.srcCount++;
-
// Set the register requirements for the node.
regMaskTP argMask = genRegMask(argReg);
#ifdef ARM_SOFTFP
@@ -301,19 +298,49 @@ void Lowering::TreeNodeInfoInitPutArgReg(
// The actual `long` types must have been transformed as a field list with two fields.
if (node->TypeGet() == TYP_LONG)
{
- info.srcCount++;
+ node->gtLsraInfo.srcCount++;
+ node->gtLsraInfo.dstCount = node->gtLsraInfo.srcCount;
assert(genRegArgNext(argReg) == REG_NEXT(argReg));
argMask |= genRegMask(REG_NEXT(argReg));
}
#endif // ARM_SOFTFP
- node->gtLsraInfo.setDstCandidates(m_lsra, argMask);
- node->gtLsraInfo.setSrcCandidates(m_lsra, argMask);
+ node->gtLsraInfo.setDstCandidates(this, argMask);
+ node->gtLsraInfo.setSrcCandidates(this, argMask);
// To avoid redundant moves, have the argument operand computed in the
// register in which the argument is passed to the call.
- node->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(m_lsra, m_lsra->getUseCandidates(node));
+ node->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, getUseCandidates(node));
+}
+
+//------------------------------------------------------------------------
+// HandleFloatVarArgs: Handle additional register requirements for a varargs call
+//
+// Arguments:
+// call - The call node of interest
+// argNode - The current argument
+//
+// Return Value:
+// None.
+//
+// Notes:
+// In the case of a varargs call, the ABI dictates that if we have floating point args,
+// we must pass the enregistered arguments in both the integer and floating point registers.
+// Since the integer register is not associated with the arg node, we will reserve it as
+// an internal register on the call so that it is not used during the evaluation of the call node
+// (e.g. for the target).
+void LinearScan::HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs)
+{
+#if FEATURE_VARARG
+ if (call->IsVarargs() && varTypeIsFloating(argNode))
+ {
+ *callHasFloatRegArgs = true;
- *callHasFloatRegArgs |= varTypeIsFloating(node->TypeGet());
+ regNumber argReg = argNode->gtRegNum;
+ regNumber targetReg = compiler->getCallArgIntRegister(argReg);
+ call->gtLsraInfo.setInternalIntCount(call->gtLsraInfo.internalIntCount + 1);
+ call->gtLsraInfo.addInternalCandidates(this, genRegMask(targetReg));
+ }
+#endif // FEATURE_VARARG
}
//------------------------------------------------------------------------
@@ -325,11 +352,9 @@ void Lowering::TreeNodeInfoInitPutArgReg(
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
+void LinearScan::TreeNodeInfoInitCall(GenTreeCall* call)
{
TreeNodeInfo* info = &(call->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
bool hasMultiRegRetVal = false;
ReturnTypeDesc* retTypeDesc = nullptr;
@@ -380,7 +405,7 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
#ifdef _TARGET_ARM64_
// Fast tail call - make sure that call target is always computed in IP0
// so that epilog sequence can generate "br xip0" to achieve fast tail call.
- ctrlExpr->gtLsraInfo.setSrcCandidates(l, genRegMask(REG_IP0));
+ ctrlExpr->gtLsraInfo.setSrcCandidates(this, genRegMask(REG_IP0));
#endif // _TARGET_ARM64_
}
}
@@ -400,47 +425,30 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
{
// The ARM CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
// TCB in REG_PINVOKE_TCB. fgMorphCall() sets the correct argument registers.
- info->setDstCandidates(l, RBM_PINVOKE_TCB);
+ info->setDstCandidates(this, RBM_PINVOKE_TCB);
}
else
#endif // _TARGET_ARM_
if (hasMultiRegRetVal)
{
assert(retTypeDesc != nullptr);
- info->setDstCandidates(l, retTypeDesc->GetABIReturnRegs());
+ info->setDstCandidates(this, retTypeDesc->GetABIReturnRegs());
}
else if (varTypeIsFloating(registerType))
{
- info->setDstCandidates(l, RBM_FLOATRET);
+ info->setDstCandidates(this, RBM_FLOATRET);
}
else if (registerType == TYP_LONG)
{
- info->setDstCandidates(l, RBM_LNGRET);
+ info->setDstCandidates(this, RBM_LNGRET);
}
else
{
- info->setDstCandidates(l, RBM_INTRET);
- }
-
- // If there is an explicit this pointer, we don't want that node to produce anything
- // as it is redundant
- if (call->gtCallObjp != nullptr)
- {
- GenTreePtr thisPtrNode = call->gtCallObjp;
-
- if (thisPtrNode->gtOper == GT_PUTARG_REG)
- {
- l->clearOperandCounts(thisPtrNode);
- thisPtrNode->SetContained();
- l->clearDstCount(thisPtrNode->gtOp.gtOp1);
- }
- else
- {
- l->clearDstCount(thisPtrNode);
- }
+ info->setDstCandidates(this, RBM_INTRET);
}
// First, count reg args
+ // Each register argument corresponds to one source.
bool callHasFloatRegArgs = false;
for (GenTreePtr list = call->gtCallLateArgs; list; list = list->MoveNext())
@@ -449,29 +457,62 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
GenTreePtr argNode = list->Current();
+#ifdef DEBUG
+ // During TreeNodeInfoInit, we only use the ArgTabEntry for validation,
+ // as getting it is rather expensive.
fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
+ regNumber argReg = curArgTabEntry->regNum;
assert(curArgTabEntry);
+#endif
- if (curArgTabEntry->regNum == REG_STK)
+ if (argNode->gtOper == GT_PUTARG_STK)
{
// late arg that is not passed in a register
- assert(argNode->gtOper == GT_PUTARG_STK);
+ assert(curArgTabEntry->regNum == REG_STK);
+ GenTree* putArgChild = argNode->gtGetOp1();
+ if (!varTypeIsStruct(putArgChild) && !putArgChild->OperIs(GT_FIELD_LIST))
+ {
+#ifdef ARM_SOFTFP
+ // The `double` types have been transformed to `long` on armel, while the actual longs
+ // have been decomposed.
+ const bool isDouble = putArgChild->TypeGet() == TYP_LONG;
+ if (isDouble)
+ {
+ argNode->gtLsraInfo.srcCount = 2;
+ }
+#endif // ARM_SOFT_FP
- TreeNodeInfoInitPutArgStk(argNode->AsPutArgStk(), curArgTabEntry);
+#ifdef DEBUG
+// Validate the slot count for this arg.
+#ifdef _TARGET_ARM_
+#ifndef ARM_SOFTFP
+ const bool isDouble = (curArgTabEntry->numSlots == 2) && (putArgChild->TypeGet() == TYP_DOUBLE);
+#endif // !ARM_SOFTFP
+
+ // We must not have a multi-reg struct; double uses 2 slots and isn't a multi-reg struct
+ assert((curArgTabEntry->numSlots == 1) || isDouble);
+
+#else // !_TARGET_ARM_
+ // We must not have a multi-reg struct
+ assert(curArgTabEntry->numSlots == 1);
+#endif // !_TARGET_ARM_
+#endif
+ }
continue;
}
// A GT_FIELD_LIST has a TYP_VOID, but is used to represent a multireg struct
if (argNode->OperGet() == GT_FIELD_LIST)
{
- argNode->SetContained();
+ assert(argNode->isContained());
// There could be up to 2-4 PUTARG_REGs in the list (3 or 4 can only occur for HFAs)
- regNumber argReg = curArgTabEntry->regNum;
for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest())
{
- TreeNodeInfoInitPutArgReg(entry->Current()->AsUnOp(), argReg, *info, false, &callHasFloatRegArgs);
-
+ info->srcCount++;
+#ifdef DEBUG
+ assert(entry->Current()->OperIs(GT_PUTARG_REG));
+ assert(entry->Current()->gtRegNum == argReg);
// Update argReg for the next putarg_reg (if any)
argReg = genRegArgNext(argReg);
@@ -482,18 +523,30 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
argReg = genRegArgNext(argReg);
}
#endif // _TARGET_ARM_
+#endif
}
}
#ifdef _TARGET_ARM_
else if (argNode->OperGet() == GT_PUTARG_SPLIT)
{
fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
- TreeNodeInfoInitPutArgSplit(argNode->AsPutArgSplit(), *info, curArgTabEntry);
+ info->srcCount += argNode->AsPutArgSplit()->gtNumRegs;
}
#endif
else
{
- TreeNodeInfoInitPutArgReg(argNode->AsUnOp(), curArgTabEntry->regNum, *info, false, &callHasFloatRegArgs);
+ assert(argNode->OperIs(GT_PUTARG_REG));
+ assert(argNode->gtRegNum == argReg);
+ HandleFloatVarArgs(call, argNode, &callHasFloatRegArgs);
+ info->srcCount++;
+#ifdef ARM_SOFTFP
+ // The `double` types have been transformed to `long` on armel,
+ // while the actual long types have been decomposed.
+ if (argNode->TypeGet() == TYP_LONG)
+ {
+ info->srcCount++;
+ }
+#endif // ARM_SOFTFP
}
}
@@ -512,31 +565,25 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
// Skip arguments that have been moved to the Late Arg list
if (!(args->gtFlags & GTF_LATE_ARG))
{
+#ifdef DEBUG
+ fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, arg);
+ assert(curArgTabEntry);
+#endif
if (arg->gtOper == GT_PUTARG_STK)
{
- fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, arg);
- assert(curArgTabEntry);
-
assert(curArgTabEntry->regNum == REG_STK);
-
- TreeNodeInfoInitPutArgStk(arg->AsPutArgStk(), curArgTabEntry);
}
#ifdef _TARGET_ARM_
else if (arg->OperGet() == GT_PUTARG_SPLIT)
{
- fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, arg);
- TreeNodeInfoInitPutArgSplit(arg->AsPutArgSplit(), *info, curArgTabEntry);
+ assert(arg->AsPutArgSplit()->gtNumRegs == curArgTabEntry->numRegs);
+ info->srcCount += arg->gtLsraInfo.dstCount;
}
#endif
else
{
TreeNodeInfo* argInfo = &(arg->gtLsraInfo);
- if (argInfo->dstCount != 0)
- {
- argInfo->isLocalDefUse = true;
- }
-
- argInfo->dstCount = 0;
+ assert((argInfo->dstCount == 0) || (argInfo->isLocalDefUse));
}
}
args = args->gtOp.gtOp2;
@@ -551,7 +598,7 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
// Don't assign the call target to any of the argument registers because
// we will use them to also pass floating point arguments as required
// by Arm64 ABI.
- ctrlExpr->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~(RBM_ARG_REGS));
+ ctrlExpr->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~(RBM_ARG_REGS));
}
#ifdef _TARGET_ARM_
@@ -576,16 +623,13 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
// Notes:
// Set the child node(s) to be contained when we have a multireg arg
//
-void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info)
+void LinearScan::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode)
{
assert(argNode->gtOper == GT_PUTARG_STK);
GenTreePtr putArgChild = argNode->gtOp.gtOp1;
- // Initialize 'argNode' as not contained, as this is both the default case
- // and how MakeSrcContained expects to find things setup.
- //
- argNode->gtLsraInfo.srcCount = 1;
+ argNode->gtLsraInfo.srcCount = 0;
argNode->gtLsraInfo.dstCount = 0;
// Do we have a TYP_STRUCT argument (or a GT_FIELD_LIST), if so it must be a multireg pass-by-value struct
@@ -595,9 +639,12 @@ void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntr
if (putArgChild->OperGet() == GT_FIELD_LIST)
{
+ assert(putArgChild->isContained());
// We consume all of the items in the GT_FIELD_LIST
- argNode->gtLsraInfo.srcCount = info->numSlots;
- putArgChild->SetContained();
+ for (GenTreeFieldList* current = putArgChild->AsFieldList(); current != nullptr; current = current->Rest())
+ {
+ argNode->gtLsraInfo.srcCount++;
+ }
}
else
{
@@ -617,40 +664,32 @@ void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntr
// We will generate all of the code for the GT_PUTARG_STK, the GT_OBJ and the GT_LCL_VAR_ADDR
// as one contained operation
//
- MakeSrcContained(putArgChild, objChild);
- putArgChild->gtLsraInfo.srcCount--;
+ assert(objChild->isContained());
}
}
- // We will generate all of the code for the GT_PUTARG_STK and it's child node
+ // We will generate all of the code for the GT_PUTARG_STK and its child node
// as one contained operation
//
argNode->gtLsraInfo.srcCount = putArgChild->gtLsraInfo.srcCount;
- MakeSrcContained(argNode, putArgChild);
+ assert(putArgChild->isContained());
}
}
else
{
-#ifdef _TARGET_ARM_
-
-#ifdef ARM_SOFTFP
- // The `double` types have been transformed to `long` on armel.
- const bool isDouble = (info->numSlots == 2) && (putArgChild->TypeGet() == TYP_LONG);
+#if defined(_TARGET_ARM_) && defined(ARM_SOFTFP)
+ // The `double` types have been transformed to `long` on armel,
+ // while the actual long types have been decomposed.
+ const bool isDouble = (putArgChild->TypeGet() == TYP_LONG);
if (isDouble)
{
argNode->gtLsraInfo.srcCount = 2;
}
-#else // !ARM_SOFTFP
- const bool isDouble = (info->numSlots == 2) && (putArgChild->TypeGet() == TYP_DOUBLE);
-#endif // !ARM_SOFTFP
-
- // We must not have a multi-reg struct; double uses 2 slots and isn't a multi-reg struct
- assert((info->numSlots == 1) || isDouble);
-
-#else // !_TARGET_ARM_
- // We must not have a multi-reg struct
- assert(info->numSlots == 1);
-#endif // !_TARGET_ARM_
+ else
+#endif // defined(_TARGET_ARM_) && defined(ARM_SOFTFP)
+ {
+ argNode->gtLsraInfo.srcCount = 1;
+ }
}
}
@@ -667,23 +706,23 @@ void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntr
// Notes:
// Set the child node(s) to be contained
//
-void Lowering::TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* argNode, TreeNodeInfo& info, fgArgTabEntryPtr argInfo)
+void LinearScan::TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* argNode)
{
assert(argNode->gtOper == GT_PUTARG_SPLIT);
GenTreePtr putArgChild = argNode->gtOp.gtOp1;
// Registers for split argument corresponds to source
- argNode->gtLsraInfo.dstCount = argInfo->numRegs;
- info.srcCount += argInfo->numRegs;
+ argNode->gtLsraInfo.dstCount = argNode->gtNumRegs;
- regNumber argReg = argInfo->regNum;
+ regNumber argReg = argNode->gtRegNum;
regMaskTP argMask = RBM_NONE;
- for (unsigned i = 0; i < argInfo->numRegs; i++)
+ for (unsigned i = 0; i < argNode->gtNumRegs; i++)
{
argMask |= genRegMask((regNumber)((unsigned)argReg + i));
}
- argNode->gtLsraInfo.setDstCandidates(m_lsra, argMask);
+ argNode->gtLsraInfo.setDstCandidates(this, argMask);
+ argNode->gtLsraInfo.setSrcCandidates(this, argMask);
if (putArgChild->OperGet() == GT_FIELD_LIST)
{
@@ -691,32 +730,35 @@ void Lowering::TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* argNode, TreeNode
// 1. Consume all of the items in the GT_FIELD_LIST (source)
// 2. Store to target slot and move to target registers (destination) from source
//
- argNode->gtLsraInfo.srcCount = argInfo->numRegs + argInfo->numSlots;
+ unsigned slotCount = 0;
// To avoid redundant moves, have the argument operand computed in the
// register in which the argument is passed to the call.
GenTreeFieldList* fieldListPtr = putArgChild->AsFieldList();
for (unsigned idx = 0; fieldListPtr != nullptr; fieldListPtr = fieldListPtr->Rest(), idx++)
{
- if (idx < argInfo->numRegs)
+ if (idx < argNode->gtNumRegs)
{
GenTreePtr node = fieldListPtr->gtGetOp1();
- node->gtLsraInfo.setSrcCandidates(m_lsra, genRegMask((regNumber)((unsigned)argReg + idx)));
+ node->gtLsraInfo.setSrcCandidates(this, genRegMask((regNumber)((unsigned)argReg + idx)));
+ }
+ else
+ {
+ slotCount++;
}
}
-
- putArgChild->SetContained();
+ argNode->gtLsraInfo.srcCount = argNode->gtNumRegs + slotCount;
+ assert(putArgChild->isContained());
}
else
{
assert(putArgChild->TypeGet() == TYP_STRUCT);
assert(putArgChild->OperGet() == GT_OBJ);
- // We could use a ldr/str sequence so we need a internal register
- argNode->gtLsraInfo.srcCount = 1;
+ // We can use a ldr/str sequence so we need an internal register
argNode->gtLsraInfo.internalIntCount = 1;
regMaskTP internalMask = RBM_ALLINT & ~argMask;
- argNode->gtLsraInfo.setInternalCandidates(m_lsra, internalMask);
+ argNode->gtLsraInfo.setInternalCandidates(this, internalMask);
GenTreePtr objChild = putArgChild->gtOp.gtOp1;
if (objChild->OperGet() == GT_LCL_VAR_ADDR)
@@ -724,11 +766,13 @@ void Lowering::TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* argNode, TreeNode
// We will generate all of the code for the GT_PUTARG_SPLIT, the GT_OBJ and the GT_LCL_VAR_ADDR
// as one contained operation
//
- MakeSrcContained(putArgChild, objChild);
- putArgChild->gtLsraInfo.srcCount--;
+ assert(objChild->isContained());
}
- argNode->gtLsraInfo.srcCount = putArgChild->gtLsraInfo.srcCount;
- MakeSrcContained(argNode, putArgChild);
+ else
+ {
+ argNode->gtLsraInfo.srcCount = GetIndirSourceCount(putArgChild->AsIndir());
+ }
+ assert(putArgChild->isContained());
}
}
#endif // _TARGET_ARM_
@@ -742,53 +786,25 @@ void Lowering::TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* argNode, TreeNode
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
+void LinearScan::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
{
- GenTree* dstAddr = blkNode->Addr();
- unsigned size = blkNode->gtBlkSize;
- GenTree* source = blkNode->Data();
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
+ GenTree* dstAddr = blkNode->Addr();
+ unsigned size = blkNode->gtBlkSize;
+ GenTree* source = blkNode->Data();
// Sources are dest address and initVal or source.
// We may require an additional source or temp register for the size.
- blkNode->gtLsraInfo.srcCount = 2;
- blkNode->gtLsraInfo.dstCount = 0;
- GenTreePtr srcAddrOrFill = nullptr;
- bool isInitBlk = blkNode->OperIsInitBlkOp();
-
- if (!isInitBlk)
- {
- // CopyObj or CopyBlk
- if (source->gtOper == GT_IND)
- {
- srcAddrOrFill = blkNode->Data()->gtGetOp1();
- // We're effectively setting source as contained, but can't call MakeSrcContained, because the
- // "inheritance" of the srcCount is to a child not a parent - it would "just work" but could be misleading.
- // If srcAddr is already non-contained, we don't need to change it.
- if (srcAddrOrFill->gtLsraInfo.getDstCount() == 0)
- {
- srcAddrOrFill->gtLsraInfo.setDstCount(1);
- srcAddrOrFill->gtLsraInfo.setSrcCount(source->gtLsraInfo.srcCount);
- }
- m_lsra->clearOperandCounts(source);
- source->SetContained();
- source->AsIndir()->Addr()->ClearContained();
- }
- else if (!source->IsMultiRegCall() && !source->OperIsSIMD())
- {
- assert(source->IsLocal());
- MakeSrcContained(blkNode, source);
- blkNode->gtLsraInfo.srcCount--;
- }
- }
+ blkNode->gtLsraInfo.srcCount = GetOperandSourceCount(dstAddr);
+ assert(blkNode->gtLsraInfo.dstCount == 0);
+ GenTreePtr srcAddrOrFill = nullptr;
+ bool isInitBlk = blkNode->OperIsInitBlkOp();
if (isInitBlk)
{
GenTreePtr initVal = source;
if (initVal->OperIsInitVal())
{
- initVal->SetContained();
+ assert(initVal->isContained());
initVal = initVal->gtGetOp1();
}
srcAddrOrFill = initVal;
@@ -800,27 +816,23 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
// code sequences to improve CQ.
// For reference see the code in lsraxarch.cpp.
NYI_ARM("initblk loop unrolling is currently not implemented.");
-
-#ifdef _TARGET_ARM64_
- // No additional temporaries required
- ssize_t fill = initVal->gtIntCon.gtIconVal & 0xFF;
- if (fill == 0)
+ if (!initVal->isContained())
{
- MakeSrcContained(blkNode, source);
- blkNode->gtLsraInfo.srcCount--;
+ blkNode->gtLsraInfo.srcCount++;
}
-#endif // _TARGET_ARM64_
}
else
{
assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
// The helper follows the regular ABI.
- dstAddr->gtLsraInfo.setSrcCandidates(l, RBM_ARG_0);
- initVal->gtLsraInfo.setSrcCandidates(l, RBM_ARG_1);
+ dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
+ assert(!initVal->isContained());
+ blkNode->gtLsraInfo.srcCount++;
+ initVal->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
if (size != 0)
{
// Reserve a temp register for the block size argument.
- blkNode->gtLsraInfo.setInternalCandidates(l, RBM_ARG_2);
+ blkNode->gtLsraInfo.setInternalCandidates(this, RBM_ARG_2);
blkNode->gtLsraInfo.internalIntCount = 1;
}
else
@@ -829,7 +841,7 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
noway_assert(blkNode->gtOper == GT_STORE_DYN_BLK);
blkNode->gtLsraInfo.setSrcCount(3);
GenTree* sizeNode = blkNode->AsDynBlk()->gtDynamicSize;
- sizeNode->gtLsraInfo.setSrcCandidates(l, RBM_ARG_2);
+ sizeNode->gtLsraInfo.setSrcCandidates(this, RBM_ARG_2);
}
}
}
@@ -837,6 +849,10 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
{
// CopyObj or CopyBlk
// Sources are src and dest and size if not constant.
+ if (source->gtOper == GT_IND)
+ {
+ srcAddrOrFill = blkNode->Data()->gtGetOp1();
+ }
if (blkNode->OperGet() == GT_STORE_OBJ)
{
// CopyObj
@@ -853,17 +869,17 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
// We can't use the special Write Barrier registers, so exclude them from the mask
regMaskTP internalIntCandidates = RBM_ALLINT & ~(RBM_WRITE_BARRIER_DST_BYREF | RBM_WRITE_BARRIER_SRC_BYREF);
- blkNode->gtLsraInfo.setInternalCandidates(l, internalIntCandidates);
+ blkNode->gtLsraInfo.setInternalCandidates(this, internalIntCandidates);
// If we have a dest address we want it in RBM_WRITE_BARRIER_DST_BYREF.
- dstAddr->gtLsraInfo.setSrcCandidates(l, RBM_WRITE_BARRIER_DST_BYREF);
+ dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_DST_BYREF);
// If we have a source address we want it in REG_WRITE_BARRIER_SRC_BYREF.
// Otherwise, if it is a local, codegen will put its address in REG_WRITE_BARRIER_SRC_BYREF,
// which is killed by a StoreObj (and thus needn't be reserved).
if (srcAddrOrFill != nullptr)
{
- srcAddrOrFill->gtLsraInfo.setSrcCandidates(l, RBM_WRITE_BARRIER_SRC_BYREF);
+ srcAddrOrFill->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_SRC_BYREF);
}
}
else
@@ -897,11 +913,11 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
else
{
assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
- dstAddr->gtLsraInfo.setSrcCandidates(l, RBM_ARG_0);
+ dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
// The srcAddr goes in arg1.
if (srcAddrOrFill != nullptr)
{
- srcAddrOrFill->gtLsraInfo.setSrcCandidates(l, RBM_ARG_1);
+ srcAddrOrFill->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
}
if (size != 0)
{
@@ -912,18 +928,19 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
else
{
// The block size argument is a third argument to GT_STORE_DYN_BLK
- noway_assert(blkNode->gtOper == GT_STORE_DYN_BLK);
- blkNode->gtLsraInfo.setSrcCount(3);
+ assert(blkNode->gtOper == GT_STORE_DYN_BLK);
+ blkNode->gtLsraInfo.srcCount++;
GenTree* blockSize = blkNode->AsDynBlk()->gtDynamicSize;
- blockSize->gtLsraInfo.setSrcCandidates(l, RBM_ARG_2);
+ blockSize->gtLsraInfo.setSrcCandidates(this, RBM_ARG_2);
}
}
if (internalIntCount != 0)
{
blkNode->gtLsraInfo.internalIntCount = internalIntCount;
- blkNode->gtLsraInfo.setInternalCandidates(l, internalIntCandidates);
+ blkNode->gtLsraInfo.setInternalCandidates(this, internalIntCandidates);
}
}
+ blkNode->gtLsraInfo.srcCount += GetOperandSourceCount(source);
}
}
@@ -936,7 +953,7 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
// Return Value:
// The number of source registers used by the *parent* of this node.
//
-int Lowering::GetOperandSourceCount(GenTree* node)
+int LinearScan::GetOperandSourceCount(GenTree* node)
{
if (!node->isContained())
{
@@ -950,6 +967,12 @@ int Lowering::GetOperandSourceCount(GenTree* node)
}
#endif // !defined(_TARGET_64BIT_)
+ if (node->OperIsIndir())
+ {
+ const unsigned srcCount = GetIndirSourceCount(node->AsIndir());
+ return srcCount;
+ }
+
return 0;
}
diff --git a/src/jit/lsraxarch.cpp b/src/jit/lsraxarch.cpp
index 6613fc3ba3..a42d8ec3fe 100644
--- a/src/jit/lsraxarch.cpp
+++ b/src/jit/lsraxarch.cpp
@@ -39,13 +39,11 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// - Setting the appropriate candidates for a store of a multi-reg call return value.
// - Requesting an internal register for SIMD12 stores.
//
-void Lowering::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
+void LinearScan::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
{
- ContainCheckStoreLoc(storeLoc);
-
TreeNodeInfo* info = &(storeLoc->gtLsraInfo);
- info->dstCount = 0;
- GenTree* op1 = storeLoc->gtGetOp1();
+ assert(info->dstCount == 0);
+ GenTree* op1 = storeLoc->gtGetOp1();
#ifdef _TARGET_X86_
if (op1->OperGet() == GT_LONG)
@@ -72,8 +70,8 @@ void Lowering::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
info->srcCount = retTypeDesc->GetReturnRegCount();
// Call node srcCandidates = Bitwise-OR(allregs(GetReturnRegType(i))) for all i=0..RetRegCount-1
- regMaskTP srcCandidates = m_lsra->allMultiRegCallNodeRegs(call);
- op1->gtLsraInfo.setSrcCandidates(m_lsra, srcCandidates);
+ regMaskTP srcCandidates = allMultiRegCallNodeRegs(call);
+ op1->gtLsraInfo.setSrcCandidates(this, srcCandidates);
return;
}
else
@@ -88,7 +86,7 @@ void Lowering::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
{
// Need an additional register to extract upper 4 bytes of Vector3.
info->internalFloatCount = 1;
- info->setInternalCandidates(m_lsra, m_lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
return;
}
@@ -110,13 +108,21 @@ void Lowering::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
// requirements needed by LSRA to build the Interval Table (source,
// destination and internal [temp] register counts).
//
-void Lowering::TreeNodeInfoInit(GenTree* tree)
+void LinearScan::TreeNodeInfoInit(GenTree* tree)
{
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
-
TreeNodeInfo* info = &(tree->gtLsraInfo);
+ if (tree->isContained())
+ {
+ info->dstCount = 0;
+ assert(info->srcCount == 0);
+ TreeNodeInfoInitCheckByteable(tree);
+ return;
+ }
+
+ // Set the default dstCount. This may be modified below.
+ info->dstCount = tree->IsValue() ? 1 : 0;
+
// floating type generates AVX instruction (vmovss etc.), set the flag
SetContainsAVXFlags(varTypeIsFloating(tree->TypeGet()));
switch (tree->OperGet())
@@ -128,10 +134,28 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
TreeNodeInfoInitSimple(tree);
break;
- case GT_LCL_FLD:
case GT_LCL_VAR:
+ // Because we do containment analysis before we redo dataflow and identify register
+ // candidates, the containment analysis only !lvDoNotEnregister to estimate register
+ // candidates.
+ // If there is a lclVar that is estimated to be register candidate but
+ // is not, if they were marked regOptional they should now be marked contained instead.
+ // TODO-XArch-CQ: When this is being called while RefPositions are being created,
+ // use lvLRACandidate here instead.
+ if (info->regOptional)
+ {
+ if (!compiler->lvaTable[tree->AsLclVarCommon()->gtLclNum].lvTracked ||
+ compiler->lvaTable[tree->AsLclVarCommon()->gtLclNum].lvDoNotEnregister)
+ {
+ info->regOptional = false;
+ tree->SetContained();
+ info->dstCount = 0;
+ }
+ }
+ __fallthrough;
+
+ case GT_LCL_FLD:
info->srcCount = 0;
- info->dstCount = 1;
#ifdef FEATURE_SIMD
// Need an additional register to read upper 4 bytes of Vector3.
@@ -141,7 +165,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// because both targetReg and internal reg will be in use at the same time.
info->internalFloatCount = 1;
info->isInternalRegDelayFree = true;
- info->setInternalCandidates(m_lsra, m_lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
#endif
break;
@@ -158,12 +182,12 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_START_NONGC:
case GT_PROF_HOOK:
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_CNS_DBL:
info->srcCount = 0;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
#if !defined(_TARGET_64BIT_)
@@ -173,14 +197,14 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
{
// An unused GT_LONG node needs to consume its sources.
info->srcCount = 2;
+ info->dstCount = 0;
}
else
{
- // Passthrough
+ // Passthrough. Should have been marked contained.
info->srcCount = 0;
+ assert(info->dstCount == 0);
}
-
- info->dstCount = 0;
break;
#endif // !defined(_TARGET_64BIT_)
@@ -190,7 +214,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_QMARK:
case GT_COLON:
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
unreached();
break;
@@ -199,20 +223,19 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
break;
case GT_RETFILT:
+ assert(info->dstCount == 0);
if (tree->TypeGet() == TYP_VOID)
{
info->srcCount = 0;
- info->dstCount = 0;
}
else
{
assert(tree->TypeGet() == TYP_INT);
info->srcCount = 1;
- info->dstCount = 0;
- info->setSrcCandidates(l, RBM_INTRET);
- tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, RBM_INTRET);
+ info->setSrcCandidates(this, RBM_INTRET);
+ tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, RBM_INTRET);
}
break;
@@ -223,24 +246,23 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
info->srcCount = 0;
if (tree->TypeGet() != TYP_VOID && tree->gtOp.gtOp1 == nullptr)
{
- info->dstCount = 1;
+ assert(info->dstCount == 1);
}
else
{
- info->dstCount = 0;
+ assert(info->dstCount == 0);
}
break;
case GT_JTRUE:
{
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
GenTree* cmp = tree->gtGetOp1();
- l->clearDstCount(cmp);
+ assert(cmp->gtLsraInfo.dstCount == 0);
#ifdef FEATURE_SIMD
- ContainCheckJTrue(tree->AsOp());
GenTree* cmpOp1 = cmp->gtGetOp1();
GenTree* cmpOp2 = cmp->gtGetOp2();
if (cmpOp1->IsSIMDEqualityOrInequality() && cmpOp2->isContained())
@@ -249,46 +271,8 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// We always generate code for a SIMD equality comparison, but the compare itself produces no value.
// Neither the SIMD node nor the immediate need to be evaluated into a register.
- l->clearOperandCounts(cmp);
- l->clearDstCount(cmpOp1);
- l->clearOperandCounts(cmpOp2);
-
- // Codegen of SIMD (in)Equality uses target integer reg only for setting flags.
- // A target reg is not needed on AVX when comparing against Vector Zero.
- // In all other cases we need to reserve an int type internal register, since we
- // have cleared dstCount.
- if (!compiler->canUseAVX() || !cmpOp1->gtGetOp2()->IsIntegralConstVector(0))
- {
- ++(cmpOp1->gtLsraInfo.internalIntCount);
- regMaskTP internalCandidates = cmpOp1->gtLsraInfo.getInternalCandidates(l);
- internalCandidates |= l->allRegs(TYP_INT);
- cmpOp1->gtLsraInfo.setInternalCandidates(l, internalCandidates);
- }
-
- // We have to reverse compare oper in the following cases:
- // 1) SIMD Equality: Sets Zero flag on equal otherwise clears it.
- // Therefore, if compare oper is == or != against false(0), we will
- // be checking opposite of what is required.
- //
- // 2) SIMD inEquality: Clears Zero flag on true otherwise sets it.
- // Therefore, if compare oper is == or != against true(1), we will
- // be checking opposite of what is required.
- GenTreeSIMD* simdNode = cmpOp1->AsSIMD();
- if (simdNode->gtSIMDIntrinsicID == SIMDIntrinsicOpEquality)
- {
- if (cmpOp2->IsIntegralConst(0))
- {
- cmp->SetOper(GenTree::ReverseRelop(cmpOper));
- }
- }
- else
- {
- assert(simdNode->gtSIMDIntrinsicID == SIMDIntrinsicOpInEquality);
- if (cmpOp2->IsIntegralConst(1))
- {
- cmp->SetOper(GenTree::ReverseRelop(cmpOper));
- }
- }
+ assert(cmpOp1->gtLsraInfo.dstCount == 0);
+ assert(cmpOp2->gtLsraInfo.dstCount == 0);
}
#endif // FEATURE_SIMD
}
@@ -296,39 +280,38 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_JCC:
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_SETCC:
info->srcCount = 0;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
#ifdef _TARGET_X86_
- info->setDstCandidates(m_lsra, RBM_BYTE_REGS);
+ info->setDstCandidates(this, RBM_BYTE_REGS);
#endif // _TARGET_X86_
break;
case GT_JMP:
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_SWITCH:
// This should never occur since switch nodes must not be visible at this
// point in the JIT.
info->srcCount = 0;
- info->dstCount = 0; // To avoid getting uninit errors.
noway_assert(!"Switch must be lowered at this point");
break;
case GT_JMPTABLE:
info->srcCount = 0;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
break;
case GT_SWITCH_TABLE:
info->srcCount = 2;
info->internalIntCount = 1;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_ASG:
@@ -336,7 +319,6 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_ASG_SUB:
noway_assert(!"We should never hit any assignment operator in lowering");
info->srcCount = 0;
- info->dstCount = 0;
break;
#if !defined(_TARGET_64BIT_)
@@ -351,10 +333,8 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// Rather they only support "op xmm, mem/xmm" form.
if (varTypeIsFloating(tree->TypeGet()))
{
- ContainCheckFloatBinary(tree->AsOp());
- info->srcCount += GetOperandSourceCount(tree->gtOp.gtOp1);
+ info->srcCount = GetOperandSourceCount(tree->gtOp.gtOp1);
info->srcCount += GetOperandSourceCount(tree->gtOp.gtOp2);
- info->dstCount = 1;
break;
}
@@ -363,21 +343,16 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_AND:
case GT_OR:
case GT_XOR:
- ContainCheckBinary(tree->AsOp());
- info->srcCount += GetOperandSourceCount(tree->gtOp.gtOp1);
+ info->srcCount = GetOperandSourceCount(tree->gtOp.gtOp1);
info->srcCount += GetOperandSourceCount(tree->gtOp.gtOp2);
- info->dstCount = 1;
- // Codegen of this tree node sets ZF and SF flags.
- tree->gtFlags |= GTF_ZSF_SET;
break;
case GT_RETURNTRAP:
// This just turns into a compare of its child with an int + a conditional call
- ContainCheckReturnTrap(tree->AsOp());
- info->srcCount = tree->gtOp.gtOp1->isContained() ? 0 : 1;
- info->dstCount = 0;
+ info->srcCount = tree->gtOp.gtOp1->isContained() ? 0 : 1;
+ assert(info->dstCount == 0);
info->internalIntCount = 1;
- info->setInternalCandidates(l, l->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allRegs(TYP_INT));
break;
case GT_MOD:
@@ -406,7 +381,6 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
#endif // FEATURE_SIMD
case GT_CAST:
- ContainCheckCast(tree->AsCast());
TreeNodeInfoInitCast(tree);
break;
@@ -417,8 +391,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
break;
case GT_NEG:
- info->srcCount = 1;
- info->dstCount = 1;
+ info->srcCount = GetOperandSourceCount(tree->gtOp.gtOp1);
// TODO-XArch-CQ:
// SSE instruction set doesn't have an instruction to negate a number.
@@ -439,18 +412,12 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
if (varTypeIsFloating(tree))
{
info->internalFloatCount = 1;
- info->setInternalCandidates(l, l->internalFloatRegCandidates());
- }
- else
- {
- // Codegen of this tree node sets ZF and SF flags.
- tree->gtFlags |= GTF_ZSF_SET;
+ info->setInternalCandidates(this, internalFloatRegCandidates());
}
break;
case GT_NOT:
- info->srcCount = 1;
- info->dstCount = 1;
+ info->srcCount = GetOperandSourceCount(tree->gtOp.gtOp1);
break;
case GT_LSH:
@@ -478,34 +445,31 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
break;
case GT_CKFINITE:
- info->srcCount = 1;
- info->dstCount = 1;
+ info->srcCount = 1;
+ assert(info->dstCount == 1);
info->internalIntCount = 1;
break;
case GT_CMPXCHG:
info->srcCount = 3;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
// comparand is preferenced to RAX.
// Remaining two operands can be in any reg other than RAX.
- tree->gtCmpXchg.gtOpComparand->gtLsraInfo.setSrcCandidates(l, RBM_RAX);
- tree->gtCmpXchg.gtOpLocation->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~RBM_RAX);
- tree->gtCmpXchg.gtOpValue->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~RBM_RAX);
- tree->gtLsraInfo.setDstCandidates(l, RBM_RAX);
+ tree->gtCmpXchg.gtOpComparand->gtLsraInfo.setSrcCandidates(this, RBM_RAX);
+ tree->gtCmpXchg.gtOpLocation->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~RBM_RAX);
+ tree->gtCmpXchg.gtOpValue->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~RBM_RAX);
+ tree->gtLsraInfo.setDstCandidates(this, RBM_RAX);
break;
case GT_LOCKADD:
- info->dstCount = (tree->TypeGet() == TYP_VOID) ? 0 : 1;
+ op2 = tree->gtOp.gtOp2;
+ info->srcCount = op2->isContained() ? 1 : 2;
+ assert(info->dstCount == (tree->TypeGet() == TYP_VOID) ? 0 : 1);
+ break;
- if (CheckImmedAndMakeContained(tree, tree->gtOp.gtOp2))
- {
- info->srcCount = 1;
- }
- else
- {
- info->srcCount = 2;
- }
+ case GT_PUTARG_REG:
+ TreeNodeInfoInitPutArgReg(tree->AsUnOp());
break;
case GT_CALL:
@@ -516,10 +480,10 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
{
// For a GT_ADDR, the child node should not be evaluated into a register
GenTreePtr child = tree->gtOp.gtOp1;
- assert(!l->isCandidateLocalRef(child));
- MakeSrcContained(tree, child);
+ assert(!isCandidateLocalRef(child));
+ assert(child->isContained());
+ assert(info->dstCount == 1);
info->srcCount = 0;
- info->dstCount = 1;
}
break;
@@ -531,12 +495,10 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
// These should all be eliminated prior to Lowering.
assert(!"Non-store block node in Lowering");
info->srcCount = 0;
- info->dstCount = 0;
break;
#ifdef FEATURE_PUT_STRUCT_ARG_STK
case GT_PUTARG_STK:
- LowerPutArgStk(tree->AsPutArgStk());
TreeNodeInfoInitPutArgStk(tree->AsPutArgStk());
break;
#endif // FEATURE_PUT_STRUCT_ARG_STK
@@ -544,14 +506,12 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_STORE_BLK:
case GT_STORE_OBJ:
case GT_STORE_DYN_BLK:
- LowerBlockStore(tree->AsBlk());
TreeNodeInfoInitBlockStore(tree->AsBlk());
break;
case GT_INIT_VAL:
// Always a passthrough of its child's value.
- info->srcCount = 0;
- info->dstCount = 0;
+ assert(!"INIT_VAL should always be contained");
break;
case GT_LCLHEAP:
@@ -562,23 +522,21 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
#ifdef FEATURE_SIMD
case GT_SIMD_CHK:
#endif // FEATURE_SIMD
- ContainCheckBoundsChk(tree->AsBoundsChk());
// Consumes arrLen & index - has no result
info->srcCount = GetOperandSourceCount(tree->AsBoundsChk()->gtIndex);
info->srcCount += GetOperandSourceCount(tree->AsBoundsChk()->gtArrLen);
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
case GT_ARR_ELEM:
// These must have been lowered to GT_ARR_INDEX
- noway_assert(!"We should never see a GT_ARR_ELEM in lowering");
+ noway_assert(!"We should never see a GT_ARR_ELEM after Lowering.");
info->srcCount = 0;
- info->dstCount = 0;
break;
case GT_ARR_INDEX:
info->srcCount = 2;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
// For GT_ARR_INDEX, the lifetime of the arrObj must be extended because it is actually used multiple
// times while the result is being computed.
tree->AsArrIndex()->ArrObj()->gtLsraInfo.isDelayFree = true;
@@ -588,27 +546,26 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
case GT_ARR_OFFSET:
// This consumes the offset, if any, the arrObj and the effective index,
// and produces the flattened offset for this dimension.
- info->srcCount = 2;
- info->dstCount = 1;
-
- if (tree->gtArrOffs.gtOffset->IsIntegralConst(0))
+ assert(info->dstCount == 1);
+ if (tree->gtArrOffs.gtOffset->isContained())
{
- MakeSrcContained(tree, tree->gtArrOffs.gtOffset);
+ info->srcCount = 2;
}
else
{
info->srcCount++;
// Here we simply need an internal register, which must be different
// from any of the operand's registers, but may be the same as targetReg.
+ info->srcCount = 3;
info->internalIntCount = 1;
}
break;
case GT_LEA:
- // The LEA usually passes its operands through to the GT_IND, in which case we'll
- // clear the info->srcCount and info->dstCount later, but we may be instantiating an address,
- // so we set them here.
+ // The LEA usually passes its operands through to the GT_IND, in which case it will
+ // be contained, but we may be instantiating an address, in which case we set them here.
info->srcCount = 0;
+ assert(info->dstCount == 1);
if (tree->AsAddrMode()->HasBase())
{
info->srcCount++;
@@ -617,70 +574,37 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
{
info->srcCount++;
}
- info->dstCount = 1;
break;
case GT_STOREIND:
- {
- info->srcCount = 2;
- info->dstCount = 0;
- GenTree* src = tree->gtOp.gtOp2;
-
if (compiler->codeGen->gcInfo.gcIsWriteBarrierAsgNode(tree))
{
TreeNodeInfoInitGCWriteBarrier(tree);
break;
}
-
- // If the source is a containable immediate, make it contained, unless it is
- // an int-size or larger store of zero to memory, because we can generate smaller code
- // by zeroing a register and then storing it.
- if (IsContainableImmed(tree, src) &&
- (!src->IsIntegralConst(0) || varTypeIsSmall(tree) || tree->gtGetOp1()->OperGet() == GT_CLS_VAR_ADDR))
- {
- MakeSrcContained(tree, src);
- }
- else if (!varTypeIsFloating(tree))
- {
- // Perform recognition of trees with the following structure:
- // StoreInd(addr, BinOp(expr, GT_IND(addr)))
- // to be able to fold this into an instruction of the form
- // BINOP [addr], register
- // where register is the actual place where 'expr' is computed.
- //
- // SSE2 doesn't support RMW form of instructions.
- if (TreeNodeInfoInitIfRMWMemOp(tree))
- {
- break;
- }
- }
-
TreeNodeInfoInitIndir(tree->AsIndir());
- }
- break;
+ break;
case GT_NULLCHECK:
- info->dstCount = 0;
- info->srcCount = 1;
- info->isLocalDefUse = true;
+ assert(info->dstCount == 0);
+ info->srcCount = 1;
break;
case GT_IND:
- info->dstCount = 1;
- info->srcCount = 1;
TreeNodeInfoInitIndir(tree->AsIndir());
+ assert(info->dstCount == 1);
break;
case GT_CATCH_ARG:
info->srcCount = 0;
- info->dstCount = 1;
- info->setDstCandidates(l, RBM_EXCEPTION_OBJECT);
+ assert(info->dstCount == 1);
+ info->setDstCandidates(this, RBM_EXCEPTION_OBJECT);
break;
#if !FEATURE_EH_FUNCLETS
case GT_END_LFIN:
info->srcCount = 0;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
break;
#endif
@@ -689,6 +613,31 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
JITDUMP("Unexpected node %s in Lower.\n", GenTree::OpName(tree->OperGet()));
unreached();
break;
+
+ case GT_INDEX_ADDR:
+ info->srcCount = 2;
+ info->dstCount = 1;
+
+ if (tree->AsIndexAddr()->Index()->TypeGet() == TYP_I_IMPL)
+ {
+ info->internalIntCount = 1;
+ }
+ else
+ {
+ switch (tree->AsIndexAddr()->gtElemSize)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+
+ default:
+ info->internalIntCount = 1;
+ break;
+ }
+ }
+ break;
} // end switch (tree->OperGet())
// If op2 of a binary-op gets marked as contained, then binary-op srcCount will be 1.
@@ -715,8 +664,6 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
op1->gtLsraInfo.isTgtPref = true;
// Is this a non-commutative operator, or is op2 a contained memory op?
- // (Note that we can't call IsContained() at this point because it uses exactly the
- // same information we're currently computing.)
// In either case, we need to make op2 remain live until the op is complete, by marking
// the source(s) associated with op2 as "delayFree".
// Note that if op2 of a binary RMW operator is a memory op, even if the operator
@@ -760,8 +707,8 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
delayUseSrc = op1;
}
- else if ((op2 != nullptr) && (!tree->OperIsCommutative() ||
- (IsContainableMemoryOp(op2, true) && (op2->gtLsraInfo.srcCount == 0))))
+ else if ((op2 != nullptr) &&
+ (!tree->OperIsCommutative() || (isContainableMemoryOp(op2) && (op2->gtLsraInfo.srcCount == 0))))
{
delayUseSrc = op2;
}
@@ -775,11 +722,15 @@ void Lowering::TreeNodeInfoInit(GenTree* tree)
TreeNodeInfoInitCheckByteable(tree);
+ if (tree->IsUnusedValue() && (info->dstCount != 0))
+ {
+ info->isLocalDefUse = true;
+ }
// We need to be sure that we've set info->srcCount and info->dstCount appropriately
assert((info->dstCount < 2) || (tree->IsMultiRegCall() && info->dstCount == MAX_RET_REG_COUNT));
}
-void Lowering::SetDelayFree(GenTree* delayUseSrc)
+void LinearScan::SetDelayFree(GenTree* delayUseSrc)
{
// If delayUseSrc is an indirection and it doesn't produce a result, then we need to set "delayFree'
// on the base & index, if any.
@@ -813,10 +764,9 @@ void Lowering::SetDelayFree(GenTree* delayUseSrc)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCheckByteable(GenTree* tree)
+void LinearScan::TreeNodeInfoInitCheckByteable(GenTree* tree)
{
#ifdef _TARGET_X86_
- LinearScan* l = m_lsra;
TreeNodeInfo* info = &(tree->gtLsraInfo);
// Exclude RBM_NON_BYTE_REGS from dst candidates of tree node and src candidates of operands
@@ -831,33 +781,33 @@ void Lowering::TreeNodeInfoInitCheckByteable(GenTree* tree)
regMaskTP regMask;
if (info->dstCount > 0)
{
- regMask = info->getDstCandidates(l);
+ regMask = info->getDstCandidates(this);
assert(regMask != RBM_NONE);
- info->setDstCandidates(l, regMask & ~RBM_NON_BYTE_REGS);
+ info->setDstCandidates(this, regMask & ~RBM_NON_BYTE_REGS);
}
- if (tree->OperIsSimple() && (info->srcCount > 0))
+ if (tree->OperIsSimple())
{
- // No need to set src candidates on a contained child operand.
GenTree* op = tree->gtOp.gtOp1;
- assert(op != nullptr);
- bool containedNode = (op->gtLsraInfo.srcCount == 0) && (op->gtLsraInfo.dstCount == 0);
- if (!containedNode)
+ if (op != nullptr)
{
- regMask = op->gtLsraInfo.getSrcCandidates(l);
- assert(regMask != RBM_NONE);
- op->gtLsraInfo.setSrcCandidates(l, regMask & ~RBM_NON_BYTE_REGS);
+ // No need to set src candidates on a contained child operand.
+ if (!op->isContained())
+ {
+ regMask = op->gtLsraInfo.getSrcCandidates(this);
+ assert(regMask != RBM_NONE);
+ op->gtLsraInfo.setSrcCandidates(this, regMask & ~RBM_NON_BYTE_REGS);
+ }
}
if (tree->OperIsBinary() && (tree->gtOp.gtOp2 != nullptr))
{
- op = tree->gtOp.gtOp2;
- containedNode = (op->gtLsraInfo.srcCount == 0) && (op->gtLsraInfo.dstCount == 0);
- if (!containedNode)
+ op = tree->gtOp.gtOp2;
+ if (!op->isContained())
{
- regMask = op->gtLsraInfo.getSrcCandidates(l);
+ regMask = op->gtLsraInfo.getSrcCandidates(this);
assert(regMask != RBM_NONE);
- op->gtLsraInfo.setSrcCandidates(l, regMask & ~RBM_NON_BYTE_REGS);
+ op->gtLsraInfo.setSrcCandidates(this, regMask & ~RBM_NON_BYTE_REGS);
}
}
}
@@ -865,8 +815,50 @@ void Lowering::TreeNodeInfoInitCheckByteable(GenTree* tree)
#endif //_TARGET_X86_
}
+//------------------------------------------------------------------------------
+// isRMWRegOper: Can this binary tree node be used in a Read-Modify-Write format
+//
+// Arguments:
+// tree - a binary tree node
+//
+// Return Value:
+// Returns true if we can use the read-modify-write instruction form
+//
+// Notes:
+// This is used to determine whether to preference the source to the destination register.
+//
+bool LinearScan::isRMWRegOper(GenTreePtr tree)
+{
+ // TODO-XArch-CQ: Make this more accurate.
+ // For now, We assume that most binary operators are of the RMW form.
+ assert(tree->OperIsBinary());
+
+ if (tree->OperIsCompare() || tree->OperIs(GT_CMP))
+ {
+ return false;
+ }
+
+ switch (tree->OperGet())
+ {
+ // These Opers either support a three op form (i.e. GT_LEA), or do not read/write their first operand
+ case GT_LEA:
+ case GT_STOREIND:
+ case GT_ARR_INDEX:
+ case GT_STORE_BLK:
+ case GT_STORE_OBJ:
+ return false;
+
+ // x86/x64 does support a three op multiply when op2|op1 is a contained immediate
+ case GT_MUL:
+ return (!tree->gtOp.gtOp2->isContainedIntOrIImmed() && !tree->gtOp.gtOp1->isContainedIntOrIImmed());
+
+ default:
+ return true;
+ }
+}
+
//------------------------------------------------------------------------
-// TreeNodeInfoInitSimple: Sets the srcCount and dstCount for all the trees
+// TreeNodeInfoInitSimple: Sets the srcCount for all the trees
// without special handling based on the tree node type.
//
// Arguments:
@@ -875,11 +867,15 @@ void Lowering::TreeNodeInfoInitCheckByteable(GenTree* tree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitSimple(GenTree* tree)
+void LinearScan::TreeNodeInfoInitSimple(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
- unsigned kind = tree->OperKind();
- info->dstCount = tree->IsValue() ? 1 : 0;
+ if (tree->isContained())
+ {
+ info->srcCount = 0;
+ return;
+ }
+ unsigned kind = tree->OperKind();
if (kind & (GTK_CONST | GTK_LEAF))
{
info->srcCount = 0;
@@ -888,12 +884,9 @@ void Lowering::TreeNodeInfoInitSimple(GenTree* tree)
{
if (tree->gtGetOp2IfPresent() != nullptr)
{
- info->srcCount = 2;
- }
- else
- {
- info->srcCount = 1;
+ info->srcCount += GetOperandSourceCount(tree->gtOp.gtOp2);
}
+ info->srcCount += GetOperandSourceCount(tree->gtOp.gtOp1);
}
else
{
@@ -910,14 +903,10 @@ void Lowering::TreeNodeInfoInitSimple(GenTree* tree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
+void LinearScan::TreeNodeInfoInitReturn(GenTree* tree)
{
- ContainCheckRet(tree->AsOp());
-
- TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
- GenTree* op1 = tree->gtGetOp1();
+ TreeNodeInfo* info = &(tree->gtLsraInfo);
+ GenTree* op1 = tree->gtGetOp1();
#if !defined(_TARGET_64BIT_)
if (tree->TypeGet() == TYP_LONG)
@@ -926,9 +915,9 @@ void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
GenTree* loVal = op1->gtGetOp1();
GenTree* hiVal = op1->gtGetOp2();
info->srcCount = 2;
- loVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_LO);
- hiVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_HI);
- info->dstCount = 0;
+ loVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_LO);
+ hiVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_HI);
+ assert(info->dstCount == 0);
}
else
#endif // !defined(_TARGET_64BIT_)
@@ -936,7 +925,7 @@ void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
regMaskTP useCandidates = RBM_NONE;
info->srcCount = ((tree->TypeGet() == TYP_VOID) || op1->isContained()) ? 0 : 1;
- info->dstCount = 0;
+ assert(info->dstCount == 0);
#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
if (varTypeIsStruct(tree))
@@ -978,7 +967,7 @@ void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
if (useCandidates != RBM_NONE)
{
- op1->gtLsraInfo.setSrcCandidates(l, useCandidates);
+ op1->gtLsraInfo.setSrcCandidates(this, useCandidates);
}
}
}
@@ -992,14 +981,9 @@ void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitShiftRotate(GenTree* tree)
+void LinearScan::TreeNodeInfoInitShiftRotate(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
- ContainCheckShiftRotate(tree->AsOp());
-
- info->srcCount = 2;
- info->dstCount = 1;
// For shift operations, we need that the number
// of bits moved gets stored in CL in case
@@ -1011,9 +995,9 @@ void Lowering::TreeNodeInfoInitShiftRotate(GenTree* tree)
// We will allow whatever can be encoded - hope you know what you are doing.
if (!shiftBy->isContained())
{
- source->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~RBM_RCX);
- shiftBy->gtLsraInfo.setSrcCandidates(l, RBM_RCX);
- info->setDstCandidates(l, l->allRegs(TYP_INT) & ~RBM_RCX);
+ source->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~RBM_RCX);
+ shiftBy->gtLsraInfo.setSrcCandidates(this, RBM_RCX);
+ info->setDstCandidates(this, allRegs(TYP_INT) & ~RBM_RCX);
if (!tree->isContained())
{
info->srcCount = 2;
@@ -1076,38 +1060,51 @@ void Lowering::TreeNodeInfoInitShiftRotate(GenTree* tree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitPutArgReg(
- GenTreeUnOp* node, regNumber argReg, TreeNodeInfo& info, bool isVarArgs, bool* callHasFloatRegArgs)
+void LinearScan::TreeNodeInfoInitPutArgReg(GenTreeUnOp* node)
{
assert(node != nullptr);
assert(node->OperIsPutArgReg());
+ node->gtLsraInfo.srcCount = 1;
+ regNumber argReg = node->gtRegNum;
assert(argReg != REG_NA);
- // Each register argument corresponds to one source.
- info.srcCount++;
-
// Set the register requirements for the node.
const regMaskTP argMask = genRegMask(argReg);
- node->gtLsraInfo.setDstCandidates(m_lsra, argMask);
- node->gtLsraInfo.setSrcCandidates(m_lsra, argMask);
+ node->gtLsraInfo.setDstCandidates(this, argMask);
+ node->gtLsraInfo.setSrcCandidates(this, argMask);
// To avoid redundant moves, have the argument operand computed in the
// register in which the argument is passed to the call.
- node->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(m_lsra, m_lsra->getUseCandidates(node));
+ node->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, getUseCandidates(node));
+}
+//------------------------------------------------------------------------
+// HandleFloatVarArgs: Handle additional register requirements for a varargs call
+//
+// Arguments:
+// call - The call node of interest
+// argNode - The current argument
+//
+// Return Value:
+// None.
+//
+// Notes:
+// In the case of a varargs call, the ABI dictates that if we have floating point args,
+// we must pass the enregistered arguments in both the integer and floating point registers.
+// Since the integer register is not associated with the arg node, we will reserve it as
+// an internal register on the call so that it is not used during the evaluation of the call node
+// (e.g. for the target).
+void LinearScan::HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs)
+{
#if FEATURE_VARARG
- *callHasFloatRegArgs |= varTypeIsFloating(node->TypeGet());
-
- // In the case of a varargs call, the ABI dictates that if we have floating point args,
- // we must pass the enregistered arguments in both the integer and floating point registers.
- // Since the integer register is not associated with this arg node, we will reserve it as
- // an internal register so that it is not used during the evaluation of the call node
- // (e.g. for the target).
- if (isVarArgs && varTypeIsFloating(node))
+ if (call->IsVarargs() && varTypeIsFloating(argNode))
{
- regNumber targetReg = comp->getCallArgIntRegister(argReg);
- info.setInternalIntCount(info.internalIntCount + 1);
- info.addInternalCandidates(m_lsra, genRegMask(targetReg));
+ *callHasFloatRegArgs = true;
+
+ regNumber argReg = argNode->gtRegNum;
+ regNumber targetReg = compiler->getCallArgIntRegister(argReg);
+ call->gtLsraInfo.setInternalIntCount(call->gtLsraInfo.internalIntCount + 1);
+ call->gtLsraInfo.addInternalCandidates(this, genRegMask(targetReg));
}
#endif // FEATURE_VARARG
}
@@ -1121,14 +1118,13 @@ void Lowering::TreeNodeInfoInitPutArgReg(
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
+void LinearScan::TreeNodeInfoInitCall(GenTreeCall* call)
{
TreeNodeInfo* info = &(call->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
bool hasMultiRegRetVal = false;
ReturnTypeDesc* retTypeDesc = nullptr;
+ assert(!call->isContained());
info->srcCount = 0;
if (call->TypeGet() != TYP_VOID)
{
@@ -1141,43 +1137,36 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
}
else
{
- info->dstCount = 1;
+ assert(info->dstCount == 1);
}
}
else
{
- info->dstCount = 0;
+ assert(info->dstCount == 0);
}
GenTree* ctrlExpr = call->gtControlExpr;
if (call->gtCallType == CT_INDIRECT)
{
- // either gtControlExpr != null or gtCallAddr != null.
- // Both cannot be non-null at the same time.
- assert(ctrlExpr == nullptr);
- assert(call->gtCallAddr != nullptr);
ctrlExpr = call->gtCallAddr;
-
-#ifdef _TARGET_X86_
- // Fast tail calls aren't currently supported on x86, but if they ever are, the code
- // below that handles indirect VSD calls will need to be fixed.
- assert(!call->IsFastTailCall() || !call->IsVirtualStub());
-#endif // _TARGET_X86_
}
// set reg requirements on call target represented as control sequence.
if (ctrlExpr != nullptr)
{
- // we should never see a gtControlExpr whose type is void.
- assert(ctrlExpr->TypeGet() != TYP_VOID);
-
- // call can take a Rm op on x64
-
// In case of fast tail implemented as jmp, make sure that gtControlExpr is
// computed into a register.
- if (!call->IsFastTailCall())
+ if (call->IsFastTailCall())
{
+ {
+ // Fast tail call - make sure that call target is always computed in RAX
+ // so that epilog sequence can generate "jmp rax" to achieve fast tail call.
+ ctrlExpr->gtLsraInfo.setSrcCandidates(this, RBM_RAX);
+ }
+ }
#ifdef _TARGET_X86_
+ else
+ {
// On x86, we need to generate a very specific pattern for indirect VSD calls:
//
// 3-byte nop
@@ -1187,24 +1176,11 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
// sure that the call target address is computed into EAX in this case.
if (call->IsVirtualStub() && (call->gtCallType == CT_INDIRECT))
{
- assert(ctrlExpr->isIndir());
-
- ctrlExpr->gtGetOp1()->gtLsraInfo.setSrcCandidates(l, RBM_VIRTUAL_STUB_TARGET);
- MakeSrcContained(call, ctrlExpr);
- }
- else
-#endif // _TARGET_X86_
- if (ctrlExpr->isIndir())
- {
- MakeSrcContained(call, ctrlExpr);
+ assert(ctrlExpr->isIndir() && ctrlExpr->isContained());
+ ctrlExpr->gtGetOp1()->gtLsraInfo.setSrcCandidates(this, RBM_VIRTUAL_STUB_TARGET);
}
}
- else
- {
- // Fast tail call - make sure that call target is always computed in RAX
- // so that epilog sequence can generate "jmp rax" to achieve fast tail call.
- ctrlExpr->gtLsraInfo.setSrcCandidates(l, RBM_RAX);
- }
+#endif // _TARGET_X86_
info->srcCount += GetOperandSourceCount(ctrlExpr);
}
@@ -1214,7 +1190,7 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
// the individual specific registers will have no effect.
if (call->IsVarargs())
{
- info->setInternalCandidates(l, RBM_NONE);
+ info->setInternalCandidates(this, RBM_NONE);
}
RegisterType registerType = call->TypeGet();
@@ -1228,55 +1204,37 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
// The x86 CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
// TCB in REG_PINVOKE_TCB. AMD64/ARM64 use the standard calling convention. fgMorphCall() sets the
// correct argument registers.
- info->setDstCandidates(l, RBM_PINVOKE_TCB);
+ info->setDstCandidates(this, RBM_PINVOKE_TCB);
}
else
#endif // _TARGET_X86_
if (hasMultiRegRetVal)
{
assert(retTypeDesc != nullptr);
- info->setDstCandidates(l, retTypeDesc->GetABIReturnRegs());
+ info->setDstCandidates(this, retTypeDesc->GetABIReturnRegs());
}
else if (varTypeIsFloating(registerType))
{
#ifdef _TARGET_X86_
// The return value will be on the X87 stack, and we will need to move it.
- info->setDstCandidates(l, l->allRegs(registerType));
+ info->setDstCandidates(this, allRegs(registerType));
#else // !_TARGET_X86_
- info->setDstCandidates(l, RBM_FLOATRET);
+ info->setDstCandidates(this, RBM_FLOATRET);
#endif // !_TARGET_X86_
}
else if (registerType == TYP_LONG)
{
- info->setDstCandidates(l, RBM_LNGRET);
+ info->setDstCandidates(this, RBM_LNGRET);
}
else
{
- info->setDstCandidates(l, RBM_INTRET);
+ info->setDstCandidates(this, RBM_INTRET);
}
// number of args to a call =
// callRegArgs + (callargs - placeholders, setup, etc)
// there is an explicit thisPtr but it is redundant
- // If there is an explicit this pointer, we don't want that node to produce anything
- // as it is redundant
- if (call->gtCallObjp != nullptr)
- {
- GenTreePtr thisPtrNode = call->gtCallObjp;
-
- if (thisPtrNode->gtOper == GT_PUTARG_REG)
- {
- l->clearOperandCounts(thisPtrNode);
- thisPtrNode->SetContained();
- l->clearDstCount(thisPtrNode->gtOp.gtOp1);
- }
- else
- {
- l->clearDstCount(thisPtrNode);
- }
- }
-
bool callHasFloatRegArgs = false;
bool isVarArgs = call->IsVarargs();
@@ -1293,19 +1251,37 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
// - a field list
// - a put arg
//
- // Note that this property is statically checked by Lowering::CheckBlock.
+ // Note that this property is statically checked by LinearScan::CheckBlock.
GenTreePtr argNode = list->Current();
+ // Each register argument corresponds to one source.
+ if (argNode->OperIsPutArgReg())
+ {
+ info->srcCount++;
+ HandleFloatVarArgs(call, argNode, &callHasFloatRegArgs);
+ }
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ else if (argNode->OperGet() == GT_FIELD_LIST)
+ {
+ for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest())
+ {
+ assert(entry->Current()->OperIsPutArgReg());
+ info->srcCount++;
+ HandleFloatVarArgs(call, argNode, &callHasFloatRegArgs);
+ }
+ }
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+#ifdef DEBUG
+ // In DEBUG only, check validity with respect to the arg table entry.
+
fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode);
assert(curArgTabEntry);
if (curArgTabEntry->regNum == REG_STK)
{
// late arg that is not passed in a register
- DISPNODE(argNode);
assert(argNode->gtOper == GT_PUTARG_STK);
- argNode->gtLsraInfo.srcCount = 1;
- argNode->gtLsraInfo.dstCount = 0;
#ifdef FEATURE_PUT_STRUCT_ARG_STK
// If the node is TYP_STRUCT and it is put on stack with
@@ -1316,35 +1292,33 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
if (argNode->TypeGet() == TYP_STRUCT)
{
assert(argNode->gtOp.gtOp1 != nullptr && argNode->gtOp.gtOp1->OperGet() == GT_OBJ);
- argNode->gtOp.gtOp1->gtLsraInfo.dstCount = 0;
- argNode->gtLsraInfo.srcCount = 0;
+ assert(argNode->gtLsraInfo.srcCount == 0);
}
#endif // FEATURE_PUT_STRUCT_ARG_STK
-
continue;
}
-
#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
if (argNode->OperGet() == GT_FIELD_LIST)
{
- argNode->SetContained();
+ assert(argNode->isContained());
assert(varTypeIsStruct(argNode) || curArgTabEntry->isStruct);
- unsigned eightbyte = 0;
+ int i = 0;
for (GenTreeFieldList* entry = argNode->AsFieldList(); entry != nullptr; entry = entry->Rest())
{
- const regNumber argReg = eightbyte == 0 ? curArgTabEntry->regNum : curArgTabEntry->otherRegNum;
- TreeNodeInfoInitPutArgReg(entry->Current()->AsUnOp(), argReg, *info, isVarArgs, &callHasFloatRegArgs);
-
- eightbyte++;
+ const regNumber argReg = (i == 0) ? curArgTabEntry->regNum : curArgTabEntry->otherRegNum;
+ assert(entry->Current()->gtRegNum == argReg);
+ assert(i < 2);
+ i++;
}
}
else
#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
{
- TreeNodeInfoInitPutArgReg(argNode->AsUnOp(), curArgTabEntry->regNum, *info, isVarArgs,
- &callHasFloatRegArgs);
+ const regNumber argReg = curArgTabEntry->regNum;
+ assert(argNode->gtRegNum == argReg);
}
+#endif // DEBUG
}
// Now, count stack args
@@ -1361,41 +1335,11 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
if (!(args->gtFlags & GTF_LATE_ARG))
{
TreeNodeInfo* argInfo = &(arg->gtLsraInfo);
- if (argInfo->dstCount != 0)
+ if ((argInfo->dstCount != 0) && !arg->IsArgPlaceHolderNode() && !arg->isContained())
{
argInfo->isLocalDefUse = true;
}
-
- // If the child of GT_PUTARG_STK is a constant, we don't need a register to
- // move it to memory (stack location).
- //
- // On AMD64, we don't want to make 0 contained, because we can generate smaller code
- // by zeroing a register and then storing it. E.g.:
- // xor rdx, rdx
- // mov gword ptr [rsp+28H], rdx
- // is 2 bytes smaller than:
- // mov gword ptr [rsp+28H], 0
- //
- // On x86, we push stack arguments; we don't use 'mov'. So:
- // push 0
- // is 1 byte smaller than:
- // xor rdx, rdx
- // push rdx
-
- argInfo->dstCount = 0;
- if (arg->gtOper == GT_PUTARG_STK)
- {
- GenTree* op1 = arg->gtOp.gtOp1;
- if (IsContainableImmed(arg, op1)
-#if defined(_TARGET_AMD64_)
- && !op1->IsIntegralConst(0)
-#endif // _TARGET_AMD64_
- )
- {
- MakeSrcContained(arg, op1);
- arg->gtLsraInfo.srcCount--;
- }
- }
+ assert(argInfo->dstCount == 0);
}
args = args->gtOp.gtOp2;
}
@@ -1408,7 +1352,7 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
// Don't assign the call target to any of the argument registers because
// we will use them to also pass floating point arguments as required
// by Amd64 ABI.
- ctrlExpr->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~(RBM_ARG_REGS));
+ ctrlExpr->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~(RBM_ARG_REGS));
}
#endif // !FEATURE_VARARG
}
@@ -1422,19 +1366,17 @@ void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
+void LinearScan::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
{
- GenTree* dstAddr = blkNode->Addr();
- unsigned size = blkNode->gtBlkSize;
- GenTree* source = blkNode->Data();
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
+ GenTree* dstAddr = blkNode->Addr();
+ unsigned size = blkNode->gtBlkSize;
+ GenTree* source = blkNode->Data();
// Sources are dest address, initVal or source.
// We may require an additional source or temp register for the size.
- blkNode->gtLsraInfo.srcCount = 0;
- blkNode->gtLsraInfo.dstCount = 0;
- blkNode->gtLsraInfo.setInternalCandidates(l, RBM_NONE);
+ blkNode->gtLsraInfo.srcCount = GetOperandSourceCount(dstAddr);
+ assert(blkNode->gtLsraInfo.dstCount == 0);
+ blkNode->gtLsraInfo.setInternalCandidates(this, RBM_NONE);
GenTreePtr srcAddrOrFill = nullptr;
bool isInitBlk = blkNode->OperIsInitBlkOp();
@@ -1447,10 +1389,14 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
GenTree* initVal = source;
if (initVal->OperIsInitVal())
{
- initVal->SetContained();
+ assert(initVal->isContained());
initVal = initVal->gtGetOp1();
}
srcAddrOrFill = initVal;
+ if (!initVal->isContained())
+ {
+ blkNode->gtLsraInfo.srcCount++;
+ }
switch (blkNode->gtBlkOpKind)
{
@@ -1458,22 +1404,12 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
assert(initVal->IsCnsIntOrI());
if (size >= XMM_REGSIZE_BYTES)
{
- // Reserve an XMM register to fill it with
- // a pack of 16 init value constants.
- ssize_t fill = initVal->gtIntCon.gtIconVal & 0xFF;
+ // Reserve an XMM register to fill it with a pack of 16 init value constants.
blkNode->gtLsraInfo.internalFloatCount = 1;
- blkNode->gtLsraInfo.setInternalCandidates(l, l->internalFloatRegCandidates());
- if ((fill == 0) && ((size & 0xf) == 0))
- {
- MakeSrcContained(blkNode, initVal);
- }
- // Use an XMM register to fill with constants; it's an AVX instruction, so set the flags.
+ blkNode->gtLsraInfo.setInternalCandidates(this, internalFloatRegCandidates());
+ // use XMM register to fill with constants, it's AVX instruction and set the flag
SetContainsAVXFlags();
}
- if (!initVal->isContained())
- {
- blkNode->gtLsraInfo.srcCount++;
- }
#ifdef _TARGET_X86_
if ((size & 1) != 0)
{
@@ -1491,7 +1427,6 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
// a) The memory address to be in RDI.
// b) The fill value has to be in RAX.
// c) The buffer size will go in RCX.
- blkNode->gtLsraInfo.srcCount++;
dstAddrRegMask = RBM_RDI;
srcAddrOrFill = initVal;
sourceRegMask = RBM_RAX;
@@ -1501,7 +1436,6 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
case GenTreeBlk::BlkOpKindHelper:
#ifdef _TARGET_AMD64_
// The helper follows the regular AMD64 ABI.
- blkNode->gtLsraInfo.srcCount++;
dstAddrRegMask = RBM_ARG_0;
sourceRegMask = RBM_ARG_1;
blkSizeRegMask = RBM_ARG_2;
@@ -1521,23 +1455,7 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
// CopyObj or CopyBlk
if (source->gtOper == GT_IND)
{
- srcAddrOrFill = blkNode->Data()->gtGetOp1();
- // We're effectively setting source as contained, but can't call MakeSrcContained, because the
- // "inheritance" of the srcCount is to a child not a parent - it would "just work" but could be misleading.
- // If srcAddr is already non-contained, we don't need to change it.
- if (srcAddrOrFill->gtLsraInfo.getDstCount() == 0)
- {
- srcAddrOrFill->gtLsraInfo.setDstCount(1);
- srcAddrOrFill->gtLsraInfo.setSrcCount(source->gtLsraInfo.srcCount);
- }
- m_lsra->clearOperandCounts(source);
- source->SetContained();
- source->AsIndir()->Addr()->ClearContained();
- }
- else if (!source->IsMultiRegCall() && !source->OperIsSIMD())
- {
- assert(source->IsLocal());
- MakeSrcContained(blkNode, source);
+ srcAddrOrFill = source->gtGetOp1();
}
if (blkNode->OperGet() == GT_STORE_OBJ)
{
@@ -1564,7 +1482,7 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
if ((size & (XMM_REGSIZE_BYTES - 1)) != 0)
{
blkNode->gtLsraInfo.internalIntCount++;
- regMaskTP regMask = l->allRegs(TYP_INT);
+ regMaskTP regMask = allRegs(TYP_INT);
#ifdef _TARGET_X86_
if ((size & 1) != 0)
@@ -1572,7 +1490,7 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
regMask &= ~RBM_NON_BYTE_REGS;
}
#endif
- blkNode->gtLsraInfo.setInternalCandidates(l, regMask);
+ blkNode->gtLsraInfo.setInternalCandidates(this, regMask);
}
if (size >= XMM_REGSIZE_BYTES)
@@ -1581,23 +1499,11 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
// reserve an XMM register to use it for a
// series of 16-byte loads and stores.
blkNode->gtLsraInfo.internalFloatCount = 1;
- blkNode->gtLsraInfo.addInternalCandidates(l, l->internalFloatRegCandidates());
+ blkNode->gtLsraInfo.addInternalCandidates(this, internalFloatRegCandidates());
// Uses XMM reg for load and store and hence check to see whether AVX instructions
// are used for codegen, set ContainsAVX flag
SetContainsAVXFlags();
}
- // If src or dst are on stack, we don't have to generate the address
- // into a register because it's just some constant+SP.
- if ((srcAddrOrFill != nullptr) && srcAddrOrFill->OperIsLocalAddr())
- {
- MakeSrcContained(blkNode, srcAddrOrFill);
- }
-
- if (dstAddr->OperIsLocalAddr())
- {
- MakeSrcContained(blkNode, dstAddr);
- }
-
break;
case GenTreeBlk::BlkOpKindRepInstr:
@@ -1630,21 +1536,20 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
blkNode->gtLsraInfo.srcCount += GetOperandSourceCount(source);
}
- blkNode->gtLsraInfo.srcCount += GetOperandSourceCount(dstAddr);
if (dstAddrRegMask != RBM_NONE)
{
- dstAddr->gtLsraInfo.setSrcCandidates(l, dstAddrRegMask);
+ dstAddr->gtLsraInfo.setSrcCandidates(this, dstAddrRegMask);
}
if (sourceRegMask != RBM_NONE)
{
if (srcAddrOrFill != nullptr)
{
- srcAddrOrFill->gtLsraInfo.setSrcCandidates(l, sourceRegMask);
+ srcAddrOrFill->gtLsraInfo.setSrcCandidates(this, sourceRegMask);
}
else
{
// This is a local source; we'll use a temp register for its address.
- blkNode->gtLsraInfo.addInternalCandidates(l, sourceRegMask);
+ blkNode->gtLsraInfo.addInternalCandidates(this, sourceRegMask);
blkNode->gtLsraInfo.internalIntCount++;
}
}
@@ -1653,16 +1558,16 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
if (size != 0)
{
// Reserve a temp register for the block size argument.
- blkNode->gtLsraInfo.addInternalCandidates(l, blkSizeRegMask);
+ blkNode->gtLsraInfo.addInternalCandidates(this, blkSizeRegMask);
blkNode->gtLsraInfo.internalIntCount++;
}
else
{
// The block size argument is a third argument to GT_STORE_DYN_BLK
- noway_assert(blkNode->gtOper == GT_STORE_DYN_BLK);
+ assert(blkNode->gtOper == GT_STORE_DYN_BLK);
blkNode->gtLsraInfo.setSrcCount(3);
GenTree* blockSize = blkNode->AsDynBlk()->gtDynamicSize;
- blockSize->gtLsraInfo.setSrcCandidates(l, blkSizeRegMask);
+ blockSize->gtLsraInfo.setSrcCandidates(this, blkSizeRegMask);
}
}
}
@@ -1677,11 +1582,11 @@ void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
+void LinearScan::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
{
TreeNodeInfo* info = &(putArgStk->gtLsraInfo);
- LinearScan* l = m_lsra;
info->srcCount = 0;
+ assert(info->dstCount == 0);
if (putArgStk->gtOp1->gtOper == GT_FIELD_LIST)
{
@@ -1699,50 +1604,15 @@ void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
const unsigned fieldOffset = current->gtFieldOffset;
assert(fieldType != TYP_LONG);
- // For x86 we must mark all integral fields as contained or reg-optional, and handle them
- // accordingly in code generation, since we may have up to 8 fields, which cannot all be in
- // registers to be consumed atomically by the call.
- if (varTypeIsIntegralOrI(fieldNode))
- {
- if (fieldNode->OperGet() == GT_LCL_VAR)
- {
- LclVarDsc* varDsc = &(comp->lvaTable[fieldNode->AsLclVarCommon()->gtLclNum]);
- if (varDsc->lvTracked && !varDsc->lvDoNotEnregister)
- {
- SetRegOptional(fieldNode);
- }
- else
- {
- MakeSrcContained(putArgStk, fieldNode);
- }
- }
- else if (fieldNode->IsIntCnsFitsInI32())
- {
- MakeSrcContained(putArgStk, fieldNode);
- }
- else
- {
- // For the case where we cannot directly push the value, if we run out of registers,
- // it would be better to defer computation until we are pushing the arguments rather
- // than spilling, but this situation is not all that common, as most cases of promoted
- // structs do not have a large number of fields, and of those most are lclVars or
- // copy-propagated constants.
- SetRegOptional(fieldNode);
- }
- }
#if defined(FEATURE_SIMD)
- // Note that we need to check the GT_FIELD_LIST type, not the fieldType. This is because the
+ // Note that we need to check the GT_FIELD_LIST type, not 'fieldType'. This is because the
// GT_FIELD_LIST will be TYP_SIMD12 whereas the fieldType might be TYP_SIMD16 for lclVar, where
// we "round up" to 16.
- else if (current->gtFieldType == TYP_SIMD12)
+ if (current->gtFieldType == TYP_SIMD12)
{
needsSimdTemp = true;
}
#endif // defined(FEATURE_SIMD)
- else
- {
- assert(varTypeIsFloating(fieldNode) || varTypeIsSIMD(fieldNode));
- }
// We can treat as a slot any field that is stored at a slot boundary, where the previous
// field is not in the same slot. (Note that we store the fields in reverse order.)
@@ -1771,27 +1641,27 @@ void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
}
}
- info->dstCount = 0;
-
if (putArgStk->gtPutArgStkKind == GenTreePutArgStk::Kind::Push)
{
// If any of the fields cannot be stored with an actual push, we may need a temporary
// register to load the value before storing it to the stack location.
info->internalIntCount = 1;
- regMaskTP regMask = l->allRegs(TYP_INT);
+ regMaskTP regMask = allRegs(TYP_INT);
if (needsByteTemp)
{
regMask &= ~RBM_NON_BYTE_REGS;
}
- info->setInternalCandidates(l, regMask);
+ info->setInternalCandidates(this, regMask);
}
#if defined(FEATURE_SIMD)
// For PutArgStk of a TYP_SIMD12, we need a SIMD temp register.
if (needsSimdTemp)
{
+ info->srcCount = putArgStk->gtOp1->gtLsraInfo.dstCount;
+ assert(info->dstCount == 0);
info->internalFloatCount += 1;
- info->addInternalCandidates(l, l->allSIMDRegs());
+ info->addInternalCandidates(this, allSIMDRegs());
}
#endif // defined(FEATURE_SIMD)
@@ -1804,9 +1674,8 @@ void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
if (putArgStk->TypeGet() == TYP_SIMD12)
{
info->srcCount = putArgStk->gtOp1->gtLsraInfo.dstCount;
- info->dstCount = 0;
info->internalFloatCount = 1;
- info->setInternalCandidates(l, l->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
return;
}
#endif // defined(FEATURE_SIMD) && defined(_TARGET_X86_)
@@ -1821,19 +1690,7 @@ void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
GenTreePtr src = putArgStk->gtOp1;
GenTreePtr srcAddr = nullptr;
- bool haveLocalAddr = false;
- if ((src->OperGet() == GT_OBJ) || (src->OperGet() == GT_IND))
- {
- srcAddr = src->gtOp.gtOp1;
- assert(srcAddr != nullptr);
- haveLocalAddr = srcAddr->OperIsLocalAddr();
- }
- else
- {
- assert(varTypeIsSIMD(putArgStk));
- }
-
- info->dstCount = 0;
+ info->srcCount = GetOperandSourceCount(src);
// If we have a buffer between XMM_REGSIZE_BYTES and CPBLK_UNROLL_LIMIT bytes, we'll use SSE2.
// Structs and buffer with sizes <= CPBLK_UNROLL_LIMIT bytes are occurring in more than 95% of
@@ -1852,7 +1709,7 @@ void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
if ((putArgStk->gtNumberReferenceSlots == 0) && (size & (XMM_REGSIZE_BYTES - 1)) != 0)
{
info->internalIntCount++;
- regMaskTP regMask = l->allRegs(TYP_INT);
+ regMaskTP regMask = allRegs(TYP_INT);
#ifdef _TARGET_X86_
if ((size % 2) != 0)
@@ -1860,7 +1717,7 @@ void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
regMask &= ~RBM_NON_BYTE_REGS;
}
#endif
- info->setInternalCandidates(l, regMask);
+ info->setInternalCandidates(this, regMask);
}
#ifdef _TARGET_X86_
@@ -1873,30 +1730,19 @@ void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
// or larger than or equal to 8 bytes on x86, reserve an XMM register to use it for a
// series of 16-byte loads and stores.
info->internalFloatCount = 1;
- info->addInternalCandidates(l, l->internalFloatRegCandidates());
+ info->addInternalCandidates(this, internalFloatRegCandidates());
SetContainsAVXFlags();
}
break;
case GenTreePutArgStk::Kind::RepInstr:
info->internalIntCount += 3;
- info->setInternalCandidates(l, (RBM_RDI | RBM_RCX | RBM_RSI));
+ info->setInternalCandidates(this, (RBM_RDI | RBM_RCX | RBM_RSI));
break;
default:
unreached();
}
-
- // Always mark the OBJ and ADDR as contained trees by the putarg_stk. The codegen will deal with this tree.
- MakeSrcContained(putArgStk, src);
-
- if (haveLocalAddr)
- {
- // If the source address is the address of a lclVar, make the source address contained to avoid unnecessary
- // copies.
- MakeSrcContained(putArgStk, srcAddr);
- }
- info->srcCount = GetOperandSourceCount(src);
}
#endif // FEATURE_PUT_STRUCT_ARG_STK
@@ -1909,15 +1755,12 @@ void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitLclHeap(GenTree* tree)
+void LinearScan::TreeNodeInfoInitLclHeap(GenTree* tree)
{
- ContainCheckLclHeap(tree->AsOp());
- TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
+ TreeNodeInfo* info = &(tree->gtLsraInfo);
info->srcCount = 1;
- info->dstCount = 1;
+ assert(info->dstCount == 1);
// Need a variable number of temp regs (see genLclHeap() in codegenamd64.cpp):
// Here '-' means don't care.
@@ -2007,18 +1850,16 @@ void Lowering::TreeNodeInfoInitLclHeap(GenTree* tree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitModDiv(GenTree* tree)
+void LinearScan::TreeNodeInfoInitModDiv(GenTree* tree)
{
- ContainCheckDivOrMod(tree->AsOp());
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
GenTree* op1 = tree->gtGetOp1();
GenTree* op2 = tree->gtGetOp2();
info->srcCount = GetOperandSourceCount(op1);
info->srcCount += GetOperandSourceCount(op2);
- info->dstCount = 1;
+ assert(info->dstCount == 1);
if (varTypeIsFloating(tree->TypeGet()))
{
@@ -2033,13 +1874,13 @@ void Lowering::TreeNodeInfoInitModDiv(GenTree* tree)
{
// We are interested in just the remainder.
// RAX is used as a trashable register during computation of remainder.
- info->setDstCandidates(l, RBM_RDX);
+ info->setDstCandidates(this, RBM_RDX);
}
else
{
// We are interested in just the quotient.
// RDX gets used as trashable register during computation of quotient
- info->setDstCandidates(l, RBM_RAX);
+ info->setDstCandidates(this, RBM_RAX);
}
#ifdef _TARGET_X86_
@@ -2056,21 +1897,21 @@ void Lowering::TreeNodeInfoInitModDiv(GenTree* tree)
// This situation also requires an internal register.
info->internalIntCount = 1;
- info->setInternalCandidates(l, l->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allRegs(TYP_INT));
- loVal->gtLsraInfo.setSrcCandidates(l, RBM_EAX);
- hiVal->gtLsraInfo.setSrcCandidates(l, RBM_EDX);
+ loVal->gtLsraInfo.setSrcCandidates(this, RBM_EAX);
+ hiVal->gtLsraInfo.setSrcCandidates(this, RBM_EDX);
}
else
#endif
{
// If possible would like to have op1 in RAX to avoid a register move
- op1->gtLsraInfo.setSrcCandidates(l, RBM_RAX);
+ op1->gtLsraInfo.setSrcCandidates(this, RBM_RAX);
}
- if (op2->IsRegOptional())
+ if (!op2->isContained())
{
- op2->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~(RBM_RAX | RBM_RDX));
+ op2->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~(RBM_RAX | RBM_RDX));
}
}
@@ -2083,12 +1924,9 @@ void Lowering::TreeNodeInfoInitModDiv(GenTree* tree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitIntrinsic(GenTree* tree)
+void LinearScan::TreeNodeInfoInitIntrinsic(GenTree* tree)
{
- ContainCheckIntrinsic(tree->AsOp());
-
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
// Both operand and its result must be of floating point type.
GenTree* op1 = tree->gtGetOp1();
@@ -2096,7 +1934,7 @@ void Lowering::TreeNodeInfoInitIntrinsic(GenTree* tree)
assert(op1->TypeGet() == tree->TypeGet());
info->srcCount = GetOperandSourceCount(op1);
- info->dstCount = 1;
+ assert(info->dstCount == 1);
switch (tree->gtIntrinsic.gtIntrinsicId)
{
@@ -2119,7 +1957,7 @@ void Lowering::TreeNodeInfoInitIntrinsic(GenTree* tree)
if (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Abs)
{
info->internalFloatCount = 1;
- info->setInternalCandidates(l, l->internalFloatRegCandidates());
+ info->setInternalCandidates(this, internalFloatRegCandidates());
}
break;
@@ -2149,14 +1987,21 @@ void Lowering::TreeNodeInfoInitIntrinsic(GenTree* tree)
// Return Value:
// None.
-void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
+void LinearScan::TreeNodeInfoInitSIMD(GenTreeSIMD* simdTree)
{
- GenTreeSIMD* simdTree = tree->AsSIMD();
- ContainCheckSIMD(simdTree);
+ TreeNodeInfo* info = &(simdTree->gtLsraInfo);
- TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* lsra = m_lsra;
- info->dstCount = 1;
+ // Only SIMDIntrinsicInit can be contained. Other than that,
+ // only SIMDIntrinsicOpEquality and SIMDIntrinsicOpInEquality can have 0 dstCount.
+ if (simdTree->isContained())
+ {
+ assert(simdTree->gtSIMDIntrinsicID == SIMDIntrinsicInit);
+ }
+ else if (info->dstCount != 1)
+ {
+ assert((simdTree->gtSIMDIntrinsicID == SIMDIntrinsicOpEquality) ||
+ (simdTree->gtSIMDIntrinsicID == SIMDIntrinsicOpInEquality));
+ }
SetContainsAVXFlags(true, simdTree->gtSIMDSize);
switch (simdTree->gtSIMDIntrinsicID)
{
@@ -2165,7 +2010,7 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
case SIMDIntrinsicInit:
{
- op1 = tree->gtOp.gtOp1;
+ op1 = simdTree->gtOp.gtOp1;
#if !defined(_TARGET_64BIT_)
if (op1->OperGet() == GT_LONG)
@@ -2204,7 +2049,7 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
{
// need a temp
info->internalFloatCount = 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
info->isInternalRegDelayFree = true;
info->srcCount = 2;
}
@@ -2223,7 +2068,7 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
// Need an internal register to stitch together all the values into a single vector in a SIMD reg.
info->internalFloatCount = 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
break;
@@ -2245,7 +2090,7 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
// Must be a Vector<int> or Vector<short> Vector<sbyte>
assert(simdTree->gtSIMDBaseType == TYP_INT || simdTree->gtSIMDBaseType == TYP_SHORT ||
simdTree->gtSIMDBaseType == TYP_BYTE);
- assert(comp->getSIMDInstructionSet() >= InstructionSet_SSE3_4);
+ assert(compiler->getSIMDInstructionSet() >= InstructionSet_SSE3_4);
info->srcCount = 1;
break;
@@ -2268,10 +2113,10 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
// SSE2 32-bit integer multiplication requires two temp regs
if (simdTree->gtSIMDIntrinsicID == SIMDIntrinsicMul && simdTree->gtSIMDBaseType == TYP_INT &&
- comp->getSIMDInstructionSet() == InstructionSet_SSE2)
+ compiler->getSIMDInstructionSet() == InstructionSet_SSE2)
{
info->internalFloatCount = 2;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
break;
@@ -2297,13 +2142,11 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
case SIMDIntrinsicOpEquality:
case SIMDIntrinsicOpInEquality:
- info->srcCount = 2;
// On SSE4/AVX, we can generate optimal code for (in)equality
// against zero using ptest. We can safely do this optimization
// for integral vectors but not for floating-point for the reason
// that we have +0.0 and -0.0 and +0.0 == -0.0
- op2 = tree->gtGetOp2();
if (simdTree->gtGetOp2()->isContained())
{
info->srcCount = 1;
@@ -2319,7 +2162,20 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
// registers reserved are guaranteed to be different from target
// integer register without explicitly specifying.
info->internalFloatCount = 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
+ }
+ if (info->isNoRegCompare)
+ {
+ info->dstCount = 0;
+ // Codegen of SIMD (in)Equality uses target integer reg only for setting flags.
+ // A target reg is not needed on AVX when comparing against Vector Zero.
+ // In all other cases we need to reserve an int type internal register if we
+ // don't have a target register on the compare.
+ if (!compiler->canUseAVX() || !simdTree->gtGetOp2()->IsIntegralConstVector(0))
+ {
+ info->internalIntCount = 1;
+ info->addInternalCandidates(this, allRegs(TYP_INT));
+ }
}
break;
@@ -2339,24 +2195,25 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
// and the need for scratch registers.
if (varTypeIsFloating(simdTree->gtSIMDBaseType))
{
- if ((comp->getSIMDInstructionSet() == InstructionSet_SSE2) ||
+ if ((compiler->getSIMDInstructionSet() == InstructionSet_SSE2) ||
(simdTree->gtOp.gtOp1->TypeGet() == TYP_SIMD32))
{
info->internalFloatCount = 1;
info->isInternalRegDelayFree = true;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
// else don't need scratch reg(s).
}
else
{
- assert(simdTree->gtSIMDBaseType == TYP_INT && comp->getSIMDInstructionSet() >= InstructionSet_SSE3_4);
+ assert(simdTree->gtSIMDBaseType == TYP_INT &&
+ compiler->getSIMDInstructionSet() >= InstructionSet_SSE3_4);
// No need to set isInternalRegDelayFree since targetReg is a
// an int type reg and guaranteed to be different from xmm/ymm
// regs.
- info->internalFloatCount = comp->canUseAVX() ? 2 : 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->internalFloatCount = compiler->canUseAVX() ? 2 : 1;
+ info->setInternalCandidates(this, allSIMDRegs());
}
info->srcCount = 2;
break;
@@ -2367,9 +2224,10 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
// - the source SIMD struct
// - index (which element to get)
// The result is baseType of SIMD struct.
+ // op1 may be a contained memory op, but if so we will consume its address.
info->srcCount = 0;
- op1 = tree->gtOp.gtOp1;
- op2 = tree->gtOp.gtOp2;
+ op1 = simdTree->gtOp.gtOp1;
+ op2 = simdTree->gtOp.gtOp2;
// op2 may be a contained constant.
if (!op2->isContained())
@@ -2401,13 +2259,13 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
info->srcCount++;
if (!op2->IsCnsIntOrI())
{
- (void)comp->getSIMDInitTempVarNum();
+ (void)compiler->getSIMDInitTempVarNum();
}
else if (!varTypeIsFloating(simdTree->gtSIMDBaseType))
{
bool needFloatTemp;
if (varTypeIsSmallInt(simdTree->gtSIMDBaseType) &&
- (comp->getSIMDInstructionSet() == InstructionSet_AVX))
+ (compiler->getSIMDInstructionSet() == InstructionSet_AVX))
{
int byteShiftCnt = (int)op2->AsIntCon()->gtIconVal * genTypeSize(simdTree->gtSIMDBaseType);
needFloatTemp = (byteShiftCnt >= 16);
@@ -2420,7 +2278,7 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
if (needFloatTemp)
{
info->internalFloatCount = 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
}
}
@@ -2434,10 +2292,10 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
info->srcCount = 2;
// We need an internal integer register for SSE2 codegen
- if (comp->getSIMDInstructionSet() == InstructionSet_SSE2)
+ if (compiler->getSIMDInstructionSet() == InstructionSet_SSE2)
{
info->internalIntCount = 1;
- info->setInternalCandidates(lsra, lsra->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allRegs(TYP_INT));
}
break;
@@ -2454,7 +2312,7 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
info->isInternalRegDelayFree = true;
info->internalIntCount = 1;
info->internalFloatCount = 2;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs() | lsra->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allSIMDRegs() | allRegs(TYP_INT));
}
break;
@@ -2471,7 +2329,7 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
// We need an internal register different from targetReg.
info->isInternalRegDelayFree = true;
info->internalFloatCount = 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
break;
@@ -2481,7 +2339,7 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
info->isInternalRegDelayFree = true;
info->srcCount = 1;
info->internalIntCount = 1;
- if (comp->getSIMDInstructionSet() == InstructionSet_AVX)
+ if (compiler->getSIMDInstructionSet() == InstructionSet_AVX)
{
info->internalFloatCount = 2;
}
@@ -2489,7 +2347,7 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
{
info->internalFloatCount = 1;
}
- info->setInternalCandidates(lsra, lsra->allSIMDRegs() | lsra->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allSIMDRegs() | allRegs(TYP_INT));
break;
case SIMDIntrinsicConvertToDouble:
@@ -2504,7 +2362,8 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
}
else
#endif
- if ((comp->getSIMDInstructionSet() == InstructionSet_AVX) || (simdTree->gtSIMDBaseType == TYP_ULONG))
+ if ((compiler->getSIMDInstructionSet() == InstructionSet_AVX) ||
+ (simdTree->gtSIMDBaseType == TYP_ULONG))
{
info->internalFloatCount = 2;
}
@@ -2512,14 +2371,14 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
{
info->internalFloatCount = 1;
}
- info->setInternalCandidates(lsra, lsra->allSIMDRegs() | lsra->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allSIMDRegs() | allRegs(TYP_INT));
break;
case SIMDIntrinsicNarrow:
// We need an internal register different from targetReg.
info->isInternalRegDelayFree = true;
info->srcCount = 2;
- if ((comp->getSIMDInstructionSet() == InstructionSet_AVX) && (simdTree->gtSIMDBaseType != TYP_DOUBLE))
+ if ((compiler->getSIMDInstructionSet() == InstructionSet_AVX) && (simdTree->gtSIMDBaseType != TYP_DOUBLE))
{
info->internalFloatCount = 2;
}
@@ -2527,7 +2386,7 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
{
info->internalFloatCount = 1;
}
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
break;
case SIMDIntrinsicShuffleSSE2:
@@ -2563,7 +2422,7 @@ void Lowering::TreeNodeInfoInitSIMD(GenTree* tree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCast(GenTree* tree)
+void LinearScan::TreeNodeInfoInitCast(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
@@ -2579,7 +2438,7 @@ void Lowering::TreeNodeInfoInitCast(GenTree* tree)
var_types castOpType = castOp->TypeGet();
info->srcCount = GetOperandSourceCount(castOp);
- info->dstCount = 1;
+ assert(info->dstCount == 1);
if (tree->gtFlags & GTF_UNSIGNED)
{
castOpType = genUnsignedType(castOpType);
@@ -2607,7 +2466,7 @@ void Lowering::TreeNodeInfoInitCast(GenTree* tree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
+void LinearScan::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
{
assert(tree->OperGet() == GT_STOREIND);
@@ -2615,25 +2474,11 @@ void Lowering::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
GenTreePtr addr = dst->Addr();
GenTreePtr src = dst->Data();
- if (addr->OperGet() == GT_LEA)
- {
- // In the case where we are doing a helper assignment, if the dst
- // is an indir through an lea, we need to actually instantiate the
- // lea in a register
- GenTreeAddrMode* lea = addr->AsAddrMode();
-
- int leaSrcCount = 0;
- if (lea->HasBase())
- {
- leaSrcCount++;
- }
- if (lea->HasIndex())
- {
- leaSrcCount++;
- }
- lea->gtLsraInfo.srcCount = leaSrcCount;
- lea->gtLsraInfo.dstCount = 1;
- }
+ // In the case where we are doing a helper assignment, we need to actually instantiate the
+ // address in a register.
+ assert(!addr->isContained());
+ tree->gtLsraInfo.srcCount = 1 + GetIndirSourceCount(dst);
+ assert(tree->gtLsraInfo.dstCount == 0);
bool useOptimizedWriteBarrierHelper = false; // By default, assume no optimized write barriers.
@@ -2643,7 +2488,7 @@ void Lowering::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
useOptimizedWriteBarrierHelper = true; // On x86, use the optimized write barriers by default.
#ifdef DEBUG
- GCInfo::WriteBarrierForm wbf = comp->codeGen->gcInfo.gcIsWriteBarrierCandidate(tree, src);
+ GCInfo::WriteBarrierForm wbf = compiler->codeGen->gcInfo.gcIsWriteBarrierCandidate(tree, src);
if (wbf == GCInfo::WBF_NoBarrier_CheckNotHeapInDebug) // This one is always a call to a C++ method.
{
useOptimizedWriteBarrierHelper = false;
@@ -2655,8 +2500,8 @@ void Lowering::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
// Special write barrier:
// op1 (addr) goes into REG_WRITE_BARRIER (rdx) and
// op2 (src) goes into any int register.
- addr->gtLsraInfo.setSrcCandidates(m_lsra, RBM_WRITE_BARRIER);
- src->gtLsraInfo.setSrcCandidates(m_lsra, RBM_WRITE_BARRIER_SRC);
+ addr->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER);
+ src->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_SRC);
}
#else // !defined(_TARGET_X86_)
@@ -2670,8 +2515,8 @@ void Lowering::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
// For the standard JIT Helper calls:
// op1 (addr) goes into REG_ARG_0 and
// op2 (src) goes into REG_ARG_1
- addr->gtLsraInfo.setSrcCandidates(m_lsra, RBM_ARG_0);
- src->gtLsraInfo.setSrcCandidates(m_lsra, RBM_ARG_1);
+ addr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
+ src->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
}
// Both src and dst must reside in a register, which they should since we haven't set
@@ -2686,7 +2531,7 @@ void Lowering::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
// Arguments:
// indirTree - GT_IND or GT_STOREIND gentree node
//
-void Lowering::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
+void LinearScan::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
{
// If this is the rhs of a block copy (i.e. non-enregisterable struct),
// it has no register requirements.
@@ -2695,32 +2540,48 @@ void Lowering::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
return;
}
- ContainCheckIndir(indirTree);
-
- GenTree* addr = indirTree->gtGetOp1();
TreeNodeInfo* info = &(indirTree->gtLsraInfo);
- GenTreePtr base = nullptr;
- GenTreePtr index = nullptr;
- unsigned mul, cns;
- bool rev;
-
info->srcCount = GetIndirSourceCount(indirTree);
if (indirTree->gtOper == GT_STOREIND)
{
GenTree* source = indirTree->gtOp.gtOp2;
if (indirTree->AsStoreInd()->IsRMWMemoryOp())
{
+ // Because 'source' is contained, we haven't yet determined its special register requirements, if any.
+ // As it happens, the Shift or Rotate cases are the only ones with special requirements.
+ assert(source->isContained() && source->OperIsRMWMemOp());
+ GenTree* nonMemSource = nullptr;
+
+ if (source->OperIsShiftOrRotate())
+ {
+ TreeNodeInfoInitShiftRotate(source);
+ }
if (indirTree->AsStoreInd()->IsRMWDstOp1())
{
if (source->OperIsBinary())
{
- info->srcCount += GetOperandSourceCount(source->gtOp.gtOp2);
+ nonMemSource = source->gtOp.gtOp2;
}
}
else if (indirTree->AsStoreInd()->IsRMWDstOp2())
{
- info->srcCount += GetOperandSourceCount(source->gtOp.gtOp1);
+ nonMemSource = source->gtOp.gtOp1;
+ }
+ if (nonMemSource != nullptr)
+ {
+ info->srcCount += GetOperandSourceCount(nonMemSource);
+ assert(!nonMemSource->isContained() || (!nonMemSource->isMemoryOp() && !nonMemSource->IsLocal()));
+#ifdef _TARGET_X86_
+ if (varTypeIsByte(indirTree) && !nonMemSource->isContained())
+ {
+ // If storeInd is of TYP_BYTE, set source to byteable registers.
+ regMaskTP regMask = nonMemSource->gtLsraInfo.getSrcCandidates(this);
+ regMask &= ~RBM_NON_BYTE_REGS;
+ assert(regMask != RBM_NONE);
+ nonMemSource->gtLsraInfo.setSrcCandidates(this, regMask);
+ }
+#endif
}
}
else
@@ -2731,10 +2592,10 @@ void Lowering::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
if (varTypeIsByte(indirTree) && !source->isContained())
{
// If storeInd is of TYP_BYTE, set source to byteable registers.
- regMaskTP regMask = source->gtLsraInfo.getSrcCandidates(m_lsra);
+ regMaskTP regMask = source->gtLsraInfo.getSrcCandidates(this);
regMask &= ~RBM_NON_BYTE_REGS;
assert(regMask != RBM_NONE);
- source->gtLsraInfo.setSrcCandidates(m_lsra, regMask);
+ source->gtLsraInfo.setSrcCandidates(this, regMask);
}
#endif
}
@@ -2757,7 +2618,7 @@ void Lowering::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
info->isInternalRegDelayFree = true;
}
- info->setInternalCandidates(m_lsra, m_lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
return;
}
@@ -2775,14 +2636,20 @@ void Lowering::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCmp(GenTreePtr tree)
+void LinearScan::TreeNodeInfoInitCmp(GenTreePtr tree)
{
assert(tree->OperIsCompare() || tree->OperIs(GT_CMP));
- ContainCheckCompare(tree->AsOp());
TreeNodeInfo* info = &(tree->gtLsraInfo);
info->srcCount = 0;
- info->dstCount = tree->OperIs(GT_CMP) ? 0 : 1;
+ if (info->isNoRegCompare)
+ {
+ info->dstCount = 0;
+ }
+ else
+ {
+ assert((info->dstCount == 1) || tree->OperIs(GT_CMP));
+ }
#ifdef _TARGET_X86_
// If the compare is used by a jump, we just need to set the condition codes. If not, then we need
@@ -2790,7 +2657,7 @@ void Lowering::TreeNodeInfoInitCmp(GenTreePtr tree)
// We always set the dst candidates, though, because if this is compare is consumed by a jump, they
// won't be used. We might be able to use GTF_RELOP_JMP_USED to determine this case, but it's not clear
// that flag is maintained until this location (especially for decomposed long compares).
- info->setDstCandidates(m_lsra, RBM_BYTE_REGS);
+ info->setDstCandidates(this, RBM_BYTE_REGS);
#endif // _TARGET_X86_
GenTreePtr op1 = tree->gtOp.gtOp1;
@@ -2798,7 +2665,10 @@ void Lowering::TreeNodeInfoInitCmp(GenTreePtr tree)
var_types op1Type = op1->TypeGet();
var_types op2Type = op2->TypeGet();
- info->srcCount += GetOperandSourceCount(op1);
+ if (!op1->gtLsraInfo.isNoRegCompare)
+ {
+ info->srcCount += GetOperandSourceCount(op1);
+ }
info->srcCount += GetOperandSourceCount(op2);
#if !defined(_TARGET_64BIT_)
@@ -2816,166 +2686,6 @@ void Lowering::TreeNodeInfoInitCmp(GenTreePtr tree)
#endif // !defined(_TARGET_64BIT_)
}
-//--------------------------------------------------------------------------------------------
-// TreeNodeInfoInitIfRMWMemOp: Checks to see if there is a RMW memory operation rooted at
-// GT_STOREIND node and if so will mark register requirements for nodes under storeInd so
-// that CodeGen will generate a single instruction of the form:
-//
-// binOp [addressing mode], reg
-//
-// Parameters
-// storeInd - GT_STOREIND node
-//
-// Return value
-// True, if RMW memory op tree pattern is recognized and op counts are set.
-// False otherwise.
-//
-bool Lowering::TreeNodeInfoInitIfRMWMemOp(GenTreePtr storeInd)
-{
- assert(storeInd->OperGet() == GT_STOREIND);
-
- // SSE2 doesn't support RMW on float values
- assert(!varTypeIsFloating(storeInd));
-
- // Terminology:
- // indirDst = memory write of an addr mode (i.e. storeind destination)
- // indirSrc = value being written to memory (i.e. storeind source which could a binary/unary op)
- // indirCandidate = memory read i.e. a gtInd of an addr mode
- // indirOpSource = source operand used in binary/unary op (i.e. source operand of indirSrc node)
-
- GenTreePtr indirCandidate = nullptr;
- GenTreePtr indirOpSource = nullptr;
-
- if (!IsRMWMemOpRootedAtStoreInd(storeInd, &indirCandidate, &indirOpSource))
- {
- JITDUMP("Lower of StoreInd didn't mark the node as self contained for reason: %d\n",
- storeInd->AsStoreInd()->GetRMWStatus());
- DISPTREERANGE(BlockRange(), storeInd);
- return false;
- }
-
- GenTreePtr indirDst = storeInd->gtGetOp1();
- GenTreePtr indirSrc = storeInd->gtGetOp2();
- genTreeOps oper = indirSrc->OperGet();
-
- // At this point we have successfully detected a RMW memory op of one of the following forms
- // storeInd(indirDst, indirSrc(indirCandidate, indirOpSource)) OR
- // storeInd(indirDst, indirSrc(indirOpSource, indirCandidate) in case of commutative operations OR
- // storeInd(indirDst, indirSrc(indirCandidate) in case of unary operations
- //
- // Here indirSrc = one of the supported binary or unary operation for RMW of memory
- // indirCandidate = a GT_IND node
- // indirCandidateChild = operand of GT_IND indirCandidate
- //
- // The logic below essentially does the following
- // Make indirOpSource contained.
- // Make indirSrc contained.
- // Make indirCandidate contained.
- // Make indirCandidateChild contained.
- // Make indirDst contained except when it is a GT_LCL_VAR or GT_CNS_INT that doesn't fit within addr
- // base.
- // Note that due to the way containment is supported, we accomplish some of the above by clearing operand counts
- // and directly propagating them upward.
- //
-
- TreeNodeInfo* info = &(storeInd->gtLsraInfo);
- info->dstCount = 0;
-
- if (GenTree::OperIsBinary(oper))
- {
- // On Xarch RMW operations require that the non-rmw operand be an immediate or in a register.
- // Therefore, if we have previously marked the indirOpSource as a contained memory op while lowering
- // the binary node, we need to reset that now.
- if (IsContainableMemoryOp(indirOpSource, true))
- {
- indirOpSource->ClearContained();
- }
- assert(!indirOpSource->isContained() || indirOpSource->OperIsConst());
- JITDUMP("Lower succesfully detected an assignment of the form: *addrMode BinOp= source\n");
- info->srcCount = indirOpSource->gtLsraInfo.dstCount;
- }
- else
- {
- assert(GenTree::OperIsUnary(oper));
- JITDUMP("Lower succesfully detected an assignment of the form: *addrMode = UnaryOp(*addrMode)\n");
- info->srcCount = 0;
- }
- DISPTREERANGE(BlockRange(), storeInd);
-
- m_lsra->clearOperandCounts(indirSrc);
- indirSrc->SetContained();
- m_lsra->clearOperandCounts(indirCandidate);
- indirCandidate->SetContained();
-
- GenTreePtr indirCandidateChild = indirCandidate->gtGetOp1();
- if (indirCandidateChild->OperGet() == GT_LEA)
- {
- GenTreeAddrMode* addrMode = indirCandidateChild->AsAddrMode();
-
- if (addrMode->HasBase())
- {
- assert(addrMode->Base()->OperIsLeaf());
- m_lsra->clearOperandCounts(addrMode->Base());
- addrMode->Base()->SetContained();
- info->srcCount++;
- }
-
- if (addrMode->HasIndex())
- {
- assert(addrMode->Index()->OperIsLeaf());
- m_lsra->clearOperandCounts(addrMode->Index());
- addrMode->Index()->SetContained();
- info->srcCount++;
- }
-
- m_lsra->clearOperandCounts(indirDst);
- indirDst->SetContained();
- }
- else
- {
- assert(indirCandidateChild->OperGet() == GT_LCL_VAR || indirCandidateChild->OperGet() == GT_LCL_VAR_ADDR ||
- indirCandidateChild->OperGet() == GT_CLS_VAR_ADDR || indirCandidateChild->OperGet() == GT_CNS_INT);
-
- // If it is a GT_LCL_VAR, it still needs the reg to hold the address.
- // We would still need a reg for GT_CNS_INT if it doesn't fit within addressing mode base.
- // For GT_CLS_VAR_ADDR, we don't need a reg to hold the address, because field address value is known at jit
- // time. Also, we don't need a reg for GT_CLS_VAR_ADDR.
- if (indirCandidateChild->OperGet() == GT_LCL_VAR_ADDR || indirCandidateChild->OperGet() == GT_CLS_VAR_ADDR)
- {
- m_lsra->clearOperandCounts(indirDst);
- indirDst->SetContained();
- }
- else if (indirCandidateChild->IsCnsIntOrI() && indirCandidateChild->AsIntConCommon()->FitsInAddrBase(comp))
- {
- m_lsra->clearOperandCounts(indirDst);
- indirDst->SetContained();
- }
- else
- {
- // Need a reg and hence increment src count of storeind
- info->srcCount += indirCandidateChild->gtLsraInfo.dstCount;
- }
- }
- m_lsra->clearOperandCounts(indirCandidateChild);
- indirCandidateChild->SetContained();
-
-#ifdef _TARGET_X86_
- if (varTypeIsByte(storeInd))
- {
- // If storeInd is of TYP_BYTE, set indirOpSources to byteable registers.
- bool containedNode = indirOpSource->gtLsraInfo.dstCount == 0;
- if (!containedNode)
- {
- regMaskTP regMask = indirOpSource->gtLsraInfo.getSrcCandidates(m_lsra);
- assert(regMask != RBM_NONE);
- indirOpSource->gtLsraInfo.setSrcCandidates(m_lsra, regMask & ~RBM_NON_BYTE_REGS);
- }
- }
-#endif
-
- return true;
-}
-
//------------------------------------------------------------------------
// TreeNodeInfoInitMul: Set the NodeInfo for a multiply.
//
@@ -2985,22 +2695,19 @@ bool Lowering::TreeNodeInfoInitIfRMWMemOp(GenTreePtr storeInd)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitMul(GenTreePtr tree)
+void LinearScan::TreeNodeInfoInitMul(GenTreePtr tree)
{
- ContainCheckMul(tree->AsOp());
-
#if defined(_TARGET_X86_)
assert(tree->OperIs(GT_MUL, GT_MULHI, GT_MUL_LONG));
#else
assert(tree->OperIs(GT_MUL, GT_MULHI));
#endif
TreeNodeInfo* info = &(tree->gtLsraInfo);
-
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- info->srcCount = GetOperandSourceCount(op1);
+ GenTree* op1 = tree->gtOp.gtOp1;
+ GenTree* op2 = tree->gtOp.gtOp2;
+ info->srcCount = GetOperandSourceCount(op1);
info->srcCount += GetOperandSourceCount(op2);
- info->dstCount = 1;
+ assert(info->dstCount == 1);
// Case of float/double mul.
if (varTypeIsFloating(tree->TypeGet()))
@@ -3035,19 +2742,19 @@ void Lowering::TreeNodeInfoInitMul(GenTreePtr tree)
// Here we set RAX as the only destination candidate
// In LSRA we set the kill set for this operation to RBM_RAX|RBM_RDX
//
- info->setDstCandidates(m_lsra, RBM_RAX);
+ info->setDstCandidates(this, RBM_RAX);
}
else if (tree->OperGet() == GT_MULHI)
{
// Have to use the encoding:RDX:RAX = RAX * rm. Since we only care about the
// upper 32 bits of the result set the destination candidate to REG_RDX.
- info->setDstCandidates(m_lsra, RBM_RDX);
+ info->setDstCandidates(this, RBM_RDX);
}
#if defined(_TARGET_X86_)
else if (tree->OperGet() == GT_MUL_LONG)
{
// have to use the encoding:RDX:RAX = RAX * rm
- info->setDstCandidates(m_lsra, RBM_RAX);
+ info->setDstCandidates(this, RBM_RAX);
}
#endif
GenTree* containedMemOp = nullptr;
@@ -3075,18 +2782,18 @@ void Lowering::TreeNodeInfoInitMul(GenTreePtr tree)
// isFloatingPointType - true if it is floating point type
// sizeOfSIMDVector - SIMD Vector size
//
-void Lowering::SetContainsAVXFlags(bool isFloatingPointType /* = true */, unsigned sizeOfSIMDVector /* = 0*/)
+void LinearScan::SetContainsAVXFlags(bool isFloatingPointType /* = true */, unsigned sizeOfSIMDVector /* = 0*/)
{
#ifdef FEATURE_AVX_SUPPORT
if (isFloatingPointType)
{
- if (comp->getFloatingPointInstructionSet() == InstructionSet_AVX)
+ if (compiler->getFloatingPointInstructionSet() == InstructionSet_AVX)
{
- comp->getEmitter()->SetContainsAVX(true);
+ compiler->getEmitter()->SetContainsAVX(true);
}
- if (sizeOfSIMDVector == 32 && comp->getSIMDInstructionSet() == InstructionSet_AVX)
+ if (sizeOfSIMDVector == 32 && compiler->getSIMDInstructionSet() == InstructionSet_AVX)
{
- comp->getEmitter()->SetContains256bitAVX(true);
+ compiler->getEmitter()->SetContains256bitAVX(true);
}
}
#endif
@@ -3103,7 +2810,7 @@ void Lowering::SetContainsAVXFlags(bool isFloatingPointType /* = true */, unsign
// Return Value:
// If we need to exclude non-byteable registers
//
-bool Lowering::ExcludeNonByteableRegisters(GenTree* tree)
+bool LinearScan::ExcludeNonByteableRegisters(GenTree* tree)
{
// Example1: GT_STOREIND(byte, addr, op2) - storeind of byte sized value from op2 into mem 'addr'
// Storeind itself will not produce any value and hence dstCount=0. But op2 could be TYP_INT
@@ -3171,7 +2878,7 @@ bool Lowering::ExcludeNonByteableRegisters(GenTree* tree)
GenTree* op1 = simdNode->gtGetOp1();
GenTree* op2 = simdNode->gtGetOp2();
var_types baseType = simdNode->gtSIMDBaseType;
- if (!IsContainableMemoryOp(op1, true) && op2->IsCnsIntOrI() && varTypeIsSmallInt(baseType))
+ if (!isContainableMemoryOp(op1) && op2->IsCnsIntOrI() && varTypeIsSmallInt(baseType))
{
bool ZeroOrSignExtnReqd = true;
unsigned baseSize = genTypeSize(baseType);
@@ -3214,7 +2921,7 @@ bool Lowering::ExcludeNonByteableRegisters(GenTree* tree)
// Return Value:
// The number of source registers used by the *parent* of this node.
//
-int Lowering::GetOperandSourceCount(GenTree* node)
+int LinearScan::GetOperandSourceCount(GenTree* node)
{
if (!node->isContained())
{
@@ -3230,9 +2937,6 @@ int Lowering::GetOperandSourceCount(GenTree* node)
if (node->OperIsIndir())
{
const unsigned srcCount = GetIndirSourceCount(node->AsIndir());
- // TODO-Cleanup: Once we are doing containment analysis during Lowering, this
- // can be removed, or changed to an assert.
- node->gtLsraInfo.srcCount = 0;
return srcCount;
}
diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
index 9beff05fd8..822510833b 100644
--- a/src/jit/morph.cpp
+++ b/src/jit/morph.cpp
@@ -63,11 +63,6 @@ GenTreePtr Compiler::fgMorphIntoHelperCall(GenTreePtr tree, int helper, GenTreeA
// The helper call ought to be semantically equivalent to the original node, so preserve its VN.
tree->ChangeOper(GT_CALL, GenTree::PRESERVE_VN);
- tree->gtFlags |= GTF_CALL;
- if (args)
- {
- tree->gtFlags |= (args->gtFlags & GTF_ALL_EFFECT);
- }
tree->gtCall.gtCallType = CT_HELPER;
tree->gtCall.gtCallMethHnd = eeFindHelper(helper);
tree->gtCall.gtCallArgs = args;
@@ -104,6 +99,20 @@ GenTreePtr Compiler::fgMorphIntoHelperCall(GenTreePtr tree, int helper, GenTreeA
}
#endif // _TARGET_XXX_
+ if (tree->OperMayThrow(this))
+ {
+ tree->gtFlags |= GTF_EXCEPT;
+ }
+ else
+ {
+ tree->gtFlags &= ~GTF_EXCEPT;
+ }
+ tree->gtFlags |= GTF_CALL;
+ if (args)
+ {
+ tree->gtFlags |= (args->gtFlags & GTF_ALL_EFFECT);
+ }
+
/* Perform the morphing */
tree = fgMorphArgs(tree->AsCall());
@@ -152,7 +161,6 @@ GenTreePtr Compiler::fgMorphCast(GenTreePtr tree)
}
var_types srcType = genActualType(oper->TypeGet());
- unsigned srcSize;
var_types dstType = tree->CastToType();
unsigned dstSize = genTypeSize(dstType);
@@ -503,21 +511,28 @@ OPTIMIZECAST:
/* Just in case new side effects were introduced */
tree->gtFlags |= (oper->gtFlags & GTF_ALL_EFFECT);
- srcType = oper->TypeGet();
-
- /* if GTF_UNSIGNED is set then force srcType to an unsigned type */
- if (tree->gtFlags & GTF_UNSIGNED)
- {
- srcType = genUnsignedType(srcType);
- }
-
- srcSize = genTypeSize(srcType);
-
if (!gtIsActiveCSE_Candidate(tree)) // tree cannot be a CSE candidate
{
+ srcType = oper->TypeGet();
+
/* See if we can discard the cast */
if (varTypeIsIntegral(srcType) && varTypeIsIntegral(dstType))
{
+ if (tree->IsUnsigned() && !varTypeIsUnsigned(srcType))
+ {
+ if (varTypeIsSmall(srcType))
+ {
+ // Small signed values are automatically sign extended to TYP_INT. If the cast is interpreting the
+ // resulting TYP_INT value as unsigned then the "sign" bits end up being "value" bits and srcType
+ // must be TYP_UINT, not the original small signed type. Otherwise "conv.ovf.i2.un(i1(-1))" is
+ // wrongly treated as a widening conversion from i1 to i2 when in fact it is a narrowing conversion
+ // from u4 to i2.
+ srcType = genActualType(srcType);
+ }
+
+ srcType = genUnsignedType(srcType);
+ }
+
if (srcType == dstType)
{ // Certainly if they are identical it is pointless
goto REMOVE_CAST;
@@ -533,9 +548,10 @@ OPTIMIZECAST:
}
}
- bool unsignedSrc = varTypeIsUnsigned(srcType);
- bool unsignedDst = varTypeIsUnsigned(dstType);
- bool signsDiffer = (unsignedSrc != unsignedDst);
+ bool unsignedSrc = varTypeIsUnsigned(srcType);
+ bool unsignedDst = varTypeIsUnsigned(dstType);
+ bool signsDiffer = (unsignedSrc != unsignedDst);
+ unsigned srcSize = genTypeSize(srcType);
// For same sized casts with
// the same signs or non-overflow cast we discard them as well
@@ -574,8 +590,7 @@ OPTIMIZECAST:
}
}
}
-
- if (srcSize < dstSize) // widening cast
+ else if (srcSize < dstSize) // widening cast
{
// Keep any long casts
if (dstSize == sizeof(int))
@@ -587,14 +602,18 @@ OPTIMIZECAST:
}
}
- // Casts from signed->unsigned can never overflow while widening
+ // Widening casts from unsigned or to signed can never overflow
if (unsignedSrc || !unsignedDst)
{
tree->gtFlags &= ~GTF_OVERFLOW;
+ if (!(oper->gtFlags & GTF_EXCEPT))
+ {
+ tree->gtFlags &= ~GTF_EXCEPT;
+ }
}
}
- else
+ else // if (srcSize > dstSize)
{
// Try to narrow the operand of the cast and discard the cast
// Note: Do not narrow a cast that is marked as a CSE
@@ -1579,8 +1598,12 @@ void fgArgInfo::ArgsComplete()
// For RyuJIT backend we will expand a Multireg arg into a GT_FIELD_LIST
// with multiple indirections, so here we consider spilling it into a tmp LclVar.
//
-
+ CLANG_FORMAT_COMMENT_ANCHOR;
+#ifdef _TARGET_ARM_
+ bool isMultiRegArg = (curArgTabEntry->numRegs > 0) && (curArgTabEntry->numRegs + curArgTabEntry->numSlots > 1);
+#else
bool isMultiRegArg = (curArgTabEntry->numRegs > 1);
+#endif
if ((argx->TypeGet() == TYP_STRUCT) && (curArgTabEntry->needTmp == false))
{
@@ -1589,10 +1612,6 @@ void fgArgInfo::ArgsComplete()
// Spill multireg struct arguments that have Assignments or Calls embedded in them
curArgTabEntry->needTmp = true;
}
-#ifndef _TARGET_ARM_
- // TODO-Arm: This optimization is not implemented for ARM32
- // so we skip this for ARM32 until it is ported to use RyuJIT backend
- //
else
{
// We call gtPrepareCost to measure the cost of evaluating this tree
@@ -1603,6 +1622,10 @@ void fgArgInfo::ArgsComplete()
// Spill multireg struct arguments that are expensive to evaluate twice
curArgTabEntry->needTmp = true;
}
+#ifndef _TARGET_ARM_
+ // TODO-Arm: This optimization is not implemented for ARM32
+ // so we skip this for ARM32 until it is ported to use RyuJIT backend
+ //
else if (argx->OperGet() == GT_OBJ)
{
GenTreeObj* argObj = argx->AsObj();
@@ -1644,8 +1667,8 @@ void fgArgInfo::ArgsComplete()
break;
}
}
- }
#endif // !_TARGET_ARM_
+ }
}
#endif // FEATURE_MULTIREG_ARGS
#endif // LEGACY_BACKEND
@@ -2878,6 +2901,37 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
nonStandardArgs.Add(arg1, REG_PINVOKE_FRAME);
}
#endif // defined(_TARGET_X86_) || defined(_TARGET_ARM_)
+#if defined(_TARGET_ARM_)
+ else if (call->gtCallMoreFlags & GTF_CALL_M_SECURE_DELEGATE_INV)
+ {
+ GenTree* arg = call->gtCallObjp;
+ if (arg->OperIsLocal())
+ {
+ arg = gtClone(arg, true);
+ }
+ else
+ {
+ GenTree* tmp = fgInsertCommaFormTemp(&arg);
+ call->gtCallObjp = arg;
+ call->gtFlags |= GTF_ASG;
+ arg = tmp;
+ }
+ noway_assert(arg != nullptr);
+
+ GenTree* newArg = new (this, GT_ADDR)
+ GenTreeAddrMode(TYP_BYREF, arg, nullptr, 0, eeGetEEInfo()->offsetOfSecureDelegateIndirectCell);
+
+ // Append newArg as the last arg
+ GenTreeArgList** insertionPoint = &call->gtCallArgs;
+ for (; *insertionPoint != nullptr; insertionPoint = &(*insertionPoint)->Rest())
+ {
+ }
+ *insertionPoint = gtNewListNode(newArg, nullptr);
+
+ numArgs++;
+ nonStandardArgs.Add(newArg, virtualStubParamInfo->GetReg());
+ }
+#endif // defined(_TARGET_ARM_)
#if defined(_TARGET_X86_)
// The x86 shift helpers have custom calling conventions and expect the lo part of the long to be in EAX and the
// hi part to be in EDX. This sets the argument registers up correctly.
@@ -3172,7 +3226,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
argx = fgMorphTree(*parentArgx);
*parentArgx = argx;
- flagsSummary |= argx->gtFlags;
assert(args->OperIsList());
assert(argx == args->Current());
@@ -3450,13 +3503,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
#error Unsupported or unset target architecture
#endif // _TARGET_XXX_
}
-#ifdef _TARGET_ARM_
- else if (isHfaArg)
- {
- size = GetHfaCount(argx);
- hasMultiregStructArgs = true;
- }
-#endif // _TARGET_ARM_
else // struct type
{
// We handle two opcodes: GT_MKREFANY and GT_OBJ
@@ -3556,11 +3602,18 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
// As we can optimize these by turning them into a GT_IND of the correct type
//
// Check for cases that we cannot optimize:
- //
+ CLANG_FORMAT_COMMENT_ANCHOR;
+#ifdef _TARGET_ARM_
+ if (((originalSize > TARGET_POINTER_SIZE) && // it is struct that is larger than a pointer
+ howToPassStruct != SPK_PrimitiveType) || // it is struct that is not one double HFA
+ !isPow2(originalSize) || // it is not a power of two (1, 2, 4 or 8)
+ (isHfaArg && (howToPassStruct != SPK_PrimitiveType))) // it is a one element HFA struct
+#else // !_TARGET_ARM_
if ((originalSize > TARGET_POINTER_SIZE) || // it is struct that is larger than a pointer
!isPow2(originalSize) || // it is not a power of two (1, 2, 4 or 8)
(isHfaArg && (hfaSlots != 1))) // it is a one element HFA struct
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#endif // !_TARGET_ARM_
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
{
// Normalize 'size' to the number of pointer sized items
// 'size' is the number of register slots that we will use to pass the argument
@@ -3684,8 +3737,14 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
// primitives
if (isHfaArg)
{
+#ifdef _TARGET_ARM_
+ // If we reach here with an HFA arg it has to be a one element HFA
+ // If HFA type is double and it has one element, hfaSlot is 2
+ assert(hfaSlots == 1 || (hfaSlots == 2 && hfaType == TYP_DOUBLE));
+#else
// If we reach here with an HFA arg it has to be a one element HFA
assert(hfaSlots == 1);
+#endif
structBaseType = hfaType; // change the indirection type to a floating point type
}
@@ -3782,6 +3841,12 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
((copyBlkClass != NO_CLASS_HANDLE) && varTypeIsIntegral(structBaseType)));
size = 1;
+#ifdef _TARGET_ARM_
+ if (isHfaArg)
+ {
+ size = hfaSlots;
+ }
+#endif
}
#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
@@ -3824,12 +3889,24 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
}
}
-#if defined(_TARGET_64BIT_) || defined(_TARGET_ARM_)
+#if defined(_TARGET_64BIT_)
if (size > 1)
{
hasMultiregStructArgs = true;
}
-#endif // _TARGET_64BIT || _TARGET_ARM_
+#elif defined(_TARGET_ARM_)
+ if (isHfaArg)
+ {
+ if (size > genTypeStSz(hfaType))
+ {
+ hasMultiregStructArgs = true;
+ }
+ }
+ else if (size > 1)
+ {
+ hasMultiregStructArgs = true;
+ }
+#endif // _TARGET_ARM_
}
// The 'size' value has now must have been set. (the original value of zero is an invalid value)
@@ -4094,6 +4171,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
if (newArgEntry->isNonStandard)
{
+ flagsSummary |= args->Current()->gtFlags;
continue;
}
@@ -4181,14 +4259,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
fgMakeOutgoingStructArgCopy(call, args, argIndex,
copyBlkClass FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(&structDesc));
- // This can cause a GTF_EXCEPT flag to be set.
- // TODO-CQ: Fix the cases where this happens. We shouldn't be adding any new flags.
- // This currently occurs in the case where we are re-morphing the args on x86/RyuJIT, and
- // there are no register arguments. Then reMorphing is never true, so we keep re-copying
- // any struct arguments.
- // i.e. assert(((call->gtFlags & GTF_EXCEPT) != 0) || ((args->Current()->gtFlags & GTF_EXCEPT) == 0)
- flagsSummary |= (args->Current()->gtFlags & GTF_EXCEPT);
-
#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
hasStackArgCopy = true;
#endif
@@ -4288,6 +4358,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
{
argSlots += size;
}
+ flagsSummary |= args->Current()->gtFlags;
} // end foreach argument loop
if (!reMorphing)
@@ -4380,9 +4451,15 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
}
#endif // FEATURE_FIXED_OUT_ARGS
- /* Update the 'side effect' flags value for the call */
+ // Clear the ASG and EXCEPT (if possible) flags on the call node
+ call->gtFlags &= ~GTF_ASG;
+ if (!call->OperMayThrow(this))
+ {
+ call->gtFlags &= ~GTF_EXCEPT;
+ }
- call->gtFlags |= (flagsSummary & GTF_ALL_EFFECT);
+ // Union in the side effect flags from the call's operands
+ call->gtFlags |= flagsSummary & GTF_ALL_EFFECT;
// If the register arguments have already been determined
// or we have no register arguments then we don't need to
@@ -4766,15 +4843,26 @@ GenTreePtr Compiler::fgMorphMultiregStructArg(GenTreePtr arg, fgArgTabEntryPtr f
#endif
#ifdef _TARGET_ARM_
- if (fgEntryPtr->isSplit)
+ if ((fgEntryPtr->isSplit && fgEntryPtr->numSlots + fgEntryPtr->numRegs > 4) ||
+ (!fgEntryPtr->isSplit && fgEntryPtr->regNum == REG_STK))
{
- if (fgEntryPtr->numSlots + fgEntryPtr->numRegs > 4)
+ // If already OBJ it is set properly already.
+ if (arg->OperGet() == GT_OBJ)
{
return arg;
}
- }
- else if (!fgEntryPtr->isHfaRegArg && fgEntryPtr->numSlots > 4)
- {
+
+ assert(arg->OperGet() == GT_LCL_VAR);
+
+ // We need to construct a `GT_OBJ` node for the argmuent,
+ // so we need to get the address of the lclVar.
+ GenTreeLclVarCommon* lclCommon = arg->AsLclVarCommon();
+
+ arg = gtNewOperNode(GT_ADDR, TYP_I_IMPL, arg);
+
+ // Create an Obj of the temp to use it as a call argument.
+ arg = gtNewObjNode(lvaGetStruct(lclCommon->gtLclNum), arg);
+
return arg;
}
#endif
@@ -5192,15 +5280,10 @@ GenTreePtr Compiler::fgMorphMultiregStructArg(GenTreePtr arg, fgArgTabEntryPtr f
{
curAddr = baseAddr;
}
- GenTreePtr curItem = gtNewOperNode(GT_IND, type[inx], curAddr);
+ GenTreePtr curItem = gtNewIndir(type[inx], curAddr);
// For safety all GT_IND should have at least GT_GLOB_REF set.
curItem->gtFlags |= GTF_GLOB_REF;
- if (fgAddrCouldBeNull(curItem))
- {
- // This indirection can cause a GPF if the address could be null.
- curItem->gtFlags |= GTF_EXCEPT;
- }
listEntry = new (this, GT_FIELD_LIST) GenTreeFieldList(curItem, offset, type[inx], listEntry);
if (newArg == nullptr)
@@ -5335,10 +5418,10 @@ void Compiler::fgMakeOutgoingStructArgCopy(
// to ref counting of the lclVars.
lvaTable[tmp].incRefCnts(compCurBB->getBBWeight(this), this);
- GenTreePtr src;
if (argx->gtOper == GT_OBJ)
{
argx->gtFlags &= ~(GTF_ALL_EFFECT) | (argx->AsBlk()->Addr()->gtFlags & GTF_ALL_EFFECT);
+ argx->SetIndirExceptionFlags(this);
}
else
{
@@ -5644,76 +5727,78 @@ void Compiler::fgMoveOpsLeft(GenTreePtr tree)
/*****************************************************************************/
-void Compiler::fgSetRngChkTarget(GenTreePtr tree, bool delay)
+void Compiler::fgSetRngChkTarget(GenTree* tree, bool delay)
{
- GenTreeBoundsChk* bndsChk = nullptr;
- SpecialCodeKind kind = SCK_RNGCHK_FAIL;
-
-#ifdef FEATURE_SIMD
- if ((tree->gtOper == GT_ARR_BOUNDS_CHECK) || (tree->gtOper == GT_SIMD_CHK))
-#else // FEATURE_SIMD
- if (tree->gtOper == GT_ARR_BOUNDS_CHECK)
-#endif // FEATURE_SIMD
+ if (tree->OperIsBoundsCheck())
{
- bndsChk = tree->AsBoundsChk();
- kind = tree->gtBoundsChk.gtThrowKind;
+ GenTreeBoundsChk* const boundsChk = tree->AsBoundsChk();
+ BasicBlock* const failBlock = fgSetRngChkTargetInner(boundsChk->gtThrowKind, delay, &boundsChk->gtStkDepth);
+ if (failBlock != nullptr)
+ {
+ boundsChk->gtIndRngFailBB = gtNewCodeRef(failBlock);
+ }
+ }
+ else if (tree->OperIs(GT_INDEX_ADDR))
+ {
+ GenTreeIndexAddr* const indexAddr = tree->AsIndexAddr();
+ BasicBlock* const failBlock = fgSetRngChkTargetInner(SCK_RNGCHK_FAIL, delay, &indexAddr->gtStkDepth);
+ if (failBlock != nullptr)
+ {
+ indexAddr->gtIndRngFailBB = gtNewCodeRef(failBlock);
+ }
}
else
{
- noway_assert((tree->gtOper == GT_ARR_ELEM) || (tree->gtOper == GT_ARR_INDEX));
+ noway_assert(tree->OperIs(GT_ARR_ELEM, GT_ARR_INDEX));
+ fgSetRngChkTargetInner(SCK_RNGCHK_FAIL, delay, nullptr);
}
+}
-#ifdef _TARGET_X86_
- unsigned callStkDepth = fgPtrArgCntCur;
-#else
- // only x86 pushes args
- const unsigned callStkDepth = 0;
-#endif
-
+BasicBlock* Compiler::fgSetRngChkTargetInner(SpecialCodeKind kind, bool delay, unsigned* stkDepth)
+{
if (opts.MinOpts())
{
delay = false;
+#ifdef _TARGET_X86_
// we need to initialize this field
- if (fgGlobalMorph && bndsChk != nullptr)
+ if (fgGlobalMorph && (stkDepth != nullptr))
{
- bndsChk->gtStkDepth = callStkDepth;
+ *stkDepth = fgPtrArgCntCur;
}
+#endif // _TARGET_X86_
}
if (!opts.compDbgCode)
{
if (delay || compIsForInlining())
{
- /* We delay this until after loop-oriented range check
- analysis. For now we merely store the current stack
- level in the tree node.
- */
- if (bndsChk != nullptr)
+#ifdef _TARGET_X86_
+ // We delay this until after loop-oriented range check analysis. For now we merely store the current stack
+ // level in the tree node.
+ if (stkDepth != nullptr)
{
- noway_assert(!bndsChk->gtIndRngFailBB || previousCompletedPhase >= PHASE_OPTIMIZE_LOOPS);
- bndsChk->gtStkDepth = callStkDepth;
+ *stkDepth = fgPtrArgCntCur;
}
+#endif // _TARGET_X86_
}
else
{
- /* Create/find the appropriate "range-fail" label */
-
+#ifdef _TARGET_X86_
// fgPtrArgCntCur is only valid for global morph or if we walk full stmt.
- noway_assert((bndsChk != nullptr) || fgGlobalMorph);
-
- unsigned stkDepth = (bndsChk != nullptr) ? bndsChk->gtStkDepth : callStkDepth;
-
- BasicBlock* rngErrBlk = fgRngChkTarget(compCurBB, stkDepth, kind);
-
- /* Add the label to the indirection node */
+ noway_assert(fgGlobalMorph || (stkDepth != nullptr));
+ const unsigned theStkDepth = fgGlobalMorph ? fgPtrArgCntCur : *stkDepth;
+#else
+ // only x86 pushes args
+ const unsigned theStkDepth = 0;
+#endif
- if (bndsChk != nullptr)
- {
- bndsChk->gtIndRngFailBB = gtNewCodeRef(rngErrBlk);
- }
+ // Create/find the appropriate "range-fail" label
+ return fgRngChkTarget(compCurBB, theStkDepth, kind);
}
}
+
+ return nullptr;
}
/*****************************************************************************
@@ -5768,9 +5853,6 @@ GenTreePtr Compiler::fgMorphArrayIndex(GenTreePtr tree)
}
#endif // FEATURE_SIMD
- GenTreePtr arrRef = asIndex->Arr();
- GenTreePtr index = asIndex->Index();
-
// Set up the the array length's offset into lenOffs
// And the the first element's offset into elemOffs
ssize_t lenOffs;
@@ -5792,6 +5874,58 @@ GenTreePtr Compiler::fgMorphArrayIndex(GenTreePtr tree)
elemOffs = offsetof(CORINFO_Array, u1Elems);
}
+#ifndef LEGACY_BACKEND
+ // In minopts, we expand GT_INDEX to GT_IND(GT_INDEX_ADDR) in order to minimize the size of the IR. As minopts
+ // compilation time is roughly proportional to the size of the IR, this helps keep compilation times down.
+ // Furthermore, this representation typically saves on code size in minopts w.r.t. the complete expansion
+ // performed when optimizing, as it does not require LclVar nodes (which are always stack loads/stores in
+ // minopts).
+ //
+ // When we *are* optimizing, we fully expand GT_INDEX to:
+ // 1. Evaluate the array address expression and store the result in a temp if the expression is complex or
+ // side-effecting.
+ // 2. Evaluate the array index expression and store the result in a temp if the expression is complex or
+ // side-effecting.
+ // 3. Perform an explicit bounds check: GT_ARR_BOUNDS_CHK(index, GT_ARR_LENGTH(array))
+ // 4. Compute the address of the element that will be accessed:
+ // GT_ADD(GT_ADD(array, firstElementOffset), GT_MUL(index, elementSize))
+ // 5. Dereference the address with a GT_IND.
+ //
+ // This expansion explicitly exposes the bounds check and the address calculation to the optimizer, which allows
+ // for more straightforward bounds-check removal, CSE, etc.
+ if (opts.MinOpts())
+ {
+ GenTree* const array = fgMorphTree(asIndex->Arr());
+ GenTree* const index = fgMorphTree(asIndex->Index());
+
+ GenTreeIndexAddr* const indexAddr =
+ new (this, GT_INDEX_ADDR) GenTreeIndexAddr(array, index, elemTyp, elemStructType, elemSize,
+ static_cast<unsigned>(lenOffs), static_cast<unsigned>(elemOffs));
+ indexAddr->gtFlags |= (array->gtFlags | index->gtFlags) & GTF_ALL_EFFECT;
+
+ // Mark the indirection node as needing a range check if necessary.
+ if ((indexAddr->gtFlags & GTF_INX_RNGCHK) != 0)
+ {
+ fgSetRngChkTarget(indexAddr);
+ }
+
+ // Change `tree` into an indirection and return.
+ tree->ChangeOper(GT_IND);
+ GenTreeIndir* const indir = tree->AsIndir();
+ indir->Addr() = indexAddr;
+ indir->gtFlags = GTF_IND_ARR_INDEX | (indexAddr->gtFlags & GTF_ALL_EFFECT);
+
+#ifdef DEBUG
+ indexAddr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
+#endif // DEBUG
+
+ return indir;
+ }
+#endif // LEGACY_BACKEND
+
+ GenTreePtr arrRef = asIndex->Arr();
+ GenTreePtr index = asIndex->Index();
+
bool chkd = ((tree->gtFlags & GTF_INX_RNGCHK) != 0); // if false, range checking will be disabled
bool nCSE = ((tree->gtFlags & GTF_DONT_CSE) != 0);
@@ -5862,7 +5996,7 @@ GenTreePtr Compiler::fgMorphArrayIndex(GenTreePtr tree)
}
#endif // _TARGET_64BIT_
- GenTree* arrLen = new (this, GT_ARR_LENGTH) GenTreeArrLen(TYP_INT, arrRef, (int)lenOffs);
+ GenTree* arrLen = gtNewArrLen(TYP_INT, arrRef, (int)lenOffs);
if (bndsChkType != TYP_INT)
{
@@ -6378,8 +6512,6 @@ GenTreePtr Compiler::fgMorphField(GenTreePtr tree, MorphAddrContext* mac)
GenTreePtr comma = nullptr;
- bool addedExplicitNullCheck = false;
-
// NULL mac means we encounter the GT_FIELD first. This denotes a dereference of the field,
// and thus is equivalent to a MACK_Ind with zero offset.
MorphAddrContext defMAC(MACK_Ind);
@@ -6403,18 +6535,33 @@ GenTreePtr Compiler::fgMorphField(GenTreePtr tree, MorphAddrContext* mac)
#define CONSERVATIVE_NULL_CHECK_BYREF_CREATION 1
- // If the objRef is a GT_ADDR node, it, itself, never requires null checking. The expression
- // whose address is being taken is either a local or static variable, whose address is necessarily
- // non-null, or else it is a field dereference, which will do its own bounds checking if necessary.
- if (objRef->gtOper != GT_ADDR && ((mac->m_kind == MACK_Addr || mac->m_kind == MACK_Ind) &&
- (!mac->m_allConstantOffsets || fgIsBigOffset(mac->m_totalOffset + fldOffset)
+ bool addExplicitNullCheck = false;
+
+ // Implicit byref locals are never null.
+ if (!((objRef->gtOper == GT_LCL_VAR) && lvaIsImplicitByRefLocal(objRef->gtLclVarCommon.gtLclNum)))
+ {
+ // If the objRef is a GT_ADDR node, it, itself, never requires null checking. The expression
+ // whose address is being taken is either a local or static variable, whose address is necessarily
+ // non-null, or else it is a field dereference, which will do its own bounds checking if necessary.
+ if (objRef->gtOper != GT_ADDR && (mac->m_kind == MACK_Addr || mac->m_kind == MACK_Ind))
+ {
+ if (!mac->m_allConstantOffsets || fgIsBigOffset(mac->m_totalOffset + fldOffset))
+ {
+ addExplicitNullCheck = true;
+ }
+ else
+ {
#if CONSERVATIVE_NULL_CHECK_BYREF_CREATION
- || (mac->m_kind == MACK_Addr && (mac->m_totalOffset + fldOffset > 0))
+ addExplicitNullCheck = (mac->m_kind == MACK_Addr && (mac->m_totalOffset + fldOffset > 0));
#else
- || (objRef->gtType == TYP_BYREF && mac->m_kind == MACK_Addr &&
- (mac->m_totalOffset + fldOffset > 0))
+ addExplicitNullCheck = (objRef->gtType == TYP_BYREF && mac->m_kind == MACK_Addr &&
+ (mac->m_totalOffset + fldOffset > 0));
#endif
- )))
+ }
+ }
+ }
+
+ if (addExplicitNullCheck)
{
#ifdef DEBUG
if (verbose)
@@ -6472,8 +6619,6 @@ GenTreePtr Compiler::fgMorphField(GenTreePtr tree, MorphAddrContext* mac)
}
addr = gtNewLclvNode(lclNum, objRefType); // Use "tmpLcl" to create "addr" node.
-
- addedExplicitNullCheck = true;
}
else if (fldOffset == 0)
{
@@ -6517,13 +6662,10 @@ GenTreePtr Compiler::fgMorphField(GenTreePtr tree, MorphAddrContext* mac)
tree->SetOper(GT_IND);
tree->gtOp.gtOp1 = addr;
- if (fgAddrCouldBeNull(addr))
- {
- // This indirection can cause a GPF if the address could be null.
- tree->gtFlags |= GTF_EXCEPT;
- }
+ tree->gtFlags &= (~GTF_EXCEPT | addr->gtFlags);
+ tree->SetIndirExceptionFlags(this);
- if (addedExplicitNullCheck)
+ if (addExplicitNullCheck)
{
//
// Create "comma2" node and link it to "tree".
@@ -6538,7 +6680,7 @@ GenTreePtr Compiler::fgMorphField(GenTreePtr tree, MorphAddrContext* mac)
#ifdef DEBUG
if (verbose)
{
- if (addedExplicitNullCheck)
+ if (addExplicitNullCheck)
{
printf("After adding explicit null check:\n");
gtDispTree(tree);
@@ -6920,15 +7062,127 @@ void Compiler::fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result)
#endif
}
-/*****************************************************************************
- *
- * Performs checks to see if this tail call can be optimized as epilog+jmp.
- */
+//------------------------------------------------------------------------
+// fgCanFastTailCall: Check to see if this tail call can be optimized as epilog+jmp.
+//
+// Arguments:
+// callee - The callee to check
+//
+// Return Value:
+// Returns true or false based on whether the callee can be fastTailCalled
+//
+// Notes:
+// This function is target specific and each target will make the fastTailCall
+// decision differently. See the notes below.
+//
+//
+// Windows Amd64:
+// A fast tail call can be made whenever the number of callee arguments
+// is larger than or equal to the number of caller arguments, or we have four
+// or fewer callee arguments. This is because, on Windows AMD64, each
+// argument uses exactly one register or one 8-byte stack slot. Thus, we only
+// need to count arguments, and not be concerned with the size of each
+// incoming or outgoing argument.
+//
+// Can fast tail call examples (amd64 Windows):
+//
+// -- Callee will have all register arguments --
+// caller(int, int, int, int)
+// callee(int, int, float, int)
+//
+// -- Callee requires stack space that is equal to the caller --
+// caller(struct, struct, struct, struct, struct, struct)
+// callee(int, int, int, int, int, int)
+//
+// -- Callee requires stack space that is less than the caller --
+// caller(struct, double, struct, float, struct, struct)
+// callee(int, int, int, int, int)
+//
+// -- Callee will have all register arguments --
+// caller(int)
+// callee(int, int, int, int)
+//
+// Cannot fast tail call examples (amd64 Windows):
+//
+// -- Callee requires stack space that is larger than the caller --
+// caller(struct, double, struct, float, struct, struct)
+// callee(int, int, int, int, int, double, double, double)
+//
+// Unix Amd64 && Arm64:
+// A fastTailCall decision can be made whenever the callee's stack space is
+// less than or equal to the caller's stack space. There are many permutations
+// of when the caller and callee have different stack sizes if there are
+// structs being passed to either the caller or callee.
+//
+// Exceptions:
+// 1) If the callee has structs which cannot be enregistered it will be
+// reported as cannot fast tail call. This is an implementation limitation
+// where the callee only is checked for non enregisterable structs. This is
+// tracked with https://github.com/dotnet/coreclr/issues/12644.
+//
+// 2) If the caller or callee has stack arguments and the callee has more
+// arguments then the caller it will be reported as cannot fast tail call.
+// This is due to a bug in LowerFastTailCall which assumes that
+// nCalleeArgs <= nCallerArgs, which is always true on Windows Amd64. This
+// is tracked with https://github.com/dotnet/coreclr/issues/12468.
+//
+// 3) If the callee has a 9 to 16 byte struct argument and the callee has
+// stack arguments, the decision will be to not fast tail call. This is
+// because before fgMorphArgs is done, the struct is unknown whether it
+// will be placed on the stack or enregistered. Therefore, the conservative
+// decision of do not fast tail call is taken. This limitations should be
+// removed if/when fgMorphArgs no longer depends on fgCanFastTailCall.
+//
+// 4) Arm64 Only, if there are HFA arguments and the callee has stack
+// arguments, the decision will be reported as cannot fast tail call.
+// This is because before fgMorphArgs is done, the struct is unknown whether it
+// will be placed on the stack or enregistered. Therefore, the conservative
+// decision of do not fast tail call is taken.
+//
+// Can fast tail call examples (amd64 Unix):
+//
+// -- Callee will have all register arguments --
+// caller(int, int, int, int)
+// callee(int, int, float, int)
+//
+// -- Callee requires stack space that is equal to the caller --
+// caller({ int, int }, { int, int }, { int }, { int }, { int }, { int }) -- 6 int register arguments, 16 byte stack
+// space
+// callee(int, int, int, int, int, int, int, int) -- 6 int register arguments, 16 byte stack space
+//
+// -- Callee requires stack space that is less than the caller --
+// caller({ int, int }, int, { int, int }, int, { int, int }, { int, int }) 6 int register arguments, 32 byte stack
+// space
+// callee(int, int, int, int, int, int, { int, int } ) // 6 int register arguments, 16 byte stack space
+//
+// -- Callee will have all register arguments --
+// caller(int)
+// callee(int, int, int, int)
+//
+// Cannot fast tail call examples (amd64 Unix):
+//
+// -- Callee requires stack space that is larger than the caller --
+// caller(float, float, float, float, float, float, float, float) -- 8 float register arguments
+// callee(int, int, int, int, int, int, int, int) -- 6 int register arguments, 16 byte stack space
+//
+// -- Callee has structs which cannot be enregistered (Implementation Limitation) --
+// caller(float, float, float, float, float, float, float, float, { double, double, double }) -- 8 float register
+// arguments, 24 byte stack space
+// callee({ double, double, double }) -- 24 bytes stack space
+//
+// -- Callee requires stack space and has a struct argument >8 bytes and <16 bytes (Implementation Limitation) --
+// caller(int, int, int, int, int, int, { double, double, double }) -- 6 int register arguments, 24 byte stack space
+// callee(int, int, int, int, int, int, { int, int }) -- 6 int registers, 16 byte stack space
+//
+// -- Caller requires stack space and nCalleeArgs > nCallerArgs (Bug) --
+// caller({ double, double, double, double, double, double }) // 48 byte stack
+// callee(int, int) -- 2 int registers
+
bool Compiler::fgCanFastTailCall(GenTreeCall* callee)
{
#if FEATURE_FASTTAILCALL
- // Reached here means that return types of caller and callee are tail call compatible.
- // In case of structs that can be returned in a register, compRetNativeType is set to the actual return type.
+ // To reach here means that the return types of the caller and callee are tail call compatible.
+ // In the case of structs that can be returned in a register, compRetNativeType is set to the actual return type.
//
// In an implicit tail call case callSig may not be available but it is guaranteed to be available
// for explicit tail call cases. The reason implicit tail case callSig may not be available is that
@@ -6945,6 +7199,42 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee)
}
#endif
+ auto reportFastTailCallDecision = [this, callee](const char* msg, size_t callerStackSize, size_t calleeStackSize) {
+#if DEBUG
+ if ((JitConfig.JitReportFastTailCallDecisions()) == 1)
+ {
+ if (callee->gtCallType != CT_INDIRECT)
+ {
+ const char* methodName;
+
+ methodName = eeGetMethodFullName(callee->gtCallMethHnd);
+
+ printf("[Fast tailcall decision]: Caller: %s\n[Fast tailcall decision]: Callee: %s -- Decision: ",
+ info.compFullName, methodName);
+ }
+ else
+ {
+ printf("[Fast tailcall decision]: Caller: %s\n[Fast tailcall decision]: Callee: IndirectCall -- "
+ "Decision: ",
+ info.compFullName);
+ }
+
+ if (callerStackSize != -1)
+ {
+ printf("%s (CallerStackSize: %d, CalleeStackSize: %d)\n\n", msg, callerStackSize, calleeStackSize);
+ }
+ else
+ {
+ printf("%s\n\n", msg);
+ }
+ }
+ else
+ {
+ JITDUMP("[Fast tailcall decision]: %s\n", msg);
+ }
+#endif // DEBUG
+ };
+
// Note on vararg methods:
// If the caller is vararg method, we don't know the number of arguments passed by caller's caller.
// But we can be sure that in-coming arg area of vararg caller would be sufficient to hold its
@@ -6952,27 +7242,31 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee)
// out-going area required for callee is bounded by caller's fixed argument space.
//
// Note that callee being a vararg method is not a problem since we can account the params being passed.
-
- // Count of caller args including implicit and hidden (i.e. thisPtr, RetBuf, GenericContext, VarargCookie)
unsigned nCallerArgs = info.compArgsCount;
+ size_t callerArgRegCount = info.compArgRegCount;
+ size_t callerFloatArgRegCount = info.compFloatArgRegCount;
+
// Count the callee args including implicit and hidden.
// Note that GenericContext and VarargCookie are added by importer while
// importing the call to gtCallArgs list along with explicit user args.
- unsigned nCalleeArgs = 0;
+ size_t calleeArgRegCount = 0;
+ size_t calleeFloatArgRegCount = 0;
+
if (callee->gtCallObjp) // thisPtr
{
- nCalleeArgs++;
+ ++calleeArgRegCount;
}
if (callee->HasRetBufArg()) // RetBuf
{
- nCalleeArgs++;
+ ++calleeArgRegCount;
// If callee has RetBuf param, caller too must have it.
// Otherwise go the slow route.
if (info.compRetBuffArg == BAD_VAR_NUM)
{
+ reportFastTailCallDecision("Callee has RetBuf but caller does not.", 0, 0);
return false;
}
}
@@ -6981,11 +7275,14 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee)
// that cannot be passed in a register. Note that we don't need to count
// non-standard and secret params passed in registers (e.g. R10, R11) since
// these won't contribute to out-going arg size.
- bool hasMultiByteArgs = false;
- for (GenTreePtr args = callee->gtCallArgs; (args != nullptr) && !hasMultiByteArgs; args = args->gtOp.gtOp2)
- {
- nCalleeArgs++;
-
+ bool hasMultiByteStackArgs = false;
+ bool hasTwoSlotSizedStruct = false;
+ bool hasHfaArg = false;
+ size_t nCalleeArgs = calleeArgRegCount; // Keep track of how many args we have.
+ size_t calleeStackSize = 0;
+ for (GenTreePtr args = callee->gtCallArgs; (args != nullptr); args = args->gtOp.gtOp2)
+ {
+ ++nCalleeArgs;
assert(args->OperIsList());
GenTreePtr argx = args->gtOp.gtOp1;
@@ -7012,24 +7309,85 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee)
{
#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
- unsigned typeSize = 0;
- hasMultiByteArgs = !VarTypeIsMultiByteAndCanEnreg(argx->TypeGet(), objClass, &typeSize, false);
+ // hasMultiByteStackArgs will determine if the struct can be passed
+ // in registers. If it cannot we will break the loop and not
+ // fastTailCall. This is an implementation limitation
+ // where the callee only is checked for non enregisterable structs.
+ // It is tracked with https://github.com/dotnet/coreclr/issues/12644.
+ unsigned typeSize = 0;
+ hasMultiByteStackArgs = hasMultiByteStackArgs ||
+ !VarTypeIsMultiByteAndCanEnreg(argx->TypeGet(), objClass, &typeSize, false);
+
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
-#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) || defined(_TARGET_ARM64_)
- // On System V/arm64 the args could be a 2 eightbyte struct that is passed in two registers.
- // Account for the second eightbyte in the nCalleeArgs.
- // https://github.com/dotnet/coreclr/issues/2666
- // TODO-CQ-Amd64-Unix/arm64: Structs of size between 9 to 16 bytes are conservatively estimated
- // as two args, since they need two registers whereas nCallerArgs is
- // counting such an arg as one. This would mean we will not be optimizing
- // certain calls though technically possible.
+ assert(objClass != nullptr);
+ eeGetSystemVAmd64PassStructInRegisterDescriptor(objClass, &structDesc);
- if (typeSize > TARGET_POINTER_SIZE)
+ if (structDesc.passedInRegisters)
{
- unsigned extraArgRegsToAdd = (typeSize / TARGET_POINTER_SIZE);
- nCalleeArgs += extraArgRegsToAdd;
+ if (structDesc.eightByteCount == 2)
+ {
+ hasTwoSlotSizedStruct = true;
+ }
+
+ for (unsigned int i = 0; i < structDesc.eightByteCount; i++)
+ {
+ if (structDesc.IsIntegralSlot(i))
+ {
+ ++calleeArgRegCount;
+ }
+ else if (structDesc.IsSseSlot(i))
+ {
+ ++calleeFloatArgRegCount;
+ }
+ else
+ {
+ assert(false && "Invalid eightbyte classification type.");
+ break;
+ }
+ }
+ }
+ else
+ {
+ calleeStackSize += roundUp(typeSize, TARGET_POINTER_SIZE);
+ }
+
+#elif defined(_TARGET_ARM64_) // ARM64
+ var_types hfaType = GetHfaType(argx);
+ bool isHfaArg = varTypeIsFloating(hfaType);
+ size_t size = 1;
+
+ if (isHfaArg)
+ {
+ hasHfaArg = true;
+
+ calleeFloatArgRegCount += GetHfaCount(argx);
+ }
+ else
+ {
+ // Structs are either passed in 1 or 2 (64-bit) slots
+ size_t roundupSize = roundUp(typeSize, TARGET_POINTER_SIZE);
+ size = roundupSize / TARGET_POINTER_SIZE;
+
+ if (size > 2)
+ {
+ size = 1;
+ }
+
+ else if (size == 2)
+ {
+ hasTwoSlotSizedStruct = true;
+ }
+
+ calleeArgRegCount += size;
}
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING || _TARGET_ARM64_
+
+#elif defined(WINDOWS_AMD64_ABI)
+
+ ++calleeArgRegCount;
+
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
#else
assert(!"Target platform ABI rules regarding passing struct type args in registers");
@@ -7038,31 +7396,142 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee)
}
else
{
- hasMultiByteArgs = true;
+ hasMultiByteStackArgs = true;
}
}
+ else
+ {
+ varTypeIsFloating(argx) ? ++calleeFloatArgRegCount : ++calleeArgRegCount;
+ }
+
+ // We can break early on multiByte cases.
+ if (hasMultiByteStackArgs)
+ {
+ break;
+ }
}
- // Go the slow route, if it has multi-byte params
- if (hasMultiByteArgs)
+ const unsigned maxRegArgs = MAX_REG_ARG;
+
+// If we reached here means that callee has only those argument types which can be passed in
+// a register and if passed on stack will occupy exactly one stack slot in out-going arg area.
+// If we are passing args on stack for the callee and it has more args passed on stack than
+// the caller, then fast tail call cannot be performed.
+//
+// Note that the GC'ness of on stack args need not match since the arg setup area is marked
+// as non-interruptible for fast tail calls.
+
+#ifdef WINDOWS_AMD64_ABI
+ assert(calleeStackSize == 0);
+ size_t calleeStackSlots = ((calleeArgRegCount + calleeFloatArgRegCount) > maxRegArgs)
+ ? (calleeArgRegCount + calleeFloatArgRegCount) - maxRegArgs
+ : 0;
+ calleeStackSize = calleeStackSlots * TARGET_POINTER_SIZE;
+ size_t callerStackSize = info.compArgStackSize;
+
+ bool hasStackArgs = false;
+
+ if (callerStackSize > 0 || calleeStackSize > 0)
+ {
+ hasStackArgs = true;
+ }
+
+ // Go the slow route, if it has multi-byte params. This is an implementation
+ // limitatio see https://github.com/dotnet/coreclr/issues/12644.
+ if (hasMultiByteStackArgs)
{
+ reportFastTailCallDecision("Will not fastTailCall hasMultiByteStackArgs", callerStackSize, calleeStackSize);
return false;
}
- // If we reached here means that callee has only those argument types which can be passed in
- // a register and if passed on stack will occupy exactly one stack slot in out-going arg area.
- // If we are passing args on stack for callee and it has more args passed on stack than
- // caller, then fast tail call cannot be performed.
+ // x64 Windows: If we have more callee registers used than MAX_REG_ARG, then
+ // make sure the callee's incoming arguments is less than the caller's
+ if (hasStackArgs && (nCalleeArgs > nCallerArgs))
+ {
+ reportFastTailCallDecision("Will not fastTailCall hasStackArgs && (nCalleeArgs > nCallerArgs)", callerStackSize,
+ calleeStackSize);
+ return false;
+ }
+
+#elif (defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI)) || defined(_TARGET_ARM64_)
+
+ // For *nix Amd64 and Arm64 check to see if all arguments for the callee
+ // and caller are passing in registers. If not, ensure that the outgoing argument stack size
+ // requirement for the callee is less than or equal to the caller's entire stack frame usage.
//
- // Note that the GC'ness of on stack args need not match since the arg setup area is marked
- // as non-interruptible for fast tail calls.
- if ((nCalleeArgs > MAX_REG_ARG) && (nCallerArgs < nCalleeArgs))
+ // Also, in the case that we have to pass arguments on the stack make sure
+ // that we are not dealing with structs that are >8 bytes.
+
+ bool hasStackArgs = false;
+ size_t maxFloatRegArgs = MAX_FLOAT_REG_ARG;
+
+ size_t calleeIntStackArgCount = calleeArgRegCount > maxRegArgs ? calleeArgRegCount - maxRegArgs : 0;
+ size_t calleeFloatStackArgCount =
+ calleeFloatArgRegCount > maxFloatRegArgs ? calleeFloatArgRegCount - maxFloatRegArgs : 0;
+
+ size_t calleeStackArgCount = calleeIntStackArgCount + calleeFloatStackArgCount;
+ size_t callerStackSize = info.compArgStackSize;
+ calleeStackSize += calleeStackArgCount * TARGET_POINTER_SIZE;
+
+ if (callerStackSize > 0 || calleeStackSize > 0)
+ {
+ hasStackArgs = true;
+ }
+
+ // Go the slow route, if it has multi-byte params. This is an implementation
+ // limitation see https://github.com/dotnet/coreclr/issues/12644.
+ if (hasMultiByteStackArgs)
{
+ reportFastTailCallDecision("Will not fastTailCall hasMultiByteStackArgs", callerStackSize, calleeStackSize);
+ return false;
+ }
+
+ // Callee has a >8 and <=16 byte struct and arguments that has to go on the stack. Do not fastTailCall.
+ if (calleeStackSize > 0 && hasTwoSlotSizedStruct)
+ {
+ reportFastTailCallDecision("Will not fastTailCall calleeStackSize > 0 && hasTwoSlotSizedStruct",
+ callerStackSize, calleeStackSize);
+ return false;
+ }
+
+ // Callee has an HFA struct and arguments that has to go on the stack. Do not fastTailCall.
+ if (calleeStackSize > 0 && hasHfaArg)
+ {
+ reportFastTailCallDecision("Will not fastTailCall calleeStackSize > 0 && hasHfaArg", callerStackSize,
+ calleeStackSize);
+ return false;
+ }
+
+ // TODO-AMD64-Unix
+ // TODO-ARM64
+ //
+ // LowerFastTailCall currently assumes nCalleeArgs <= nCallerArgs. This is
+ // not true in many cases on x64 linux, remove this pessimization when
+ // LowerFastTailCall is fixed. See https://github.com/dotnet/coreclr/issues/12468
+ // for more information.
+ if (hasStackArgs && (nCalleeArgs > nCallerArgs))
+ {
+ reportFastTailCallDecision("Will not fastTailCall hasStackArgs && (nCalleeArgs > nCallerArgs)", callerStackSize,
+ calleeStackSize);
+ return false;
+ }
+
+ if (calleeStackSize > callerStackSize)
+ {
+ reportFastTailCallDecision("Will not fastTailCall calleeStackSize > callerStackSize", callerStackSize,
+ calleeStackSize);
return false;
}
- return true;
#else
+
+ NYI("fastTailCall not supported on this Architecture.");
+
+#endif // WINDOWS_AMD64_ABI
+
+ reportFastTailCallDecision("Will fastTailCall", callerStackSize, calleeStackSize);
+ return true;
+#else // FEATURE_FASTTAILCALL
return false;
#endif
}
@@ -7165,14 +7634,30 @@ void Compiler::fgMorphTailCall(GenTreeCall* call)
unsigned vtabOffsOfIndirection;
unsigned vtabOffsAfterIndirection;
- info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection, &vtabOffsAfterIndirection);
+ bool isRelative;
+ info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection, &vtabOffsAfterIndirection,
+ &isRelative);
/* Get the appropriate vtable chunk */
if (vtabOffsOfIndirection != CORINFO_VIRTUALCALL_NO_CHUNK)
{
- add = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, gtNewIconNode(vtabOffsOfIndirection, TYP_I_IMPL));
+ add = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, gtNewIconNode(vtabOffsOfIndirection, TYP_I_IMPL));
+
+ GenTreePtr indOffTree = nullptr;
+
+ if (isRelative)
+ {
+ indOffTree = impCloneExpr(add, &add, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL,
+ nullptr DEBUGARG("virtual table call"));
+ }
+
vtbl = gtNewOperNode(GT_IND, TYP_I_IMPL, add);
+
+ if (isRelative)
+ {
+ vtbl = gtNewOperNode(GT_ADD, TYP_I_IMPL, vtbl, indOffTree);
+ }
}
/* Now the appropriate vtable slot */
@@ -7190,12 +7675,13 @@ void Compiler::fgMorphTailCall(GenTreeCall* call)
call->gtFlags |= GTF_EXCEPT;
}
- // Now inject a placeholder for the real call target that codegen
- // will generate
- GenTreePtr arg = new (this, GT_NOP) GenTreeOp(GT_NOP, TYP_I_IMPL);
+// Now inject a placeholder for the real call target that codegen will generate
#ifdef LEGACY_BACKEND
+ GenTreePtr arg = new (this, GT_NOP) GenTreeOp(GT_NOP, TYP_I_IMPL);
codeGen->genMarkTreeInReg(arg, REG_TAILCALL_ADDR);
-#endif
+#else // !LEGACY_BACKEND
+ GenTreePtr arg = gtNewIconNode(0, TYP_I_IMPL);
+#endif // !LEGACY_BACKEND
call->gtCallArgs = gtNewListNode(arg, call->gtCallArgs);
// Lastly inject the pointer for the copy routine
@@ -7204,8 +7690,12 @@ void Compiler::fgMorphTailCall(GenTreeCall* call)
arg = gtNewIconHandleNode(ssize_t(pfnCopyArgs), GTF_ICON_FTN_ADDR);
call->gtCallArgs = gtNewListNode(arg, call->gtCallArgs);
- // It is now a varargs tail call
+// It is now a varargs tail call
+#ifdef LEGACY_BACKEND
call->gtCallMoreFlags = GTF_CALL_M_VARARGS | GTF_CALL_M_TAILCALL;
+#else // !LEGACY_BACKEND
+ call->gtCallMoreFlags |= GTF_CALL_M_VARARGS | GTF_CALL_M_TAILCALL | GTF_CALL_M_TAILCALL_VIA_HELPER;
+#endif // !LEGACY_BACKEND
call->gtFlags &= ~GTF_CALL_POP_ARGS;
#elif defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
@@ -7585,33 +8075,37 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa
// If compInitMem is set, we may need to zero-initialize some locals. Normally it's done in the prolog
// but this loop can't include the prolog. Since we don't have liveness information, we insert zero-initialization
- // for all non-parameter non-temp locals. Liveness phase will remove unnecessary initializations.
+ // for all non-parameter non-temp locals as well as temp structs with GC fields.
+ // Liveness phase will remove unnecessary initializations.
if (info.compInitMem)
{
unsigned varNum;
LclVarDsc* varDsc;
- for (varNum = 0, varDsc = lvaTable; varNum < info.compLocalsCount; varNum++, varDsc++)
+ for (varNum = 0, varDsc = lvaTable; varNum < lvaCount; varNum++, varDsc++)
{
+ var_types lclType = varDsc->TypeGet();
if (!varDsc->lvIsParam)
{
- assert(!varDsc->lvIsTemp);
- var_types lclType = varDsc->TypeGet();
- GenTreePtr lcl = gtNewLclvNode(varNum, lclType);
- GenTreePtr init = nullptr;
- if (lclType == TYP_STRUCT)
- {
- const bool isVolatile = false;
- const bool isCopyBlock = false;
- init = gtNewBlkOpNode(lcl, gtNewIconNode(0), varDsc->lvSize(), isVolatile, isCopyBlock);
- init = fgMorphInitBlock(init);
- }
- else
+ if (!varDsc->lvIsTemp || ((lclType == TYP_STRUCT) && (varDsc->lvStructGcCount > 0)))
{
- GenTreePtr zero = gtNewZeroConNode(genActualType(lclType));
- init = gtNewAssignNode(lcl, zero);
+ var_types lclType = varDsc->TypeGet();
+ GenTreePtr lcl = gtNewLclvNode(varNum, lclType);
+ GenTreePtr init = nullptr;
+ if (lclType == TYP_STRUCT)
+ {
+ const bool isVolatile = false;
+ const bool isCopyBlock = false;
+ init = gtNewBlkOpNode(lcl, gtNewIconNode(0), varDsc->lvSize(), isVolatile, isCopyBlock);
+ init = fgMorphInitBlock(init);
+ }
+ else
+ {
+ GenTreePtr zero = gtNewZeroConNode(genActualType(lclType));
+ init = gtNewAssignNode(lcl, zero);
+ }
+ GenTreePtr initStmt = gtNewStmt(init, callILOffset);
+ fgInsertStmtBefore(block, last, initStmt);
}
- GenTreePtr initStmt = gtNewStmt(init, callILOffset);
- fgInsertStmtBefore(block, last, initStmt);
}
}
}
@@ -7619,9 +8113,14 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa
// Remove the call
fgRemoveStmt(block, last);
- // Set the loop edge.
+ // Set the loop edge. Ensure we have a scratch block and then target the
+ // next block. Loop detection needs to see a pred out of the loop, so
+ // mark the scratch block BBF_DONT_REMOVE to prevent empty block removal
+ // on it.
+ fgEnsureFirstBBisScratch();
+ fgFirstBB->bbFlags |= BBF_DONT_REMOVE;
block->bbJumpKind = BBJ_ALWAYS;
- block->bbJumpDest = fgFirstBBisScratch() ? fgFirstBB->bbNext : fgFirstBB;
+ block->bbJumpDest = fgFirstBB->bbNext;
fgAddRefPred(block->bbJumpDest, block);
block->bbFlags &= ~BBF_HAS_JMP;
}
@@ -7998,7 +8497,8 @@ GenTreePtr Compiler::fgMorphCall(GenTreeCall* call)
// a recursive call into a loop. Another option is to modify gtIsRecursiveCall() to check that the
// generic type parameters of both caller and callee generic method are the same.
if (opts.compTailCallLoopOpt && canFastTailCall && gtIsRecursiveCall(call) && !lvaReportParamTypeArg() &&
- !lvaKeepAliveAndReportThis() && !call->IsVirtual() && !hasStructParam && !varTypeIsStruct(call->TypeGet()))
+ !lvaKeepAliveAndReportThis() && !call->IsVirtual() && !hasStructParam &&
+ !varTypeIsStruct(call->TypeGet()) && ((info.compClassAttr & CORINFO_FLG_MARSHAL_BYREF) == 0))
{
call->gtCallMoreFlags |= GTF_CALL_M_TAILCALL_TO_LOOP;
fastTailCallToLoop = true;
@@ -8344,12 +8844,30 @@ NO_TAIL_CALL:
compCurBB->bbFlags |= BBF_GC_SAFE_POINT;
}
- // Morph Type.op_Equality and Type.op_Inequality
- // We need to do this before the arguments are morphed
+ // Morph Type.op_Equality, Type.op_Inequality, and Enum.HasFlag
+ //
+ // We need to do these before the arguments are morphed
if ((call->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC))
{
CorInfoIntrinsics methodID = info.compCompHnd->getIntrinsicID(call->gtCallMethHnd);
+ if (methodID == CORINFO_INTRINSIC_Illegal)
+ {
+ // Check for a new-style jit intrinsic.
+ const NamedIntrinsic ni = lookupNamedIntrinsic(call->gtCallMethHnd);
+
+ if (ni == NI_Enum_HasFlag)
+ {
+ GenTree* thisOp = call->gtCallObjp;
+ GenTree* flagOp = call->gtCallArgs->gtOp.gtOp1;
+ GenTree* optTree = gtOptimizeEnumHasFlag(thisOp, flagOp);
+ if (optTree != nullptr)
+ {
+ return fgMorphTree(optTree);
+ }
+ }
+ }
+
genTreeOps simpleOp = GT_CALL;
if (methodID == CORINFO_INTRINSIC_TypeEQ)
{
@@ -8376,6 +8894,9 @@ NO_TAIL_CALL:
if (gtCanOptimizeTypeEquality(op1) || gtCanOptimizeTypeEquality(op2))
{
+ JITDUMP("Optimizing call to Type:op_%s to simple compare via %s\n",
+ methodID == CORINFO_INTRINSIC_TypeEQ ? "Equality" : "Inequality", GenTree::OpName(simpleOp));
+
GenTreePtr compare = gtNewOperNode(simpleOp, TYP_INT, op1, op2);
// fgMorphSmpOp will further optimize the following patterns:
@@ -8518,7 +9039,7 @@ NO_TAIL_CALL:
CORINFO_INTRINSIC_GetCurrentManagedThread)
{
// substitute expression with call to helper
- GenTreePtr newCall = gtNewHelperCallNode(CORINFO_HELP_GETCURRENTMANAGEDTHREADID, TYP_INT, 0);
+ GenTreePtr newCall = gtNewHelperCallNode(CORINFO_HELP_GETCURRENTMANAGEDTHREADID, TYP_INT);
JITDUMP("get_ManagedThreadId(get_CurrentThread) folding performed\n");
return fgMorphTree(newCall);
}
@@ -8619,7 +9140,7 @@ GenTreePtr Compiler::fgMorphConst(GenTreePtr tree)
gtNewIconEmbScpHndNode(tree->gtStrCon.gtScpHnd));
}
- tree = gtNewHelperCallNode(helper, TYP_REF, 0, args);
+ tree = gtNewHelperCallNode(helper, TYP_REF, args);
return fgMorphTree(tree);
}
}
@@ -8933,7 +9454,7 @@ GenTreePtr Compiler::fgMorphOneAsgBlockOp(GenTreePtr tree)
if (dest == lclVarTree)
{
- dest = gtNewOperNode(GT_IND, asgType, gtNewOperNode(GT_ADDR, TYP_BYREF, dest));
+ dest = gtNewIndir(asgType, gtNewOperNode(GT_ADDR, TYP_BYREF, dest));
}
}
}
@@ -8959,9 +9480,13 @@ GenTreePtr Compiler::fgMorphOneAsgBlockOp(GenTreePtr tree)
if (!fgIsIndirOfAddrOfLocal(dest))
{
- dest->gtFlags |= (GTF_EXCEPT | GTF_GLOB_REF | GTF_IND_TGTANYWHERE);
- tree->gtFlags |= (GTF_EXCEPT | GTF_GLOB_REF | GTF_IND_TGTANYWHERE);
+ dest->gtFlags |= (GTF_GLOB_REF | GTF_IND_TGTANYWHERE);
+ tree->gtFlags |= GTF_GLOB_REF;
}
+
+ dest->gtFlags &= (~GTF_EXCEPT | dest->AsIndir()->Addr()->gtFlags);
+ dest->SetIndirExceptionFlags(this);
+ tree->gtFlags |= (dest->gtFlags & GTF_EXCEPT);
}
LclVarDsc* srcVarDsc = nullptr;
@@ -9018,14 +9543,20 @@ GenTreePtr Compiler::fgMorphOneAsgBlockOp(GenTreePtr tree)
}
}
}
- // If we have no information about the src, we have to assume it could
- // live anywhere (not just in the GC heap).
- // Mark the GT_IND node so that we use the correct write barrier helper in case
- // the field is a GC ref.
- if (!fgIsIndirOfAddrOfLocal(src))
+ if (src->OperIsIndir())
{
- src->gtFlags |= (GTF_EXCEPT | GTF_GLOB_REF | GTF_IND_TGTANYWHERE);
+ if (!fgIsIndirOfAddrOfLocal(src))
+ {
+ // If we have no information about the src, we have to assume it could
+ // live anywhere (not just in the GC heap).
+ // Mark the GT_IND node so that we use the correct write barrier helper in case
+ // the field is a GC ref.
+ src->gtFlags |= (GTF_GLOB_REF | GTF_IND_TGTANYWHERE);
+ }
+
+ src->gtFlags &= (~GTF_EXCEPT | src->AsIndir()->Addr()->gtFlags);
+ src->SetIndirExceptionFlags(this);
}
}
else
@@ -9080,6 +9611,7 @@ GenTreePtr Compiler::fgMorphOneAsgBlockOp(GenTreePtr tree)
asg->ChangeType(asgType);
dest->gtFlags |= GTF_DONT_CSE;
+ asg->gtFlags &= ~GTF_EXCEPT;
asg->gtFlags |= ((dest->gtFlags | src->gtFlags) & GTF_ALL_EFFECT);
// Un-set GTF_REVERSE_OPS, and it will be set later if appropriate.
asg->gtFlags &= ~GTF_REVERSE_OPS;
@@ -9456,6 +9988,9 @@ GenTreePtr Compiler::fgMorphGetStructAddr(GenTreePtr* pTree, CORINFO_CLASS_HANDL
case GT_ARR_ELEM:
addr = gtNewOperNode(GT_ADDR, TYP_BYREF, tree);
break;
+ case GT_INDEX_ADDR:
+ addr = tree;
+ break;
default:
{
// TODO: Consider using lvaGrabTemp and gtNewTempAssign instead, since we're
@@ -9483,10 +10018,12 @@ GenTreePtr Compiler::fgMorphGetStructAddr(GenTreePtr* pTree, CORINFO_CLASS_HANDL
GenTree* Compiler::fgMorphBlkNode(GenTreePtr tree, bool isDest)
{
- if (tree->gtOper == GT_COMMA)
+ GenTree* handleTree = nullptr;
+ GenTree* addr = nullptr;
+ if (tree->OperIs(GT_COMMA))
{
GenTree* effectiveVal = tree->gtEffectiveVal();
- GenTree* addr = gtNewOperNode(GT_ADDR, TYP_BYREF, effectiveVal);
+ addr = gtNewOperNode(GT_ADDR, TYP_BYREF, effectiveVal);
#ifdef DEBUG
addr->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
#endif
@@ -9509,13 +10046,24 @@ GenTree* Compiler::fgMorphBlkNode(GenTreePtr tree, bool isDest)
lastComma->gtOp.gtOp2 = addr;
addr = tree;
}
- var_types structType = effectiveVal->TypeGet();
+
+ handleTree = effectiveVal;
+ }
+ else if (tree->OperIs(GT_IND) && tree->AsIndir()->Addr()->OperIs(GT_INDEX_ADDR))
+ {
+ handleTree = tree;
+ addr = tree->AsIndir()->Addr();
+ }
+
+ if (addr != nullptr)
+ {
+ var_types structType = handleTree->TypeGet();
if (structType == TYP_STRUCT)
{
- CORINFO_CLASS_HANDLE structHnd = gtGetStructHandleIfPresent(effectiveVal);
+ CORINFO_CLASS_HANDLE structHnd = gtGetStructHandleIfPresent(handleTree);
if (structHnd == NO_CLASS_HANDLE)
{
- tree = gtNewOperNode(GT_IND, effectiveVal->TypeGet(), addr);
+ tree = gtNewOperNode(GT_IND, structType, addr);
}
else
{
@@ -9620,7 +10168,7 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne
else if (effectiveVal->TypeGet() != asgType)
{
GenTree* addr = gtNewOperNode(GT_ADDR, TYP_BYREF, effectiveVal);
- effectiveVal = gtNewOperNode(GT_IND, asgType, addr);
+ effectiveVal = gtNewIndir(asgType, addr);
}
}
else
@@ -9715,7 +10263,7 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne
}
else
{
- newTree = new (this, GT_IND) GenTreeIndir(GT_IND, asgType, addr, nullptr);
+ newTree = gtNewIndir(asgType, addr);
}
effectiveVal = newTree;
}
@@ -9779,7 +10327,7 @@ void Compiler::fgMorphUnsafeBlk(GenTreeObj* dest)
// When performing a field by field assignment we can have one of Source() or Dest treated as a blob of bytes
// and in such cases we will call lvaSetVarDoNotEnregister() on the one treated as a blob of bytes.
// if the Source() or Dest() is a a struct that has a "CustomLayout" and "ConstainsHoles" then we
-// can not use a field by field assignment and must the orginal block copy unmodified.
+// can not use a field by field assignment and must leave the orginal block copy unmodified.
GenTreePtr Compiler::fgMorphCopyBlock(GenTreePtr tree)
{
@@ -10220,7 +10768,7 @@ GenTreePtr Compiler::fgMorphCopyBlock(GenTreePtr tree)
{
noway_assert(rhs->gtOper == GT_LCL_VAR);
GenTree* rhsAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, rhs);
- rhs = gtNewOperNode(GT_IND, TYP_STRUCT, rhsAddr);
+ rhs = gtNewIndir(TYP_STRUCT, rhsAddr);
}
#endif // LEGACY_BACKEND
// Formerly, liveness did not consider copyblk arguments of simple types as being
@@ -10464,7 +11012,7 @@ GenTreePtr Compiler::fgMorphCopyBlock(GenTreePtr tree)
dest = gtNewOperNode(GT_ADD, TYP_BYREF, dest, fieldOffsetNode);
- dest = gtNewOperNode(GT_IND, lvaTable[fieldLclNum].TypeGet(), dest);
+ dest = gtNewIndir(lvaTable[fieldLclNum].TypeGet(), dest);
// !!! The destination could be on stack. !!!
// This flag will let us choose the correct write barrier.
@@ -10520,7 +11068,7 @@ GenTreePtr Compiler::fgMorphCopyBlock(GenTreePtr tree)
new (this, GT_CNS_INT)
GenTreeIntCon(TYP_I_IMPL, lvaTable[fieldLclNum].lvFldOffset, curFieldSeq));
- src = gtNewOperNode(GT_IND, lvaTable[fieldLclNum].TypeGet(), src);
+ src = gtNewIndir(lvaTable[fieldLclNum].TypeGet(), src);
}
}
@@ -11347,20 +11895,43 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac)
}
#ifdef _TARGET_ARM64_
-
// For ARM64 we don't have a remainder instruction,
// The architecture manual suggests the following transformation to
// generate code for such operator:
//
// a % b = a - (a / b) * b;
//
- // NOTE: we should never need to perform this transformation when remorphing, since global morphing
- // should already have done so and we do not introduce new modulus nodes in later phases.
- assert(!optValnumCSE_phase);
- tree = fgMorphModToSubMulDiv(tree->AsOp());
- op1 = tree->gtOp.gtOp1;
- op2 = tree->gtOp.gtOp2;
-#else //_TARGET_ARM64_
+ // We will use the suggested transform except in the special case
+ // when the modulo operation is unsigned and the divisor is a
+ // integer constant power of two. In this case, we will rely on lower
+ // to make the transform:
+ //
+ // a % b = a & (b - 1);
+ //
+ // Note: We must always perform one or the other of these transforms.
+ // Therefore we must also detect the special cases where lower does not do the
+ // % to & transform. In our case there is only currently one extra condition:
+ //
+ // * Dividend must not be constant. Lower disables this rare const % const case
+ //
+ {
+ // Do "a % b = a - (a / b) * b" morph if ...........................
+ bool doMorphModToSubMulDiv =
+ (tree->OperGet() == GT_MOD) || // Modulo operation is signed
+ !op2->IsIntegralConst() || // Divisor is not an integer constant
+ !isPow2(op2->AsIntCon()->IconValue()) || // Divisor is not a power of two
+ op1->IsCnsIntOrI(); // Dividend is constant
+
+ if (doMorphModToSubMulDiv)
+ {
+ assert(!optValnumCSE_phase);
+
+ tree = fgMorphModToSubMulDiv(tree->AsOp());
+ op1 = tree->gtOp.gtOp1;
+ op2 = tree->gtOp.gtOp2;
+ }
+ }
+#else // !_TARGET_ARM64_
// If b is not a power of 2 constant then lowering replaces a % b
// with a - (a / b) * b and applies magic division optimization to
// a / b. The code may already contain an a / b expression (e.g.
@@ -11482,6 +12053,8 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac)
if (bOp1ClassFromHandle && bOp2ClassFromHandle)
{
+ JITDUMP("Optimizing compare of types-from-handles to instead compare handles\n");
+
GenTreePtr classFromHandleArg1 = tree->gtOp.gtOp1->gtCall.gtCallArgs->gtOp.gtOp1;
GenTreePtr classFromHandleArg2 = tree->gtOp.gtOp2->gtCall.gtCallArgs->gtOp.gtOp1;
@@ -11539,20 +12112,23 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac)
if (info.compCompHnd->canInlineTypeCheckWithObjectVTable(clsHnd))
{
- // Method Table tree
- CLANG_FORMAT_COMMENT_ANCHOR;
+ // Fetch object method table from the object itself
+ JITDUMP("Optimizing compare of obj.GetType()"
+ " and type-from-handle to compare handles\n");
+
+ // Method table constant
+ GenTree* cnsMT = pGetClassFromHandleArgument;
#ifdef LEGACY_BACKEND
- GenTreePtr objMT = gtNewOperNode(GT_IND, TYP_I_IMPL, pGetType->gtCall.gtCallObjp);
+ // Method table from object
+ GenTree* objMT = gtNewOperNode(GT_IND, TYP_I_IMPL, pGetType->gtCall.gtCallObjp);
#else
- GenTreePtr objMT = gtNewOperNode(GT_IND, TYP_I_IMPL, pGetType->gtUnOp.gtOp1);
+ // Method table from object
+ GenTree* objMT = gtNewOperNode(GT_IND, TYP_I_IMPL, pGetType->gtUnOp.gtOp1);
#endif
objMT->gtFlags |= GTF_EXCEPT; // Null ref exception if object is null
compCurBB->bbFlags |= BBF_HAS_VTABREF;
optMethodFlags |= OMF_HAS_VTABLEREF;
- // Method table constant
- GenTreePtr cnsMT = pGetClassFromHandleArgument;
-
GenTreePtr compare = gtNewOperNode(oper, TYP_INT, objMT, cnsMT);
compare->gtFlags |=
@@ -11605,16 +12181,6 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac)
tree = fgMorphToEmulatedFP(tree);
#endif
- /* Could this operator throw an exception? */
- if (fgGlobalMorph && tree->OperMayThrow())
- {
- if (((tree->OperGet() != GT_IND) && !tree->OperIsBlk()) || fgAddrCouldBeNull(tree->gtOp.gtOp1))
- {
- /* Mark the tree node as potentially throwing an exception */
- tree->gtFlags |= GTF_EXCEPT;
- }
- }
-
/*-------------------------------------------------------------------------
* Process the first operand, if any
*/
@@ -11757,11 +12323,6 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac)
tree->gtFlags &= ~GTF_CALL;
}
- if (!tree->OperMayThrow())
- {
- tree->gtFlags &= ~GTF_EXCEPT;
- }
-
/* Propagate the new flags */
tree->gtFlags |= (op1->gtFlags & GTF_ALL_EFFECT);
@@ -11914,6 +12475,36 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac)
DONE_MORPHING_CHILDREN:
+ if (tree->OperMayThrow(this))
+ {
+ // Mark the tree node as potentially throwing an exception
+ tree->gtFlags |= GTF_EXCEPT;
+ }
+ else
+ {
+ if (tree->OperIsIndirOrArrLength())
+ {
+ tree->gtFlags |= GTF_IND_NONFAULTING;
+ }
+ if (((op1 == nullptr) || ((op1->gtFlags & GTF_EXCEPT) == 0)) &&
+ ((op2 == nullptr) || ((op2->gtFlags & GTF_EXCEPT) == 0)))
+ {
+ tree->gtFlags &= ~GTF_EXCEPT;
+ }
+ }
+
+ if (tree->OperRequiresAsgFlag())
+ {
+ tree->gtFlags |= GTF_ASG;
+ }
+ else
+ {
+ if (((op1 == nullptr) || ((op1->gtFlags & GTF_ASG) == 0)) &&
+ ((op2 == nullptr) || ((op2->gtFlags & GTF_ASG) == 0)))
+ {
+ tree->gtFlags &= ~GTF_ASG;
+ }
+ }
/*-------------------------------------------------------------------------
* Now do POST-ORDER processing
*/
@@ -13398,11 +13989,14 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac)
#endif
while (commaNode->gtOp.gtOp2->gtOper == GT_COMMA)
{
- commaNode = commaNode->gtOp.gtOp2;
- commaNode->gtType = typ;
- commaNode->gtFlags = (treeFlags & ~GTF_REVERSE_OPS); // Bashing the GT_COMMA flags here is
- // dangerous, clear the GTF_REVERSE_OPS at
- // least.
+ commaNode = commaNode->gtOp.gtOp2;
+ commaNode->gtType = typ;
+ commaNode->gtFlags =
+ (treeFlags & ~GTF_REVERSE_OPS & ~GTF_ASG); // Bashing the GT_COMMA flags here is
+ // dangerous, clear the GTF_REVERSE_OPS at
+ // least.
+ commaNode->gtFlags |=
+ ((commaNode->gtOp.gtOp1->gtFlags & GTF_ASG) | (commaNode->gtOp.gtOp2->gtFlags & GTF_ASG));
#ifdef DEBUG
commaNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
#endif
@@ -13415,9 +14009,13 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac)
assert(b);
GetArrayInfoMap()->Remove(tree);
}
- tree = op1;
- op1 = gtNewOperNode(GT_IND, typ, commaNode->gtOp.gtOp2);
- op1->gtFlags = treeFlags;
+ tree = op1;
+ GenTree* addr = commaNode->gtOp.gtOp2;
+ op1 = gtNewIndir(typ, addr);
+ // This is very conservative
+ op1->gtFlags |= treeFlags & ~GTF_ALL_EFFECT & ~GTF_IND_NONFAULTING;
+ op1->gtFlags |= (addr->gtFlags & GTF_ALL_EFFECT);
+
if (wasArrIndex)
{
GetArrayInfoMap()->Set(op1, arrInfo);
@@ -13426,6 +14024,7 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac)
op1->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
#endif
commaNode->gtOp.gtOp2 = op1;
+ commaNode->gtFlags |= (op1->gtFlags & GTF_ALL_EFFECT);
return tree;
}
@@ -13514,6 +14113,8 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac)
if (commaOp2->gtOper == GT_IND)
{
commaOp2->gtFlags |= GTF_IND_NONFAULTING;
+ commaOp2->gtFlags &= ~GTF_EXCEPT;
+ commaOp2->gtFlags |= (commaOp2->gtOp.gtOp1->gtFlags & GTF_EXCEPT);
}
op1 = gtNewOperNode(GT_ADDR, TYP_BYREF, commaOp2);
@@ -13541,6 +14142,12 @@ GenTreePtr Compiler::fgMorphSmpOp(GenTreePtr tree, MorphAddrContext* mac)
commaNode = commaNode->gtOp.gtOp2;
}
+ tree->gtFlags &= ~GTF_EXCEPT;
+
+ // Propagate the new flags
+ tree->gtFlags |= (tree->gtOp.gtOp1->gtFlags & GTF_EXCEPT);
+ tree->gtFlags |= (tree->gtOp.gtOp2->gtFlags & GTF_EXCEPT);
+
return tree;
}
@@ -15093,6 +15700,14 @@ GenTreePtr Compiler::fgMorphTree(GenTreePtr tree, MorphAddrContext* mac)
break;
case GT_CALL:
+ if (tree->OperMayThrow(this))
+ {
+ tree->gtFlags |= GTF_EXCEPT;
+ }
+ else
+ {
+ tree->gtFlags &= ~GTF_EXCEPT;
+ }
tree = fgMorphCall(tree->AsCall());
break;
@@ -15122,14 +15737,20 @@ GenTreePtr Compiler::fgMorphTree(GenTreePtr tree, MorphAddrContext* mac)
case GT_ARR_ELEM:
tree->gtArrElem.gtArrObj = fgMorphTree(tree->gtArrElem.gtArrObj);
- tree->gtFlags |= tree->gtArrElem.gtArrObj->gtFlags & GTF_ALL_EFFECT;
unsigned dim;
for (dim = 0; dim < tree->gtArrElem.gtArrRank; dim++)
{
tree->gtArrElem.gtArrInds[dim] = fgMorphTree(tree->gtArrElem.gtArrInds[dim]);
+ }
+
+ tree->gtFlags |= tree->gtArrElem.gtArrObj->gtFlags & GTF_ALL_EFFECT;
+
+ for (dim = 0; dim < tree->gtArrElem.gtArrRank; dim++)
+ {
tree->gtFlags |= tree->gtArrElem.gtArrInds[dim]->gtFlags & GTF_ALL_EFFECT;
}
+
if (fgGlobalMorph)
{
fgSetRngChkTarget(tree, false);
@@ -15138,10 +15759,11 @@ GenTreePtr Compiler::fgMorphTree(GenTreePtr tree, MorphAddrContext* mac)
case GT_ARR_OFFSET:
tree->gtArrOffs.gtOffset = fgMorphTree(tree->gtArrOffs.gtOffset);
+ tree->gtArrOffs.gtIndex = fgMorphTree(tree->gtArrOffs.gtIndex);
+ tree->gtArrOffs.gtArrObj = fgMorphTree(tree->gtArrOffs.gtArrObj);
+
tree->gtFlags |= tree->gtArrOffs.gtOffset->gtFlags & GTF_ALL_EFFECT;
- tree->gtArrOffs.gtIndex = fgMorphTree(tree->gtArrOffs.gtIndex);
tree->gtFlags |= tree->gtArrOffs.gtIndex->gtFlags & GTF_ALL_EFFECT;
- tree->gtArrOffs.gtArrObj = fgMorphTree(tree->gtArrOffs.gtArrObj);
tree->gtFlags |= tree->gtArrOffs.gtArrObj->gtFlags & GTF_ALL_EFFECT;
if (fgGlobalMorph)
{
@@ -15153,14 +15775,37 @@ GenTreePtr Compiler::fgMorphTree(GenTreePtr tree, MorphAddrContext* mac)
tree->gtCmpXchg.gtOpLocation = fgMorphTree(tree->gtCmpXchg.gtOpLocation);
tree->gtCmpXchg.gtOpValue = fgMorphTree(tree->gtCmpXchg.gtOpValue);
tree->gtCmpXchg.gtOpComparand = fgMorphTree(tree->gtCmpXchg.gtOpComparand);
+
+ tree->gtFlags &= ~GTF_EXCEPT;
+
+ tree->gtFlags |= tree->gtCmpXchg.gtOpLocation->gtFlags & GTF_ALL_EFFECT;
+ tree->gtFlags |= tree->gtCmpXchg.gtOpValue->gtFlags & GTF_ALL_EFFECT;
+ tree->gtFlags |= tree->gtCmpXchg.gtOpComparand->gtFlags & GTF_ALL_EFFECT;
break;
case GT_STORE_DYN_BLK:
- tree->gtDynBlk.Data() = fgMorphTree(tree->gtDynBlk.Data());
- __fallthrough;
case GT_DYN_BLK:
+ if (tree->OperGet() == GT_STORE_DYN_BLK)
+ {
+ tree->gtDynBlk.Data() = fgMorphTree(tree->gtDynBlk.Data());
+ }
tree->gtDynBlk.Addr() = fgMorphTree(tree->gtDynBlk.Addr());
tree->gtDynBlk.gtDynamicSize = fgMorphTree(tree->gtDynBlk.gtDynamicSize);
+
+ tree->gtFlags &= ~GTF_EXCEPT;
+ tree->SetIndirExceptionFlags(this);
+
+ if (tree->OperGet() == GT_STORE_DYN_BLK)
+ {
+ tree->gtFlags |= tree->gtDynBlk.Data()->gtFlags & GTF_ALL_EFFECT;
+ }
+ tree->gtFlags |= tree->gtDynBlk.Addr()->gtFlags & GTF_ALL_EFFECT;
+ tree->gtFlags |= tree->gtDynBlk.gtDynamicSize->gtFlags & GTF_ALL_EFFECT;
+ break;
+
+ case GT_INDEX_ADDR:
+ tree->AsIndexAddr()->Index() = fgMorphTree(tree->AsIndexAddr()->Index());
+ tree->AsIndexAddr()->Arr() = fgMorphTree(tree->AsIndexAddr()->Arr());
break;
default:
@@ -15842,8 +16487,6 @@ void Compiler::fgMorphStmts(BasicBlock* block, bool* mult, bool* lnot, bool* loa
{
fgRemoveRestOfBlock = false;
- noway_assert(fgExpandInline == false);
-
/* Make the current basic block address available globally */
compCurBB = block;
@@ -16068,8 +16711,6 @@ void Compiler::fgMorphStmts(BasicBlock* block, bool* mult, bool* lnot, bool* loa
fgConvertBBToThrowBB(block);
}
- noway_assert(fgExpandInline == false);
-
#if FEATURE_FASTTAILCALL
GenTreePtr recursiveTailCall = nullptr;
if (block->endsWithTailCallConvertibleToLoop(this, &recursiveTailCall))
@@ -16347,21 +16988,6 @@ void Compiler::fgMorphBlocks()
{
if ((genReturnBB != nullptr) && (genReturnBB != block) && ((block->bbFlags & BBF_HAS_JMP) == 0))
{
- /* We'll jump to the genReturnBB */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#if !defined(_TARGET_X86_)
- if (info.compFlags & CORINFO_FLG_SYNCH)
- {
- fgConvertSyncReturnToLeave(block);
- }
- else
-#endif // !_TARGET_X86_
- {
- block->bbJumpKind = BBJ_ALWAYS;
- block->bbJumpDest = genReturnBB;
- fgReturnCount--;
- }
// Note 1: A block is not guaranteed to have a last stmt if its jump kind is BBJ_RETURN.
// For example a method returning void could have an empty block with jump kind BBJ_RETURN.
@@ -16377,54 +17003,76 @@ void Compiler::fgMorphBlocks()
GenTreePtr last = (block->bbTreeList != nullptr) ? block->bbTreeList->gtPrev : nullptr;
GenTreePtr ret = (last != nullptr) ? last->gtStmt.gtStmtExpr : nullptr;
- // replace the GT_RETURN node to be a GT_ASG that stores the return value into genReturnLocal.
- if (genReturnLocal != BAD_VAR_NUM)
+ if ((ret != nullptr) && ((ret->gtFlags & GTF_RET_MERGED) != 0))
{
- // Method must be returning a value other than TYP_VOID.
- noway_assert(compMethodHasRetVal());
+ // This return was generated during epilog merging, so leave it alone
+ }
+ else
+ {
+ /* We'll jump to the genReturnBB */
+ CLANG_FORMAT_COMMENT_ANCHOR;
+
+#if !defined(_TARGET_X86_)
+ if (info.compFlags & CORINFO_FLG_SYNCH)
+ {
+ fgConvertSyncReturnToLeave(block);
+ }
+ else
+#endif // !_TARGET_X86_
+ {
+ block->bbJumpKind = BBJ_ALWAYS;
+ block->bbJumpDest = genReturnBB;
+ fgReturnCount--;
+ }
+ if (genReturnLocal != BAD_VAR_NUM)
+ {
+ // replace the GT_RETURN node to be a GT_ASG that stores the return value into genReturnLocal.
- // This block must be ending with a GT_RETURN
- noway_assert(last != nullptr);
- noway_assert(last->gtOper == GT_STMT);
- noway_assert(last->gtNext == nullptr);
- noway_assert(ret != nullptr);
+ // Method must be returning a value other than TYP_VOID.
+ noway_assert(compMethodHasRetVal());
- // GT_RETURN must have non-null operand as the method is returning the value assigned to
- // genReturnLocal
- noway_assert(ret->OperGet() == GT_RETURN);
- noway_assert(ret->gtGetOp1() != nullptr);
+ // This block must be ending with a GT_RETURN
+ noway_assert(last != nullptr);
+ noway_assert(last->gtOper == GT_STMT);
+ noway_assert(last->gtNext == nullptr);
+ noway_assert(ret != nullptr);
- GenTreePtr tree = gtNewTempAssign(genReturnLocal, ret->gtGetOp1());
+ // GT_RETURN must have non-null operand as the method is returning the value assigned to
+ // genReturnLocal
+ noway_assert(ret->OperGet() == GT_RETURN);
+ noway_assert(ret->gtGetOp1() != nullptr);
- last->gtStmt.gtStmtExpr = (tree->OperIsCopyBlkOp()) ? fgMorphCopyBlock(tree) : tree;
+ GenTreePtr tree = gtNewTempAssign(genReturnLocal, ret->gtGetOp1());
- // make sure that copy-prop ignores this assignment.
- last->gtStmt.gtStmtExpr->gtFlags |= GTF_DONT_CSE;
- }
- else if (ret != nullptr && ret->OperGet() == GT_RETURN)
- {
- // This block ends with a GT_RETURN
- noway_assert(last != nullptr);
- noway_assert(last->gtOper == GT_STMT);
- noway_assert(last->gtNext == nullptr);
+ last->gtStmt.gtStmtExpr = (tree->OperIsCopyBlkOp()) ? fgMorphCopyBlock(tree) : tree;
- // Must be a void GT_RETURN with null operand; delete it as this block branches to oneReturn block
- noway_assert(ret->TypeGet() == TYP_VOID);
- noway_assert(ret->gtGetOp1() == nullptr);
+ // make sure that copy-prop ignores this assignment.
+ last->gtStmt.gtStmtExpr->gtFlags |= GTF_DONT_CSE;
+ }
+ else if (ret != nullptr && ret->OperGet() == GT_RETURN)
+ {
+ // This block ends with a GT_RETURN
+ noway_assert(last != nullptr);
+ noway_assert(last->gtOper == GT_STMT);
+ noway_assert(last->gtNext == nullptr);
- fgRemoveStmt(block, last);
- }
+ // Must be a void GT_RETURN with null operand; delete it as this block branches to oneReturn
+ // block
+ noway_assert(ret->TypeGet() == TYP_VOID);
+ noway_assert(ret->gtGetOp1() == nullptr);
+ fgRemoveStmt(block, last);
+ }
#ifdef DEBUG
- if (verbose)
- {
- printf("morph BB%02u to point at onereturn. New block is\n", block->bbNum);
- fgTableDispBasicBlock(block);
- }
+ if (verbose)
+ {
+ printf("morph BB%02u to point at onereturn. New block is\n", block->bbNum);
+ fgTableDispBasicBlock(block);
+ }
#endif
+ }
}
}
-
block = block->bbNext;
} while (block);
@@ -16669,20 +17317,19 @@ GenTreePtr Compiler::fgInitThisClass()
vtTree->gtFlags |= GTF_EXCEPT; // Null-pointer exception
GenTreePtr methodHnd = gtNewIconEmbMethHndNode(info.compMethodHnd);
- return gtNewHelperCallNode(CORINFO_HELP_INITINSTCLASS, TYP_VOID, 0,
- gtNewArgList(vtTree, methodHnd));
+ return gtNewHelperCallNode(CORINFO_HELP_INITINSTCLASS, TYP_VOID, gtNewArgList(vtTree, methodHnd));
}
case CORINFO_LOOKUP_CLASSPARAM:
{
GenTreePtr vtTree = gtNewLclvNode(info.compTypeCtxtArg, TYP_I_IMPL);
- return gtNewHelperCallNode(CORINFO_HELP_INITCLASS, TYP_VOID, 0, gtNewArgList(vtTree));
+ return gtNewHelperCallNode(CORINFO_HELP_INITCLASS, TYP_VOID, gtNewArgList(vtTree));
}
case CORINFO_LOOKUP_METHODPARAM:
{
GenTreePtr methHndTree = gtNewLclvNode(info.compTypeCtxtArg, TYP_I_IMPL);
- return gtNewHelperCallNode(CORINFO_HELP_INITINSTCLASS, TYP_VOID, 0,
+ return gtNewHelperCallNode(CORINFO_HELP_INITINSTCLASS, TYP_VOID,
gtNewArgList(gtNewIconNode(0), methHndTree));
}
}
@@ -17280,7 +17927,7 @@ void Compiler::fgMorph()
// confirm that the argument is a GC pointer (for debugging (GC stress))
GenTreePtr op = gtNewLclvNode(i, TYP_REF);
GenTreeArgList* args = gtNewArgList(op);
- op = gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_VOID, 0, args);
+ op = gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_VOID, args);
fgEnsureFirstBBisScratch();
fgInsertStmtAtEnd(fgFirstBB, op);
@@ -17391,6 +18038,15 @@ void Compiler::fgMorph()
DBEXEC(VERBOSE, fgDispBasicBlocks(true));
#endif
+#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+ if (fgNeedToAddFinallyTargetBits)
+ {
+ // We previously wiped out the BBF_FINALLY_TARGET bits due to some morphing; add them back.
+ fgAddFinallyTargetFlags();
+ fgNeedToAddFinallyTargetBits = false;
+ }
+#endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+
/* Decide the kind of code we want to generate */
fgSetOptions();
@@ -18024,11 +18680,6 @@ void Compiler::fgRetypeImplicitByRefArgs()
//
varDsc->lvOverlappingFields = 0; // This flag could have been set, clear it.
-#ifdef DEBUG
- // This should not be converted to a double in stress mode,
- // because it is really a pointer
- varDsc->lvKeepType = 1;
-
// The struct parameter may have had its address taken, but the pointer parameter
// cannot -- any uses of the struct parameter's address are uses of the pointer
// parameter's value, and there's no way for the MSIL to reference the pointer
@@ -18036,6 +18687,11 @@ void Compiler::fgRetypeImplicitByRefArgs()
varDsc->lvAddrExposed = 0;
varDsc->lvDoNotEnregister = 0;
+#ifdef DEBUG
+ // This should not be converted to a double in stress mode,
+ // because it is really a pointer
+ varDsc->lvKeepType = 1;
+
if (verbose)
{
printf("Changing the lvType for struct parameter V%02d to TYP_BYREF.\n", lclNum);
@@ -19044,7 +19700,7 @@ bool Compiler::fgMorphCombineSIMDFieldAssignments(BasicBlock* block, GenTreePtr
{
assert(simdStructNode->OperIsLocal());
assert(lvaIsImplicitByRefLocal(simdStructNode->AsLclVarCommon()->gtLclNum));
- simdStructNode = gtNewOperNode(GT_IND, simdType, simdStructNode);
+ simdStructNode = gtNewIndir(simdType, simdStructNode);
}
else
{
diff --git a/src/jit/namedintrinsiclist.h b/src/jit/namedintrinsiclist.h
new file mode 100644
index 0000000000..cf81afc119
--- /dev/null
+++ b/src/jit/namedintrinsiclist.h
@@ -0,0 +1,16 @@
+// 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 _NAMEDINTRINSICLIST_H_
+#define _NAMEDINTRINSICLIST_H_
+
+// Named jit intrinsics
+
+enum NamedIntrinsic
+{
+ NI_Illegal = 0,
+ NI_Enum_HasFlag = 1
+};
+
+#endif // _NAMEDINTRINSICLIST_H_
diff --git a/src/jit/nodeinfo.h b/src/jit/nodeinfo.h
index 5f03da2776..3f8532bd37 100644
--- a/src/jit/nodeinfo.h
+++ b/src/jit/nodeinfo.h
@@ -32,6 +32,7 @@ public:
regOptional = false;
definesAnyRegisters = false;
isInternalRegDelayFree = false;
+ isNoRegCompare = false;
#ifdef DEBUG
isInitialized = false;
#endif
@@ -144,6 +145,9 @@ public:
// in which result is produced.
unsigned char isInternalRegDelayFree : 1;
+ // True if this is a compare feeding a JTRUE that doesn't need to be generated into a register.
+ unsigned char isNoRegCompare : 1;
+
#ifdef DEBUG
// isInitialized is set when the tree node is handled.
unsigned char isInitialized : 1;
diff --git a/src/jit/optcse.cpp b/src/jit/optcse.cpp
index cadb3daf78..bcb5a4c2a8 100644
--- a/src/jit/optcse.cpp
+++ b/src/jit/optcse.cpp
@@ -724,9 +724,14 @@ unsigned Compiler::optValnumCSE_Locate()
}
// Don't CSE constant values, instead let the Value Number
- // based Assertion Prop phase handle them.
+ // based Assertion Prop phase handle them. Here, unlike
+ // the rest of optCSE, we use the conservative value number
+ // rather than the liberal one, since the conservative one
+ // is what the Value Number based Assertion Prop will use
+ // and the point is to avoid optimizing cases that it will
+ // handle.
//
- if (vnStore->IsVNConstant(vnlib))
+ if (vnStore->IsVNConstant(tree->GetVN(VNK_Conservative)))
{
continue;
}
diff --git a/src/jit/optimizer.cpp b/src/jit/optimizer.cpp
index edf8b270db..5d11c2cc1d 100644
--- a/src/jit/optimizer.cpp
+++ b/src/jit/optimizer.cpp
@@ -1119,10 +1119,11 @@ bool Compiler::optExtractInitTestIncr(
/*****************************************************************************
*
- * Record the loop in the loop table.
+ * Record the loop in the loop table. Return true if successful, false if
+ * out of entries in loop table.
*/
-void Compiler::optRecordLoop(BasicBlock* head,
+bool Compiler::optRecordLoop(BasicBlock* head,
BasicBlock* first,
BasicBlock* top,
BasicBlock* entry,
@@ -1138,7 +1139,7 @@ void Compiler::optRecordLoop(BasicBlock* head,
#if COUNT_LOOPS
loopOverflowThisMethod = true;
#endif
- return;
+ return false;
}
// Assumed preconditions on the loop we're adding.
@@ -1318,6 +1319,7 @@ void Compiler::optRecordLoop(BasicBlock* head,
DONE_LOOP:
DBEXEC(verbose, optPrintLoopRecording(loopInd));
optLoopCount++;
+ return true;
}
#ifdef DEBUG
@@ -1416,430 +1418,1047 @@ void Compiler::optCheckPreds()
#endif // DEBUG
-/*****************************************************************************
- * Find the natural loops, using dominators. Note that the test for
- * a loop is slightly different from the standard one, because we have
- * not done a depth first reordering of the basic blocks.
- */
-
-void Compiler::optFindNaturalLoops()
+namespace
{
-#ifdef DEBUG
- if (verbose)
+//------------------------------------------------------------------------
+// LoopSearch: Class that handles scanning a range of blocks to detect a loop,
+// moving blocks to make the loop body contiguous, and recording
+// the loop.
+//
+// We will use the following terminology:
+// HEAD - the basic block that flows into the loop ENTRY block (Currently MUST be lexically before entry).
+// Not part of the looping of the loop.
+// FIRST - the lexically first basic block (in bbNext order) within this loop.
+// TOP - the target of the backward edge from BOTTOM. In most cases FIRST and TOP are the same.
+// BOTTOM - the lexically last block in the loop (i.e. the block from which we jump to the top)
+// EXIT - the predecessor of loop's unique exit edge, if it has a unique exit edge; else nullptr
+// ENTRY - the entry in the loop (not necessarly the TOP), but there must be only one entry
+//
+// We (currently) require the body of a loop to be a contiguous (in bbNext order) sequence of basic blocks.
+// When the loop is identified, blocks will be moved out to make it a compact contiguous region if possible,
+// and in cases where compaction is not possible, we'll subsequently treat all blocks in the lexical range
+// between TOP and BOTTOM as part of the loop even if they aren't part of the SCC.
+// Regarding nesting: Since a given block can only have one back-edge (we only detect loops with back-edges
+// from BBJ_COND or BBJ_ALWAYS blocks), no two loops will share the same BOTTOM. Two loops may share the
+// same FIRST/TOP/ENTRY as reported by LoopSearch, and optCanonicalizeLoopNest will subsequently re-write
+// the CFG so that no two loops share the same FIRST/TOP/ENTRY anymore.
+//
+// |
+// v
+// head
+// |
+// | top/first <--+
+// | | |
+// | ... |
+// | | |
+// | v |
+// +---> entry |
+// | |
+// ... |
+// | |
+// v |
+// +-- exit/tail |
+// | | |
+// | ... |
+// | | |
+// | v |
+// | bottom ---+
+// |
+// +------+
+// |
+// v
+//
+class LoopSearch
+{
+
+ // Keeping track of which blocks are in the loop requires two block sets since we may add blocks
+ // as we go but the BlockSet type's max ID doesn't increase to accomodate them. Define a helper
+ // struct to make the ensuing code more readable.
+ struct LoopBlockSet
{
- printf("*************** In optFindNaturalLoops()\n");
- }
-#endif // DEBUG
+ private:
+ // Keep track of blocks with bbNum <= oldBlockMaxNum in a regular BlockSet, since
+ // it can hold all of them.
+ BlockSet oldBlocksInLoop; // Blocks with bbNum <= oldBlockMaxNum
- flowList* pred;
- flowList* predTop;
- flowList* predEntry;
+ // Keep track of blocks with bbNum > oldBlockMaxNum in a separate BlockSet, but
+ // indexing them by (blockNum - oldBlockMaxNum); since we won't generate more than
+ // one new block per old block, this must be sufficient to track any new blocks.
+ BlockSet newBlocksInLoop; // Blocks with bbNum > oldBlockMaxNum
- noway_assert(fgDomsComputed);
- assert(fgHasLoops);
+ Compiler* comp;
+ unsigned int oldBlockMaxNum;
-#if COUNT_LOOPS
- hasMethodLoops = false;
- loopsThisMethod = 0;
- loopOverflowThisMethod = false;
+ public:
+ LoopBlockSet(Compiler* comp)
+ : oldBlocksInLoop(BlockSetOps::UninitVal())
+ , newBlocksInLoop(BlockSetOps::UninitVal())
+ , comp(comp)
+ , oldBlockMaxNum(comp->fgBBNumMax)
+ {
+ }
+
+ void Reset(unsigned int seedBlockNum)
+ {
+ if (BlockSetOps::MayBeUninit(oldBlocksInLoop))
+ {
+ // Either the block sets are uninitialized (and long), so we need to initialize
+ // them (and allocate their backing storage), or they are short and empty, so
+ // assigning MakeEmpty to them is as cheap as ClearD.
+ oldBlocksInLoop = BlockSetOps::MakeEmpty(comp);
+ newBlocksInLoop = BlockSetOps::MakeEmpty(comp);
+ }
+ else
+ {
+ // We know the backing storage is already allocated, so just clear it.
+ BlockSetOps::ClearD(comp, oldBlocksInLoop);
+ BlockSetOps::ClearD(comp, newBlocksInLoop);
+ }
+ assert(seedBlockNum <= oldBlockMaxNum);
+ BlockSetOps::AddElemD(comp, oldBlocksInLoop, seedBlockNum);
+ }
+
+ bool CanRepresent(unsigned int blockNum)
+ {
+ // We can represent old blocks up to oldBlockMaxNum, and
+ // new blocks up to 2 * oldBlockMaxNum.
+ return (blockNum <= 2 * oldBlockMaxNum);
+ }
+
+ bool IsMember(unsigned int blockNum)
+ {
+ if (blockNum > oldBlockMaxNum)
+ {
+ return BlockSetOps::IsMember(comp, newBlocksInLoop, blockNum - oldBlockMaxNum);
+ }
+ return BlockSetOps::IsMember(comp, oldBlocksInLoop, blockNum);
+ }
+
+ void Insert(unsigned int blockNum)
+ {
+ if (blockNum > oldBlockMaxNum)
+ {
+ BlockSetOps::AddElemD(comp, newBlocksInLoop, blockNum - oldBlockMaxNum);
+ }
+ else
+ {
+ BlockSetOps::AddElemD(comp, oldBlocksInLoop, blockNum);
+ }
+ }
+
+ bool TestAndInsert(unsigned int blockNum)
+ {
+ if (blockNum > oldBlockMaxNum)
+ {
+ unsigned int shiftedNum = blockNum - oldBlockMaxNum;
+ if (!BlockSetOps::IsMember(comp, newBlocksInLoop, shiftedNum))
+ {
+ BlockSetOps::AddElemD(comp, newBlocksInLoop, shiftedNum);
+ return false;
+ }
+ }
+ else
+ {
+ if (!BlockSetOps::IsMember(comp, oldBlocksInLoop, blockNum))
+ {
+ BlockSetOps::AddElemD(comp, oldBlocksInLoop, blockNum);
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+
+ LoopBlockSet loopBlocks; // Set of blocks identified as part of the loop
+ Compiler* comp;
+
+ // See LoopSearch class comment header for a diagram relating these fields:
+ BasicBlock* head; // Predecessor of unique entry edge
+ BasicBlock* first; // Lexically first in-loop block
+ BasicBlock* top; // Successor of back-edge from BOTTOM
+ BasicBlock* bottom; // Predecessor of back-edge to TOP, also lexically last in-loop block
+ BasicBlock* entry; // Successor of unique entry edge
+
+ BasicBlock* lastExit; // Most recently discovered exit block
+ unsigned char exitCount; // Number of discovered exit edges
+ unsigned int oldBlockMaxNum; // Used to identify new blocks created during compaction
+ BlockSet bottomBlocks; // BOTTOM blocks of already-recorded loops
+#ifdef DEBUG
+ bool forgotExit = false; // Flags a rare case where lastExit gets nulled out, for assertions
#endif
+ bool changedFlowGraph = false; // Signals that loop compaction has modified the flow graph
+
+public:
+ LoopSearch(Compiler* comp)
+ : loopBlocks(comp), comp(comp), oldBlockMaxNum(comp->fgBBNumMax), bottomBlocks(BlockSetOps::MakeEmpty(comp))
+ {
+ // Make sure we've renumbered such that the bitsets can hold all the bits
+ assert(comp->fgBBNumMax <= comp->fgCurBBEpochSize);
+ }
- /* We will use the following terminology:
- * HEAD - the basic block that flows into the loop ENTRY block (Currently MUST be lexically before entry).
- Not part of the looping of the loop.
- * FIRST - the lexically first basic block (in bbNext order) within this loop. (May be part of a nested loop,
- * but not the outer loop. ???)
- * TOP - the target of the backward edge from BOTTOM. In most cases FIRST and TOP are the same.
- * BOTTOM - the lexically last block in the loop (i.e. the block from which we jump to the top)
- * EXIT - the loop exit or the block right after the bottom
- * ENTRY - the entry in the loop (not necessarly the TOP), but there must be only one entry
- *
- * We (currently) require the body of a loop to be a contiguous (in bbNext order) sequence of basic blocks.
-
- |
- v
- head
- |
- | top/beg <--+
- | | |
- | ... |
- | | |
- | v |
- +---> entry |
- | |
- ... |
- | |
- v |
- +-- exit/tail |
- | | |
- | ... |
- | | |
- | v |
- | bottom ---+
- |
- +------+
- |
- v
+ //------------------------------------------------------------------------
+ // RecordLoop: Notify the Compiler that a loop has been found.
+ //
+ // Return Value:
+ // true - Loop successfully recorded.
+ // false - Compiler has run out of loop descriptors; loop not recorded.
+ //
+ bool RecordLoop()
+ {
+ /* At this point we have a compact loop - record it in the loop table
+ * If we found only one exit, record it in the table too
+ * (otherwise an exit = nullptr in the loop table means multiple exits) */
- */
+ BasicBlock* onlyExit = (exitCount == 1 ? lastExit : nullptr);
+ if (comp->optRecordLoop(head, first, top, entry, bottom, onlyExit, exitCount))
+ {
+ // Record the BOTTOM block for future reference before returning.
+ assert(bottom->bbNum <= oldBlockMaxNum);
+ BlockSetOps::AddElemD(comp, bottomBlocks, bottom->bbNum);
+ return true;
+ }
- BasicBlock* head;
- BasicBlock* top;
- BasicBlock* bottom;
- BasicBlock* entry;
- BasicBlock* exit;
- unsigned char exitCount;
+ // Unable to record this loop because the loop descriptor table overflowed.
+ return false;
+ }
- for (head = fgFirstBB; head->bbNext; head = head->bbNext)
+ //------------------------------------------------------------------------
+ // ChangedFlowGraph: Determine whether loop compaction has modified the flow graph.
+ //
+ // Return Value:
+ // true - The flow graph has been modified; fgUpdateChangedFlowGraph should
+ // be called (which is the caller's responsibility).
+ // false - The flow graph has not been modified by this LoopSearch.
+ //
+ bool ChangedFlowGraph()
{
- top = head->bbNext;
- exit = nullptr;
- exitCount = 0;
+ return changedFlowGraph;
+ }
- // Blocks that are rarely run have a zero bbWeight and should
- // never be optimized here
+ //------------------------------------------------------------------------
+ // FindLoop: Search for a loop with the given HEAD block and back-edge.
+ //
+ // Arguments:
+ // head - Block to be the HEAD of any loop identified
+ // top - Block to be the TOP of any loop identified
+ // bottom - Block to be the BOTTOM of any loop identified
+ //
+ // Return Value:
+ // true - Found a valid loop.
+ // false - Did not find a valid loop.
+ //
+ // Notes:
+ // May modify flow graph to make loop compact before returning.
+ // Will set instance fields to track loop's extent and exits if a valid
+ // loop is found, and potentially trash them otherwise.
+ //
+ bool FindLoop(BasicBlock* head, BasicBlock* top, BasicBlock* bottom)
+ {
+ /* Is this a loop candidate? - We look for "back edges", i.e. an edge from BOTTOM
+ * to TOP (note that this is an abuse of notation since this is not necessarily a back edge
+ * as the definition says, but merely an indication that we have a loop there).
+ * Thus, we have to be very careful and after entry discovery check that it is indeed
+ * the only place we enter the loop (especially for non-reducible flow graphs).
+ */
- if (top->bbWeight == BB_ZERO_WEIGHT)
+ if (top->bbNum > bottom->bbNum) // is this a backward edge? (from BOTTOM to TOP)
{
- continue;
+ // Edge from BOTTOM to TOP is not a backward edge
+ return false;
}
- for (pred = top->bbPreds; pred; pred = pred->flNext)
+ if (bottom->bbNum > oldBlockMaxNum)
{
- /* Is this a loop candidate? - We look for "back edges", i.e. an edge from BOTTOM
- * to TOP (note that this is an abuse of notation since this is not necessarily a back edge
- * as the definition says, but merely an indication that we have a loop there).
- * Thus, we have to be very careful and after entry discovery check that it is indeed
- * the only place we enter the loop (especially for non-reducible flow graphs).
- */
+ // Not a true back-edge; bottom is a block added to reconnect fall-through during
+ // loop processing, so its block number does not reflect its position.
+ return false;
+ }
- bottom = pred->flBlock;
- exitCount = 0;
+ if ((bottom->bbJumpKind == BBJ_EHFINALLYRET) || (bottom->bbJumpKind == BBJ_EHFILTERRET) ||
+ (bottom->bbJumpKind == BBJ_EHCATCHRET) || (bottom->bbJumpKind == BBJ_CALLFINALLY) ||
+ (bottom->bbJumpKind == BBJ_SWITCH))
+ {
+ /* BBJ_EHFINALLYRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, and BBJ_CALLFINALLY can never form a loop.
+ * BBJ_SWITCH that has a backward jump appears only for labeled break. */
+ return false;
+ }
- if (top->bbNum <= bottom->bbNum) // is this a backward edge? (from BOTTOM to TOP)
+ /* The presence of a "back edge" is an indication that a loop might be present here
+ *
+ * LOOP:
+ * 1. A collection of STRONGLY CONNECTED nodes i.e. there is a path from any
+ * node in the loop to any other node in the loop (wholly within the loop)
+ * 2. The loop has a unique ENTRY, i.e. there is only one way to reach a node
+ * in the loop from outside the loop, and that is through the ENTRY
+ */
+
+ /* Let's find the loop ENTRY */
+ BasicBlock* entry = FindEntry(head, top, bottom);
+
+ if (entry == nullptr)
+ {
+ // For now, we only recognize loops where HEAD has some successor ENTRY in the loop.
+ return false;
+ }
+
+ // Passed the basic checks; initialize instance state for this back-edge.
+ this->head = head;
+ this->top = top;
+ this->entry = entry;
+ this->bottom = bottom;
+ this->lastExit = nullptr;
+ this->exitCount = 0;
+
+ // Now we find the "first" block -- the earliest block reachable within the loop.
+ // With our current algorithm, this is always the same as "top".
+ this->first = top;
+
+ if (!HasSingleEntryCycle())
+ {
+ // There isn't actually a loop between TOP and BOTTOM
+ return false;
+ }
+
+ if (!loopBlocks.IsMember(top->bbNum))
+ {
+ // The "back-edge" we identified isn't actually part of the flow cycle containing ENTRY
+ return false;
+ }
+
+ // Disqualify loops where the first block of the loop is less nested in EH than
+ // the bottom block. That is, we don't want to handle loops where the back edge
+ // goes from within an EH region to a first block that is outside that same EH
+ // region. Note that we *do* handle loops where the first block is the *first*
+ // block of a more nested EH region (since it is legal to branch to the first
+ // block of an immediately more nested EH region). So, for example, disqualify
+ // this:
+ //
+ // BB02
+ // ...
+ // try {
+ // ...
+ // BB10 BBJ_COND => BB02
+ // ...
+ // }
+ //
+ // Here, BB10 is more nested than BB02.
+
+ if (bottom->hasTryIndex() && !comp->bbInTryRegions(bottom->getTryIndex(), first))
+ {
+ JITDUMP("Loop 'first' BB%02u is in an outer EH region compared to loop 'bottom' BB%02u. Rejecting "
+ "loop.\n",
+ first->bbNum, bottom->bbNum);
+ return false;
+ }
+
+#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+ // Disqualify loops where the first block of the loop is a finally target.
+ // The main problem is when multiple loops share a 'first' block that is a finally
+ // target and we canonicalize the loops by adding a new loop head. In that case, we
+ // need to update the blocks so the finally target bit is moved to the newly created
+ // block, and removed from the old 'first' block. This is 'hard', so at this point
+ // in the RyuJIT codebase (when we don't expect to keep the "old" ARM32 code generator
+ // long-term), it's easier to disallow the loop than to update the flow graph to
+ // support this case.
+
+ if ((first->bbFlags & BBF_FINALLY_TARGET) != 0)
+ {
+ JITDUMP("Loop 'first' BB%02u is a finally target. Rejecting loop.\n", first->bbNum);
+ return false;
+ }
+#endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+
+ // Compact the loop (sweep through it and move out any blocks that aren't part of the
+ // flow cycle), and find the exits.
+ if (!MakeCompactAndFindExits())
+ {
+ // Unable to preserve well-formed loop during compaction.
+ return false;
+ }
+
+ // We have a valid loop.
+ return true;
+ }
+
+private:
+ //------------------------------------------------------------------------
+ // FindEntry: See if given HEAD flows to valid ENTRY between given TOP and BOTTOM
+ //
+ // Arguments:
+ // head - Block to be the HEAD of any loop identified
+ // top - Block to be the TOP of any loop identified
+ // bottom - Block to be the BOTTOM of any loop identified
+ //
+ // Return Value:
+ // Block to be the ENTRY of any loop identified, or nullptr if no
+ // such entry meeting our criteria can be found.
+ //
+ // Notes:
+ // Returns main entry if one is found, does not check for side-entries.
+ //
+ BasicBlock* FindEntry(BasicBlock* head, BasicBlock* top, BasicBlock* bottom)
+ {
+ if (head->bbJumpKind == BBJ_ALWAYS)
+ {
+ if (head->bbJumpDest->bbNum <= bottom->bbNum && head->bbJumpDest->bbNum >= top->bbNum)
{
- if ((bottom->bbJumpKind == BBJ_EHFINALLYRET) || (bottom->bbJumpKind == BBJ_EHFILTERRET) ||
- (bottom->bbJumpKind == BBJ_EHCATCHRET) || (bottom->bbJumpKind == BBJ_CALLFINALLY) ||
- (bottom->bbJumpKind == BBJ_SWITCH))
- {
- /* BBJ_EHFINALLYRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, and BBJ_CALLFINALLY can never form a loop.
- * BBJ_SWITCH that has a backward jump appears only for labeled break. */
- goto NO_LOOP;
- }
+ /* OK - we enter somewhere within the loop */
- BasicBlock* loopBlock;
+ /* some useful asserts
+ * Cannot enter at the top - should have being caught by redundant jumps */
- /* The presence of a "back edge" is an indication that a loop might be present here
- *
- * LOOP:
- * 1. A collection of STRONGLY CONNECTED nodes i.e. there is a path from any
- * node in the loop to any other node in the loop (wholly within the loop)
- * 2. The loop has a unique ENTRY, i.e. there is only one way to reach a node
- * in the loop from outside the loop, and that is through the ENTRY
- */
+ assert((head->bbJumpDest != top) || (head->bbFlags & BBF_KEEP_BBJ_ALWAYS));
- /* Let's find the loop ENTRY */
+ return head->bbJumpDest;
+ }
+ else
+ {
+ /* special case - don't consider now */
+ // assert (!"Loop entered in weird way!");
+ return nullptr;
+ }
+ }
+ // Can we fall through into the loop?
+ else if (head->bbJumpKind == BBJ_NONE || head->bbJumpKind == BBJ_COND)
+ {
+ /* The ENTRY is at the TOP (a do-while loop) */
+ return top;
+ }
+ else
+ {
+ return nullptr; // head does not flow into the loop bail for now
+ }
+ }
- if (head->bbJumpKind == BBJ_ALWAYS)
- {
- if (head->bbJumpDest->bbNum <= bottom->bbNum && head->bbJumpDest->bbNum >= top->bbNum)
- {
- /* OK - we enter somewhere within the loop */
- entry = head->bbJumpDest;
+ //------------------------------------------------------------------------
+ // HasSingleEntryCycle: Perform a reverse flow walk from ENTRY, visiting
+ // only blocks between TOP and BOTTOM, to determine if such a cycle
+ // exists and if it has a single entry.
+ //
+ // Return Value:
+ // true - Found a single-entry cycle.
+ // false - Did not find a single-entry cycle.
+ //
+ // Notes:
+ // Will mark (in `loopBlocks`) all blocks found to participate in the
+ // cycle.
+ //
+ bool HasSingleEntryCycle()
+ {
+ // Now do a backwards flow walk from entry to see if we have a single-entry loop
+ bool foundCycle = false;
- /* some useful asserts
- * Cannot enter at the top - should have being caught by redundant jumps */
+ // Seed the loop block set and worklist with the entry block.
+ loopBlocks.Reset(entry->bbNum);
+ jitstd::list<BasicBlock*> worklist(comp->getAllocator());
+ worklist.push_back(entry);
- assert((entry != top) || (head->bbFlags & BBF_KEEP_BBJ_ALWAYS));
- }
- else
+ while (!worklist.empty())
+ {
+ BasicBlock* block = worklist.back();
+ worklist.pop_back();
+
+ /* Make sure ENTRY dominates all blocks in the loop
+ * This is necessary to ensure condition 2. above
+ */
+ if (block->bbNum > oldBlockMaxNum)
+ {
+ // This is a new block we added to connect fall-through, so the
+ // recorded dominator information doesn't cover it. Just continue,
+ // and when we process its unique predecessor we'll abort if ENTRY
+ // doesn't dominate that.
+ }
+ else if (!comp->fgDominate(entry, block))
+ {
+ return false;
+ }
+
+ // Add preds to the worklist, checking for side-entries.
+ for (flowList* predIter = block->bbPreds; predIter != nullptr; predIter = predIter->flNext)
+ {
+ BasicBlock* pred = predIter->flBlock;
+
+ unsigned int testNum = PositionNum(pred);
+
+ if ((testNum < top->bbNum) || (testNum > bottom->bbNum))
+ {
+ // Pred is out of loop range
+ if (block == entry)
{
- /* special case - don't consider now */
- // assert (!"Loop entered in weird way!");
- goto NO_LOOP;
+ if (pred == head)
+ {
+ // This is the single entry we expect.
+ continue;
+ }
+ // ENTRY has some pred other than head outside the loop. If ENTRY does not
+ // dominate this pred, we'll consider this a side-entry and skip this loop;
+ // otherwise the loop is still valid and this may be a (flow-wise) back-edge
+ // of an outer loop. For the dominance test, if `pred` is a new block, use
+ // its unique predecessor since the dominator tree has info for that.
+ BasicBlock* effectivePred = (pred->bbNum > oldBlockMaxNum ? pred->bbPrev : pred);
+ if (comp->fgDominate(entry, effectivePred))
+ {
+ // Outer loop back-edge
+ continue;
+ }
}
+
+ // There are multiple entries to this loop, don't consider it.
+ return false;
}
- // Can we fall through into the loop?
- else if (head->bbJumpKind == BBJ_NONE || head->bbJumpKind == BBJ_COND)
+
+ bool isFirstVisit;
+ if (pred == entry)
{
- /* The ENTRY is at the TOP (a do-while loop) */
- entry = top;
+ // We have indeed found a cycle in the flow graph.
+ isFirstVisit = !foundCycle;
+ foundCycle = true;
+ assert(loopBlocks.IsMember(pred->bbNum));
+ }
+ else if (loopBlocks.TestAndInsert(pred->bbNum))
+ {
+ // Already visited this pred
+ isFirstVisit = false;
}
else
{
- goto NO_LOOP; // head does not flow into the loop bail for now
+ // Add this pred to the worklist
+ worklist.push_back(pred);
+ isFirstVisit = true;
}
- // Now we find the "first" block -- the earliest block reachable within the loop.
- // This is usually the same as "top", but can differ in rare cases where "top" is
- // the entry block of a nested loop, and that nested loop branches backwards to a
- // a block before "top". We find this by searching for such backwards branches
- // in the loop known so far.
- BasicBlock* first = top;
- BasicBlock* newFirst;
- bool blocksToSearch = true;
- BasicBlock* validatedAfter = bottom->bbNext;
- while (blocksToSearch)
+ if (isFirstVisit && (pred->bbNext != nullptr) && (PositionNum(pred->bbNext) == pred->bbNum))
{
- blocksToSearch = false;
- newFirst = nullptr;
- blocksToSearch = false;
- for (loopBlock = first; loopBlock != validatedAfter; loopBlock = loopBlock->bbNext)
- {
- unsigned nSucc = loopBlock->NumSucc();
- for (unsigned j = 0; j < nSucc; j++)
- {
- BasicBlock* succ = loopBlock->GetSucc(j);
- if ((newFirst == nullptr && succ->bbNum < first->bbNum) ||
- (newFirst != nullptr && succ->bbNum < newFirst->bbNum))
- {
- newFirst = succ;
- }
- }
- }
- if (newFirst != nullptr)
- {
- validatedAfter = first;
- first = newFirst;
- blocksToSearch = true;
- }
+ // We've created a new block immediately after `pred` to
+ // reconnect what was fall-through. Mark it as in-loop also;
+ // it needs to stay with `prev` and if it exits the loop we'd
+ // just need to re-create it if we tried to move it out.
+ loopBlocks.Insert(pred->bbNext->bbNum);
}
+ }
+ }
+
+ return foundCycle;
+ }
+
+ //------------------------------------------------------------------------
+ // PositionNum: Get the number identifying a block's position per the
+ // lexical ordering that existed before searching for (and compacting)
+ // loops.
+ //
+ // Arguments:
+ // block - Block whose position is desired.
+ //
+ // Return Value:
+ // A number indicating that block's position relative to others.
+ //
+ // Notes:
+ // When the given block is a new one created during loop compaction,
+ // the number of its unique predecessor is returned.
+ //
+ unsigned int PositionNum(BasicBlock* block)
+ {
+ if (block->bbNum > oldBlockMaxNum)
+ {
+ // This must be a block we inserted to connect fall-through after moving blocks.
+ // To determine if it's in the loop or not, use the number of its unique predecessor
+ // block.
+ assert(block->bbPreds->flBlock == block->bbPrev);
+ assert(block->bbPreds->flNext == nullptr);
+ return block->bbPrev->bbNum;
+ }
+ return block->bbNum;
+ }
- // Is "head" still before "first"? If not, we don't have a valid loop...
- if (head->bbNum >= first->bbNum)
+ //------------------------------------------------------------------------
+ // MakeCompactAndFindExits: Compact the loop (sweep through it and move out
+ // any blocks that aren't part of the flow cycle), and find the exits (set
+ // lastExit and exitCount).
+ //
+ // Return Value:
+ // true - Loop successfully compacted (or `loopBlocks` expanded to
+ // include all blocks in the lexical range), exits enumerated.
+ // false - Loop cannot be made compact and remain well-formed.
+ //
+ bool MakeCompactAndFindExits()
+ {
+ // Compaction (if it needs to happen) will require an insertion point.
+ BasicBlock* moveAfter = nullptr;
+
+ for (BasicBlock* previous = top->bbPrev; previous != bottom;)
+ {
+ BasicBlock* block = previous->bbNext;
+
+ if (loopBlocks.IsMember(block->bbNum))
+ {
+ // This block is a member of the loop. Check to see if it may exit the loop.
+ CheckForExit(block);
+
+ // Done processing this block; move on to the next.
+ previous = block;
+ continue;
+ }
+
+ // This blocks is lexically between TOP and BOTTOM, but it does not
+ // participate in the flow cycle. Check for a run of consecutive
+ // such blocks.
+ BasicBlock* lastNonLoopBlock = block;
+ BasicBlock* nextLoopBlock = block->bbNext;
+ while (!loopBlocks.IsMember(nextLoopBlock->bbNum))
+ {
+ lastNonLoopBlock = nextLoopBlock;
+ nextLoopBlock = nextLoopBlock->bbNext;
+ // This loop must terminate because we know BOTTOM is in loopBlocks.
+ }
+
+ // Choose an insertion point for non-loop blocks if we haven't yet done so.
+ if (moveAfter == nullptr)
+ {
+ moveAfter = FindInsertionPoint();
+ }
+
+ if (!BasicBlock::sameEHRegion(previous, nextLoopBlock) || !BasicBlock::sameEHRegion(previous, moveAfter))
+ {
+ // EH regions would be ill-formed if we moved these blocks out.
+ // See if we can consider them loop blocks without introducing
+ // a side-entry.
+ if (CanTreatAsLoopBlocks(block, lastNonLoopBlock))
{
- JITDUMP(
- "Extending loop [BB%02u..BB%02u] 'first' to BB%02u captures head BB%02u. Rejecting loop.\n",
- top->bbNum, bottom->bbNum, first->bbNum, head->bbNum);
- goto NO_LOOP;
+ // The call to `canTreatAsLoop` marked these blocks as part of the loop;
+ // iterate without updating `previous` so that we'll analyze them as part
+ // of the loop.
+ continue;
}
+ else
+ {
+ // We can't move these out of the loop or leave them in, so just give
+ // up on this loop.
+ return false;
+ }
+ }
- /* Make sure ENTRY dominates all blocks in the loop
- * This is necessary to ensure condition 2. above
- * At the same time check if the loop has a single exit
- * point - those loops are easier to optimize */
+ // Now physically move the blocks.
+ BasicBlock* moveBefore = moveAfter->bbNext;
- for (loopBlock = top; loopBlock != bottom->bbNext; loopBlock = loopBlock->bbNext)
- {
- if (!fgDominate(entry, loopBlock))
- {
- goto NO_LOOP;
- }
+ comp->fgUnlinkRange(block, lastNonLoopBlock);
+ comp->fgMoveBlocksAfter(block, lastNonLoopBlock, moveAfter);
+ comp->ehUpdateLastBlocks(moveAfter, lastNonLoopBlock);
- if (loopBlock == bottom)
- {
- if (bottom->bbJumpKind != BBJ_ALWAYS)
- {
- /* there is an exit at the bottom */
+ // Apply any adjustments needed for fallthrough at the boundaries of the moved region.
+ FixupFallThrough(moveAfter, moveBefore, block);
+ FixupFallThrough(lastNonLoopBlock, nextLoopBlock, moveBefore);
+ // Also apply any adjustments needed where the blocks were snipped out of the loop.
+ BasicBlock* newBlock = FixupFallThrough(previous, block, nextLoopBlock);
+ if (newBlock != nullptr)
+ {
+ // This new block is in the loop and is a loop exit.
+ loopBlocks.Insert(newBlock->bbNum);
+ lastExit = newBlock;
+ ++exitCount;
+ }
- noway_assert(bottom->bbJumpDest == top);
- exit = bottom;
- exitCount++;
- continue;
- }
- }
+ // Update moveAfter for the next insertion.
+ moveAfter = lastNonLoopBlock;
- BasicBlock* exitPoint;
+ // Note that we've changed the flow graph, and continue without updating
+ // `previous` so that we'll process nextLoopBlock.
+ changedFlowGraph = true;
+ }
- switch (loopBlock->bbJumpKind)
- {
- case BBJ_COND:
- case BBJ_CALLFINALLY:
- case BBJ_ALWAYS:
- case BBJ_EHCATCHRET:
- assert(loopBlock->bbJumpDest);
- exitPoint = loopBlock->bbJumpDest;
-
- if (exitPoint->bbNum < top->bbNum || exitPoint->bbNum > bottom->bbNum)
- {
- /* exit from a block other than BOTTOM */
- exit = loopBlock;
- exitCount++;
- }
- break;
+ if ((exitCount == 1) && (lastExit == nullptr))
+ {
+ // If we happen to have a loop with two exits, one of which goes to an
+ // infinite loop that's lexically nested inside it, where the inner loop
+ // can't be moved out, we can end up in this situation (because
+ // CanTreatAsLoopBlocks will have decremented the count expecting to find
+ // another exit later). Bump the exit count to 2, since downstream code
+ // will not be prepared for null lastExit with exitCount of 1.
+ assert(forgotExit);
+ exitCount = 2;
+ }
- case BBJ_NONE:
- break;
+ // Loop compaction was successful
+ return true;
+ }
- case BBJ_EHFINALLYRET:
- case BBJ_EHFILTERRET:
- /* The "try" associated with this "finally" must be in the
- * same loop, so the finally block will return control inside the loop */
- break;
+ //------------------------------------------------------------------------
+ // FindInsertionPoint: Find an appropriate spot to which blocks that are
+ // lexically between TOP and BOTTOM but not part of the flow cycle
+ // can be moved.
+ //
+ // Return Value:
+ // Block after which to insert moved blocks.
+ //
+ BasicBlock* FindInsertionPoint()
+ {
+ // Find an insertion point for blocks we're going to move. Move them down
+ // out of the loop, and if possible find a spot that won't break up fall-through.
+ BasicBlock* moveAfter = bottom;
+ while (moveAfter->bbFallsThrough())
+ {
+ // Keep looking for a better insertion point if we can.
+ BasicBlock* newMoveAfter = TryAdvanceInsertionPoint(moveAfter);
- case BBJ_THROW:
- case BBJ_RETURN:
- /* those are exits from the loop */
- exit = loopBlock;
- exitCount++;
- break;
+ if (newMoveAfter == nullptr)
+ {
+ // Ran out of candidate insertion points, so just split up the fall-through.
+ return moveAfter;
+ }
- case BBJ_SWITCH:
+ moveAfter = newMoveAfter;
+ }
- unsigned jumpCnt;
- jumpCnt = loopBlock->bbJumpSwt->bbsCount;
- BasicBlock** jumpTab;
- jumpTab = loopBlock->bbJumpSwt->bbsDstTab;
+ return moveAfter;
+ }
- do
- {
- noway_assert(*jumpTab);
- exitPoint = *jumpTab;
+ //------------------------------------------------------------------------
+ // TryAdvanceInsertionPoint: Find the next legal insertion point after
+ // the given one, if one exists.
+ //
+ // Arguments:
+ // oldMoveAfter - Prior insertion point; find the next after this.
+ //
+ // Return Value:
+ // The next block after `oldMoveAfter` that is a legal insertion point
+ // (i.e. blocks being swept out of the loop can be moved immediately
+ // after it), if one exists, else nullptr.
+ //
+ BasicBlock* TryAdvanceInsertionPoint(BasicBlock* oldMoveAfter)
+ {
+ BasicBlock* newMoveAfter = oldMoveAfter->bbNext;
- if (exitPoint->bbNum < top->bbNum || exitPoint->bbNum > bottom->bbNum)
- {
- exit = loopBlock;
- exitCount++;
- }
- } while (++jumpTab, --jumpCnt);
- break;
+ if (!BasicBlock::sameEHRegion(oldMoveAfter, newMoveAfter))
+ {
+ // Don't cross an EH region boundary.
+ return nullptr;
+ }
- default:
- noway_assert(!"Unexpected bbJumpKind");
- break;
- }
- }
+ if ((newMoveAfter->bbJumpKind == BBJ_ALWAYS) || (newMoveAfter->bbJumpKind == BBJ_COND))
+ {
+ unsigned int destNum = newMoveAfter->bbJumpDest->bbNum;
+ if ((destNum >= top->bbNum) && (destNum <= bottom->bbNum) && !loopBlocks.IsMember(destNum))
+ {
+ // Reversing this branch out of block `newMoveAfter` could confuse this algorithm
+ // (in particular, the edge would still be numerically backwards but no longer be
+ // lexically backwards, so a lexical forward walk from TOP would not find BOTTOM),
+ // so don't do that.
+ // We're checking for BBJ_ALWAYS and BBJ_COND only here -- we don't need to
+ // check for BBJ_SWITCH because we'd never consider it a loop back-edge.
+ return nullptr;
+ }
+ }
- /* Make sure we can iterate the loop (i.e. there is a way back to ENTRY)
- * This is to ensure condition 1. above which prevents marking fake loops
- *
- * Below is an example:
- * for (....)
- * {
- * ...
- * computations
- * ...
- * break;
- * }
- * The example above is not a loop since we bail after the first iteration
- *
- * The condition we have to check for is
- * 1. ENTRY must have at least one predecessor inside the loop. Since we know that that block is
- * reachable, it can only be reached through ENTRY, therefore we have a way back to ENTRY
- *
- * 2. If we have a GOTO (BBJ_ALWAYS) outside of the loop and that block dominates the
- * loop bottom then we cannot iterate
- *
- * NOTE that this doesn't entirely satisfy condition 1. since "break" statements are not
- * part of the loop nodes (as per definition they are loop exits executed only once),
- * but we have no choice but to include them because we consider all blocks within TOP-BOTTOM */
-
- for (loopBlock = top; loopBlock != bottom; loopBlock = loopBlock->bbNext)
- {
- switch (loopBlock->bbJumpKind)
- {
- case BBJ_ALWAYS:
- case BBJ_THROW:
- case BBJ_RETURN:
- if (fgDominate(loopBlock, bottom))
- {
- goto NO_LOOP;
- }
- default:
- break;
- }
- }
+ // Similarly check to see if advancing to `newMoveAfter` would reverse the lexical order
+ // of an edge from the run of blocks being moved to `newMoveAfter` -- doing so would
+ // introduce a new lexical back-edge, which could (maybe?) confuse the loop search
+ // algorithm, and isn't desirable layout anyway.
+ for (flowList* predIter = newMoveAfter->bbPreds; predIter != nullptr; predIter = predIter->flNext)
+ {
+ unsigned int predNum = predIter->flBlock->bbNum;
- bool canIterateLoop = false;
+ if ((predNum >= top->bbNum) && (predNum <= bottom->bbNum) && !loopBlocks.IsMember(predNum))
+ {
+ // Don't make this forward edge a backwards edge.
+ return nullptr;
+ }
+ }
- for (predEntry = entry->bbPreds; predEntry; predEntry = predEntry->flNext)
- {
- if (predEntry->flBlock->bbNum >= top->bbNum && predEntry->flBlock->bbNum <= bottom->bbNum)
- {
- canIterateLoop = true;
- break;
- }
- else if (predEntry->flBlock != head)
- {
- // The entry block has multiple predecessors outside the loop; the 'head'
- // block isn't the only one. We only support a single 'head', so bail.
- goto NO_LOOP;
- }
- }
+ if (IsRecordedBottom(newMoveAfter))
+ {
+ // This is the BOTTOM of another loop; don't move any blocks past it, to avoid moving them
+ // out of that loop (we should have already done so when processing that loop if it were legal).
+ return nullptr;
+ }
- if (!canIterateLoop)
- {
- goto NO_LOOP;
- }
+ // Advancing the insertion point is ok, except that we can't split up any CallFinally/BBJ_ALWAYS
+ // pair, so if we've got such a pair recurse to see if we can move past the whole thing.
+ return (newMoveAfter->isBBCallAlwaysPair() ? TryAdvanceInsertionPoint(newMoveAfter) : newMoveAfter);
+ }
- /* Double check - make sure that all loop blocks except ENTRY
- * have no predecessors outside the loop - this ensures only one loop entry and prevents
- * us from considering non-loops due to incorrectly assuming that we had a back edge
- *
- * OBSERVATION:
- * Loops of the form "while (a || b)" will be treated as 2 nested loops (with the same header)
- */
+ //------------------------------------------------------------------------
+ // isOuterBottom: Determine if the given block is the BOTTOM of a previously
+ // recorded loop.
+ //
+ // Arguments:
+ // block - Block to check for BOTTOM-ness.
+ //
+ // Return Value:
+ // true - The blocks was recorded as `bottom` of some earlier-processed loop.
+ // false - No loops yet recorded have this block as their `bottom`.
+ //
+ bool IsRecordedBottom(BasicBlock* block)
+ {
+ if (block->bbNum > oldBlockMaxNum)
+ {
+ // This is a new block, which can't be an outer bottom block because we only allow old blocks
+ // as BOTTOM.
+ return false;
+ }
+ return BlockSetOps::IsMember(comp, bottomBlocks, block->bbNum);
+ }
+
+ //------------------------------------------------------------------------
+ // CanTreatAsLoopBlocks: If the given range of blocks can be treated as
+ // loop blocks, add them to loopBlockSet and return true. Otherwise,
+ // return false.
+ //
+ // Arguments:
+ // firstNonLoopBlock - First block in the run to be subsumed.
+ // lastNonLoopBlock - Last block in the run to be subsumed.
+ //
+ // Return Value:
+ // true - The blocks from `fistNonLoopBlock` to `lastNonLoopBlock` were
+ // successfully added to `loopBlocks`.
+ // false - Treating the blocks from `fistNonLoopBlock` to `lastNonLoopBlock`
+ // would not be legal (it would induce a side-entry).
+ //
+ // Notes:
+ // `loopBlocks` may be modified even if `false` is returned.
+ // `exitCount` and `lastExit` may be modified if this process identifies
+ // in-loop edges that were previously counted as exits.
+ //
+ bool CanTreatAsLoopBlocks(BasicBlock* firstNonLoopBlock, BasicBlock* lastNonLoopBlock)
+ {
+ BasicBlock* nextLoopBlock = lastNonLoopBlock->bbNext;
+ for (BasicBlock* testBlock = firstNonLoopBlock; testBlock != nextLoopBlock; testBlock = testBlock->bbNext)
+ {
+ for (flowList* predIter = testBlock->bbPreds; predIter != nullptr; predIter = predIter->flNext)
+ {
+ BasicBlock* testPred = predIter->flBlock;
+ unsigned int predPosNum = PositionNum(testPred);
+ unsigned int firstNonLoopPosNum = PositionNum(firstNonLoopBlock);
+ unsigned int lastNonLoopPosNum = PositionNum(lastNonLoopBlock);
- for (loopBlock = top; loopBlock != bottom->bbNext; loopBlock = loopBlock->bbNext)
+ if (loopBlocks.IsMember(predPosNum) ||
+ ((predPosNum >= firstNonLoopPosNum) && (predPosNum <= lastNonLoopPosNum)))
{
- if (loopBlock == entry)
- {
- continue;
- }
+ // This pred is in the loop (or what will be the loop if we determine this
+ // run of exit blocks doesn't include a side-entry).
- for (predTop = loopBlock->bbPreds; predTop != nullptr; predTop = predTop->flNext)
+ if (predPosNum < firstNonLoopPosNum)
{
- if (predTop->flBlock->bbNum < top->bbNum || predTop->flBlock->bbNum > bottom->bbNum)
+ // We've already counted this block as an exit, so decrement the count.
+ --exitCount;
+ if (lastExit == testPred)
{
- // noway_assert(!"Found loop with multiple entries");
- goto NO_LOOP;
+ // Erase this now-bogus `lastExit` entry.
+ lastExit = nullptr;
+ INDEBUG(forgotExit = true);
}
}
}
+ else
+ {
+ // This pred is not in the loop, so this constitutes a side-entry.
+ return false;
+ }
+ }
- // Disqualify loops where the first block of the loop is less nested in EH than
- // the bottom block. That is, we don't want to handle loops where the back edge
- // goes from within an EH region to a first block that is outside that same EH
- // region. Note that we *do* handle loops where the first block is the *first*
- // block of a more nested EH region (since it is legal to branch to the first
- // block of an immediately more nested EH region). So, for example, disqualify
- // this:
- //
- // BB02
- // ...
- // try {
- // ...
- // BB10 BBJ_COND => BB02
- // ...
- // }
- //
- // Here, BB10 is more nested than BB02.
+ // Either we're going to abort the loop on a subsequent testBlock, or this
+ // testBlock is part of the loop.
+ loopBlocks.Insert(testBlock->bbNum);
+ }
- if (bottom->hasTryIndex() && !bbInTryRegions(bottom->getTryIndex(), first))
+ // All blocks were ok to leave in the loop.
+ return true;
+ }
+
+ //------------------------------------------------------------------------
+ // FixupFallThrough: Re-establish any broken control flow connectivity
+ // and eliminate any "goto-next"s that were created by changing the
+ // given block's lexical follower.
+ //
+ // Arguments:
+ // block - Block whose `bbNext` has changed.
+ // oldNext - Previous value of `block->bbNext`.
+ // newNext - New value of `block->bbNext`.
+ //
+ // Return Value:
+ // If a new block is created to reconnect flow, the new block is
+ // returned; otherwise, nullptr.
+ //
+ BasicBlock* FixupFallThrough(BasicBlock* block, BasicBlock* oldNext, BasicBlock* newNext)
+ {
+ // If we create a new block, that will be our return value.
+ BasicBlock* newBlock = nullptr;
+
+ if (block->bbFallsThrough())
+ {
+ // Need to reconnect the flow from `block` to `oldNext`.
+
+ if ((block->bbJumpKind == BBJ_COND) && (block->bbJumpDest == newNext))
+ {
+ /* Reverse the jump condition */
+ GenTree* test = block->lastNode();
+ noway_assert(test->OperIsConditionalJump());
+
+ if (test->OperGet() == GT_JTRUE)
{
- JITDUMP("Loop 'first' BB%02u is in an outer EH region compared to loop 'bottom' BB%02u. Rejecting "
- "loop.\n",
- first->bbNum, bottom->bbNum);
- goto NO_LOOP;
+ GenTree* cond = comp->gtReverseCond(test->gtOp.gtOp1);
+ assert(cond == test->gtOp.gtOp1); // Ensure `gtReverseCond` did not create a new node.
+ test->gtOp.gtOp1 = cond;
}
-
-#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
- // Disqualify loops where the first block of the loop is a finally target.
- // The main problem is when multiple loops share a 'first' block that is a finally
- // target and we canonicalize the loops by adding a new loop head. In that case, we
- // need to update the blocks so the finally target bit is moved to the newly created
- // block, and removed from the old 'first' block. This is 'hard', so at this point
- // in the RyuJIT codebase (when we don't expect to keep the "old" ARM32 code generator
- // long-term), it's easier to disallow the loop than to update the flow graph to
- // support this case.
-
- if ((first->bbFlags & BBF_FINALLY_TARGET) != 0)
+ else
{
- JITDUMP("Loop 'first' BB%02u is a finally target. Rejecting loop.\n", first->bbNum);
- goto NO_LOOP;
+ comp->gtReverseCond(test);
}
-#endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
- /* At this point we have a loop - record it in the loop table
- * If we found only one exit, record it in the table too
- * (otherwise an exit = 0 in the loop table means multiple exits) */
+ // Redirect the Conditional JUMP to go to `oldNext`
+ block->bbJumpDest = oldNext;
+ }
+ else
+ {
+ // Insert an unconditional jump to `oldNext` just after `block`.
+ newBlock = comp->fgConnectFallThrough(block, oldNext);
+ noway_assert((newBlock == nullptr) || loopBlocks.CanRepresent(newBlock->bbNum));
+ }
+ }
+ else if ((block->bbJumpKind == BBJ_ALWAYS) && (block->bbJumpDest == newNext))
+ {
+ // We've made `block`'s jump target its bbNext, so remove the jump.
+ if (!comp->fgOptimizeBranchToNext(block, newNext, block->bbPrev))
+ {
+ // If optimizing away the goto-next failed for some reason, mark it KEEP_BBJ_ALWAYS to
+ // prevent assertions from complaining about it.
+ block->bbFlags |= BBF_KEEP_BBJ_ALWAYS;
+ }
+ }
+
+ // Make sure we don't leave around a goto-next unless it's marked KEEP_BBJ_ALWAYS.
+ assert((block->bbJumpKind != BBJ_COND) || (block->bbJumpKind != BBJ_ALWAYS) || (block->bbJumpDest != newNext) ||
+ ((block->bbFlags & BBF_KEEP_BBJ_ALWAYS) != 0));
+ return newBlock;
+ }
+
+ //------------------------------------------------------------------------
+ // CheckForExit: Check if the given block has any successor edges that are
+ // loop exits, and update `lastExit` and `exitCount` if so.
+ //
+ // Arguments:
+ // block - Block whose successor edges are to be checked.
+ //
+ // Notes:
+ // If one block has multiple exiting successor edges, those are counted
+ // as multiple exits in `exitCount`.
+ //
+ void CheckForExit(BasicBlock* block)
+ {
+ BasicBlock* exitPoint;
+
+ switch (block->bbJumpKind)
+ {
+ case BBJ_COND:
+ case BBJ_CALLFINALLY:
+ case BBJ_ALWAYS:
+ case BBJ_EHCATCHRET:
+ assert(block->bbJumpDest);
+ exitPoint = block->bbJumpDest;
- assert(pred);
- if (exitCount != 1)
+ if (!loopBlocks.IsMember(exitPoint->bbNum))
{
- exit = nullptr;
+ /* exit from a block other than BOTTOM */
+ lastExit = block;
+ exitCount++;
}
- optRecordLoop(head, first, top, entry, bottom, exit, exitCount);
+ break;
+
+ case BBJ_NONE:
+ break;
+
+ case BBJ_EHFINALLYRET:
+ case BBJ_EHFILTERRET:
+ /* The "try" associated with this "finally" must be in the
+ * same loop, so the finally block will return control inside the loop */
+ break;
+
+ case BBJ_THROW:
+ case BBJ_RETURN:
+ /* those are exits from the loop */
+ lastExit = block;
+ exitCount++;
+ break;
+
+ case BBJ_SWITCH:
+
+ unsigned jumpCnt;
+ jumpCnt = block->bbJumpSwt->bbsCount;
+ BasicBlock** jumpTab;
+ jumpTab = block->bbJumpSwt->bbsDstTab;
+
+ do
+ {
+ noway_assert(*jumpTab);
+ exitPoint = *jumpTab;
+
+ if (!loopBlocks.IsMember(exitPoint->bbNum))
+ {
+ lastExit = block;
+ exitCount++;
+ }
+ } while (++jumpTab, --jumpCnt);
+ break;
+
+ default:
+ noway_assert(!"Unexpected bbJumpKind");
+ break;
+ }
+
+ if (block->bbFallsThrough() && !loopBlocks.IsMember(block->bbNext->bbNum))
+ {
+ // Found a fall-through exit.
+ lastExit = block;
+ exitCount++;
+ }
+ }
+};
+}
+
+/*****************************************************************************
+ * Find the natural loops, using dominators. Note that the test for
+ * a loop is slightly different from the standard one, because we have
+ * not done a depth first reordering of the basic blocks.
+ */
+
+void Compiler::optFindNaturalLoops()
+{
+#ifdef DEBUG
+ if (verbose)
+ {
+ printf("*************** In optFindNaturalLoops()\n");
+ }
+#endif // DEBUG
+
+ noway_assert(fgDomsComputed);
+ assert(fgHasLoops);
+
+#if COUNT_LOOPS
+ hasMethodLoops = false;
+ loopsThisMethod = 0;
+ loopOverflowThisMethod = false;
+#endif
+
+ LoopSearch search(this);
+
+ for (BasicBlock* head = fgFirstBB; head->bbNext; head = head->bbNext)
+ {
+ BasicBlock* top = head->bbNext;
+
+ // Blocks that are rarely run have a zero bbWeight and should
+ // never be optimized here
+
+ if (top->bbWeight == BB_ZERO_WEIGHT)
+ {
+ continue;
+ }
+
+ for (flowList* pred = top->bbPreds; pred; pred = pred->flNext)
+ {
+ if (search.FindLoop(head, top, pred->flBlock))
+ {
+ // Found a loop; record it and see if we've hit the limit.
+ bool recordedLoop = search.RecordLoop();
+
+ (void)recordedLoop; // avoid unusued variable warnings in COUNT_LOOPS and !DEBUG
#if COUNT_LOOPS
if (!hasMethodLoops)
@@ -1855,13 +2474,26 @@ void Compiler::optFindNaturalLoops()
/* keep track of the number of exits */
loopExitCountTable.record(static_cast<unsigned>(exitCount));
+#else // COUNT_LOOPS
+ assert(recordedLoop);
+ if (optLoopCount == MAX_LOOP_NUM)
+ {
+ // We won't be able to record any more loops, so stop looking.
+ goto NO_MORE_LOOPS;
+ }
#endif // COUNT_LOOPS
- }
- /* current predecessor not good for a loop - continue with another one, if any */
- NO_LOOP:;
+ // Continue searching preds of `top` to see if any other are
+ // back-edges (this can happen for nested loops). The iteration
+ // is safe because the compaction we do only modifies predecessor
+ // lists of blocks that gain or lose fall-through from their
+ // `bbPrev`, but since the motion is from within the loop to below
+ // it, we know we're not altering the relationship between `top`
+ // and its `bbPrev`.
+ }
}
}
+NO_MORE_LOOPS:
#if COUNT_LOOPS
loopCountTable.record(loopsThisMethod);
@@ -1910,9 +2542,10 @@ void Compiler::optFindNaturalLoops()
}
}
+ bool mod = search.ChangedFlowGraph();
+
// Make sure that loops are canonical: that every loop has a unique "top", by creating an empty "nop"
// one, if necessary, for loops containing others that share a "top."
- bool mod = false;
for (unsigned char loopInd = 0; loopInd < optLoopCount; loopInd++)
{
// Traverse the outermost loops as entries into the loop nest; so skip non-outermost.
@@ -4288,8 +4921,7 @@ void Compiler::optPerformStaticOptimizations(unsigned loopNum, LoopCloneContext*
{
LcJaggedArrayOptInfo* arrIndexInfo = optInfo->AsLcJaggedArrayOptInfo();
compCurBB = arrIndexInfo->arrIndex.useBlock;
- optRemoveRangeCheck(arrIndexInfo->arrIndex.bndsChks[arrIndexInfo->dim], arrIndexInfo->stmt, true,
- GTF_ASG, true);
+ optRemoveRangeCheck(arrIndexInfo->arrIndex.bndsChks[arrIndexInfo->dim], arrIndexInfo->stmt);
DBEXEC(dynamicPath, optDebugLogLoopCloning(arrIndexInfo->arrIndex.useBlock, arrIndexInfo->stmt));
}
break;
@@ -7291,35 +7923,25 @@ void Compiler::optRemoveTree(GenTreePtr deadTree, GenTreePtr keepList)
fgWalkTreePre(&deadTree, optRemoveTreeVisitor, (void*)keepList);
}
-/*****************************************************************************
- *
- * Given an array index node, mark it as not needing a range check.
- */
+//------------------------------------------------------------------------------
+// optRemoveRangeCheck : Given an array index node, mark it as not needing a range check.
+//
+// Arguments:
+// tree - Range check tree
+// stmt - Statement the tree belongs to
-void Compiler::optRemoveRangeCheck(
- GenTreePtr tree, GenTreePtr stmt, bool updateCSEcounts, unsigned sideEffFlags, bool forceRemove)
+void Compiler::optRemoveRangeCheck(GenTreePtr tree, GenTreePtr stmt)
{
- GenTreePtr add1;
- GenTreePtr* addp;
-
- GenTreePtr nop1;
- GenTreePtr* nopp;
-
- GenTreePtr icon;
- GenTreePtr mult;
-
- GenTreePtr base;
-
- ssize_t ival;
-
#if !REARRANGE_ADDS
noway_assert(!"can't remove range checks without REARRANGE_ADDS right now");
#endif
noway_assert(stmt->gtOper == GT_STMT);
noway_assert(tree->gtOper == GT_COMMA);
- noway_assert(tree->gtOp.gtOp1->OperIsBoundsCheck());
- noway_assert(forceRemove || optIsRangeCheckRemovable(tree->gtOp.gtOp1));
+
+ GenTree* bndsChkTree = tree->gtOp.gtOp1;
+
+ noway_assert(bndsChkTree->OperIsBoundsCheck());
GenTreeBoundsChk* bndsChk = tree->gtOp.gtOp1->AsBoundsChk();
@@ -7332,21 +7954,20 @@ void Compiler::optRemoveRangeCheck(
#endif
GenTreePtr sideEffList = nullptr;
- if (sideEffFlags)
- {
- gtExtractSideEffList(tree->gtOp.gtOp1, &sideEffList, sideEffFlags);
- }
+
+ gtExtractSideEffList(bndsChkTree, &sideEffList, GTF_ASG);
// Decrement the ref counts for any LclVars that are being deleted
//
- optRemoveTree(tree->gtOp.gtOp1, sideEffList);
+ optRemoveTree(bndsChkTree, sideEffList);
// Just replace the bndsChk with a NOP as an operand to the GT_COMMA, if there are no side effects.
tree->gtOp.gtOp1 = (sideEffList != nullptr) ? sideEffList : gtNewNothingNode();
-
// TODO-CQ: We should also remove the GT_COMMA, but in any case we can no longer CSE the GT_COMMA.
tree->gtFlags |= GTF_DONT_CSE;
+ gtUpdateSideEffects(stmt, tree);
+
/* Recalculate the gtCostSz, etc... */
gtSetStmtInfo(stmt);
@@ -7618,8 +8239,11 @@ bool Compiler::optIdentifyLoopOptInfo(unsigned loopNum, LoopCloneContext* contex
compCurBB = block;
for (GenTreePtr stmt = block->bbTreeList; stmt; stmt = stmt->gtNext)
{
- info.stmt = stmt;
- fgWalkTreePre(&stmt->gtStmt.gtStmtExpr, optCanOptimizeByLoopCloningVisitor, &info, false, false);
+ info.stmt = stmt;
+ const bool lclVarsOnly = false;
+ const bool computeStack = false;
+ fgWalkTreePre(&stmt->gtStmt.gtStmtExpr, optCanOptimizeByLoopCloningVisitor, &info, lclVarsOnly,
+ computeStack);
}
}
@@ -8285,7 +8909,7 @@ void Compiler::optOptimizeBools()
B2: brtrue(t2, BX)
B3:
we will try to fold it to :
- B1: brtrue((!t1)&&t2, B3)
+ B1: brtrue((!t1)&&t2, BX)
B3:
*/
diff --git a/src/jit/rangecheck.cpp b/src/jit/rangecheck.cpp
index 427bab7c2a..803758095d 100644
--- a/src/jit/rangecheck.cpp
+++ b/src/jit/rangecheck.cpp
@@ -247,7 +247,7 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, GenTreePtr stmt, GenTreeP
if (arrSize > 0 && idxVal < arrSize && idxVal >= 0)
{
JITDUMP("Removing range check\n");
- m_pCompiler->optRemoveRangeCheck(treeParent, stmt, true, GTF_ASG, true /* force remove */);
+ m_pCompiler->optRemoveRangeCheck(treeParent, stmt);
return;
}
}
@@ -289,7 +289,7 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, GenTreePtr stmt, GenTreeP
if (BetweenBounds(range, 0, bndsChk->gtArrLen))
{
JITDUMP("[RangeCheck::OptimizeRangeCheck] Between bounds\n");
- m_pCompiler->optRemoveRangeCheck(treeParent, stmt, true, GTF_ASG, true /* force remove */);
+ m_pCompiler->optRemoveRangeCheck(treeParent, stmt);
}
return;
}
@@ -1117,15 +1117,6 @@ bool RangeCheck::ComputeDoesOverflow(BasicBlock* block, GenTreePtr stmt, GenTree
return overflows;
}
-struct Node
-{
- Range range;
- Node* next;
- Node() : range(Limit(Limit::keUndef)), next(nullptr)
- {
- }
-};
-
// Compute the range recursively by asking for the range of each variable in the dependency chain.
// eg.: c = a + b; ask range of "a" and "b" and add the results.
// If the result cannot be determined i.e., the dependency chain does not terminate in a value,
@@ -1195,40 +1186,24 @@ Range RangeCheck::ComputeRange(
// If phi, then compute the range for arguments, calling the result "dependent" when looping begins.
else if (expr->OperGet() == GT_PHI)
{
- Node* cur = nullptr;
- Node* head = nullptr;
+ Range argRange = Range(Limit(Limit::keUndef));
for (GenTreeArgList* args = expr->gtOp.gtOp1->AsArgList(); args != nullptr; args = args->Rest())
{
- // Collect the range for each phi argument in a linked list.
- Node* node = new (m_pCompiler->getAllocator()) Node();
- if (cur != nullptr)
+ if (path->Lookup(args->Current()))
{
- cur->next = node;
- cur = cur->next;
+ JITDUMP("PhiArg [%06d] is already being computed\n", Compiler::dspTreeID(args->Current()));
+ argRange = Range(Limit(Limit::keDependent));
}
else
{
- head = node;
- cur = head;
+ argRange = GetRange(block, stmt, args->Current(), path, monotonic DEBUGARG(indent + 1));
}
- if (path->Lookup(args->Current()))
- {
- JITDUMP("PhiArg [%06d] is already being computed\n", Compiler::dspTreeID(args->Current()));
- cur->range = Range(Limit(Limit::keDependent));
- MergeAssertion(block, stmt, args->Current(), path, &cur->range DEBUGARG(indent + 1));
- continue;
- }
- cur->range = GetRange(block, stmt, args->Current(), path, monotonic DEBUGARG(indent + 1));
- MergeAssertion(block, stmt, args->Current(), path, &cur->range DEBUGARG(indent + 1));
- }
- // Walk the linked list and merge the ranges.
- for (cur = head; cur; cur = cur->next)
- {
- assert(!cur->range.LowerLimit().IsUndef());
- assert(!cur->range.UpperLimit().IsUndef());
+ assert(!argRange.LowerLimit().IsUndef());
+ assert(!argRange.UpperLimit().IsUndef());
+ MergeAssertion(block, stmt, args->Current(), path, &argRange DEBUGARG(indent + 1));
JITDUMP("Merging ranges %s %s:", range.ToString(m_pCompiler->getAllocatorDebugOnly()),
- cur->range.ToString(m_pCompiler->getAllocatorDebugOnly()));
- range = RangeOps::Merge(range, cur->range, monotonic);
+ argRange.ToString(m_pCompiler->getAllocatorDebugOnly()));
+ range = RangeOps::Merge(range, argRange, monotonic);
JITDUMP("%s\n", range.ToString(m_pCompiler->getAllocatorDebugOnly()));
}
}
diff --git a/src/jit/rationalize.cpp b/src/jit/rationalize.cpp
index 23d4f99fb8..257e02d491 100644
--- a/src/jit/rationalize.cpp
+++ b/src/jit/rationalize.cpp
@@ -516,6 +516,7 @@ void Rationalizer::RewriteAssignment(LIR::Use& use)
location->gtType = TYP_BYREF;
assignment->SetOper(GT_STOREIND);
+ assignment->AsStoreInd()->SetRMWStatusDefault();
// TODO: JIT dump
}
@@ -731,6 +732,7 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, ArrayStack<G
{
use.ReplaceWith(comp, node->gtGetOp1());
BlockRange().Remove(node);
+ node = node->gtGetOp1();
}
break;
diff --git a/src/jit/regalloc.cpp b/src/jit/regalloc.cpp
index 8280503795..a9119945d9 100644
--- a/src/jit/regalloc.cpp
+++ b/src/jit/regalloc.cpp
@@ -4835,6 +4835,17 @@ regMaskTP Compiler::rpPredictTreeRegUse(GenTreePtr tree,
}
assert(list == NULL);
+#ifdef LEGACY_BACKEND
+#if CPU_LOAD_STORE_ARCH
+#ifdef FEATURE_READYTORUN_COMPILER
+ if (tree->gtCall.IsR2RRelativeIndir())
+ {
+ tree->gtUsedRegs |= RBM_R2R_INDIRECT_PARAM;
+ }
+#endif // FEATURE_READYTORUN_COMPILER
+#endif // CPU_LOAD_STORE_ARCH
+#endif // LEGACY_BACKEND
+
regMaskTP callAddrMask;
callAddrMask = RBM_NONE;
#if CPU_LOAD_STORE_ARCH
@@ -6256,23 +6267,13 @@ void Compiler::rpPredictRegUse()
mustPredict |= rpLostEnreg;
#ifdef _TARGET_ARM_
-
// See if we previously reserved REG_R10 and try to make it available if we have a small frame now
- //
- if ((rpPasses == 0) && (codeGen->regSet.rsMaskResvd & RBM_OPT_RSVD))
+ if ((rpPasses == 0) && ((codeGen->regSet.rsMaskResvd & RBM_OPT_RSVD) != 0) &&
+ !compRsvdRegCheck(REGALLOC_FRAME_LAYOUT))
{
- if (compRsvdRegCheck(REGALLOC_FRAME_LAYOUT))
- {
- // We must keep reserving R10 in this case
- codeGen->regSet.rsMaskResvd |= RBM_OPT_RSVD;
- }
- else
- {
- // We can release our reservation on R10 and use it to color registers
- //
- codeGen->regSet.rsMaskResvd &= ~RBM_OPT_RSVD;
- allAcceptableRegs |= RBM_OPT_RSVD;
- }
+ // We can release our reservation on R10 and use it to color registers
+ codeGen->regSet.rsMaskResvd &= ~RBM_OPT_RSVD;
+ allAcceptableRegs |= RBM_OPT_RSVD;
}
#endif
@@ -6470,6 +6471,21 @@ void Compiler::rpPredictRegUse()
/* Decide whether we need to set mustPredict */
mustPredict = false;
+#ifdef _TARGET_ARM_
+ // The spill count may be now high enough that we now need to reserve r10. If this is the case, we'll need to
+ // reserve r10, and if it was used, throw out the last prediction and repredict.
+ if (((codeGen->regSet.rsMaskResvd & RBM_OPT_RSVD) == 0) && compRsvdRegCheck(REGALLOC_FRAME_LAYOUT))
+ {
+ codeGen->regSet.rsMaskResvd |= RBM_OPT_RSVD;
+ allAcceptableRegs &= ~RBM_OPT_RSVD;
+ if ((regUsed & RBM_OPT_RSVD) != 0)
+ {
+ mustPredict = true;
+ rpBestRecordedPrediction = nullptr;
+ }
+ }
+#endif
+
if (rpAddedVarIntf)
{
mustPredict = true;
diff --git a/src/jit/regset.cpp b/src/jit/regset.cpp
index 720466348c..44312dab42 100644
--- a/src/jit/regset.cpp
+++ b/src/jit/regset.cpp
@@ -869,7 +869,7 @@ void RegSet::rsMarkRegUsed(GenTreePtr tree, GenTreePtr addr)
else
printf(" / Constant(0x%X)", tree->gtIntCon.gtIconVal);
}
- printf("]\n");
+ printf("\n");
}
#endif // DEBUG
@@ -927,7 +927,7 @@ void RegSet::rsMarkArgRegUsedByPromotedFieldArg(GenTreePtr promotedStructArg, re
else
printf(" / Constant(0x%X)", promotedStructArg->gtIntCon.gtIconVal);
}
- printf("]\n");
+ printf("\n");
}
#endif
@@ -990,10 +990,10 @@ void RegSet::rsMarkRegPairUsed(GenTreePtr tree)
#ifdef DEBUG
if (m_rsCompiler->verbose)
{
- printf("\t\t\t\t\t\t\tThe register %s currently holds \n", m_rsCompiler->compRegVarName(regLo));
+ printf("\t\t\t\t\t\t\tThe register %s currently holds ", m_rsCompiler->compRegVarName(regLo));
Compiler::printTreeID(tree);
printf("/lo32\n");
- printf("\t\t\t\t\t\t\tThe register %s currently holds \n", m_rsCompiler->compRegVarName(regHi));
+ printf("\t\t\t\t\t\t\tThe register %s currently holds ", m_rsCompiler->compRegVarName(regHi));
Compiler::printTreeID(tree);
printf("/hi32\n");
}
@@ -1167,7 +1167,11 @@ void RegSet::rsMarkRegFree(regMaskTP regMask)
{
printf("\t\t\t\t\t\t\tThe register %s no longer holds ", m_rsCompiler->compRegVarName(regNum));
Compiler::printTreeID(rsUsedTree[regNum]);
- Compiler::printTreeID(rsUsedAddr[regNum]);
+ if (rsUsedAddr[regNum] != nullptr)
+ {
+ Compiler::printTreeID(rsUsedAddr[regNum]);
+ }
+
printf("\n");
}
#endif
@@ -1529,6 +1533,7 @@ void RegSet::rsSpillTree(regNumber reg, GenTreePtr tree, unsigned regIdx /* =0 *
var_types treeType;
#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
GenTreePutArgSplit* splitArg = nullptr;
+ GenTreeMultiRegOp* multiReg = nullptr;
#endif
#ifndef LEGACY_BACKEND
@@ -1544,6 +1549,11 @@ void RegSet::rsSpillTree(regNumber reg, GenTreePtr tree, unsigned regIdx /* =0 *
splitArg = tree->AsPutArgSplit();
treeType = splitArg->GetRegType(regIdx);
}
+ else if (tree->OperIsMultiRegOp())
+ {
+ multiReg = tree->AsMultiRegOp();
+ treeType = multiReg->GetRegType(regIdx);
+ }
#endif // _TARGET_ARM_
else
#endif // !LEGACY_BACKEND
@@ -1601,6 +1611,12 @@ void RegSet::rsSpillTree(regNumber reg, GenTreePtr tree, unsigned regIdx /* =0 *
assert((regFlags & GTF_SPILL) != 0);
regFlags &= ~GTF_SPILL;
}
+ else if (multiReg != nullptr)
+ {
+ regFlags = multiReg->GetRegSpillFlagByIdx(regIdx);
+ assert((regFlags & GTF_SPILL) != 0);
+ regFlags &= ~GTF_SPILL;
+ }
#endif // _TARGET_ARM_
else
{
@@ -1623,7 +1639,8 @@ void RegSet::rsSpillTree(regNumber reg, GenTreePtr tree, unsigned regIdx /* =0 *
}
#elif defined(_TARGET_ARM_)
assert(tree->gtRegNum == reg || (call != nullptr && call->GetRegNumByIdx(regIdx) == reg) ||
- (splitArg != nullptr && splitArg->GetRegNumByIdx(regIdx) == reg));
+ (splitArg != nullptr && splitArg->GetRegNumByIdx(regIdx) == reg) ||
+ (multiReg != nullptr && multiReg->GetRegNumByIdx(regIdx) == reg));
#else
assert(tree->gtRegNum == reg || (call != nullptr && call->GetRegNumByIdx(regIdx) == reg));
#endif // !CPU_LONG_USES_REGPAIR && !_TARGET_ARM_
@@ -1648,8 +1665,10 @@ void RegSet::rsSpillTree(regNumber reg, GenTreePtr tree, unsigned regIdx /* =0 *
printf("\t\t\t\t\t\t\tThe register %s spilled with ", m_rsCompiler->compRegVarName(reg));
Compiler::printTreeID(spill->spillTree);
#ifdef LEGACY_BACKEND
- printf("/");
- Compiler::printTreeID(spill->spillAddr);
+ if (spill->spillAddr != nullptr)
+ {
+ Compiler::printTreeID(spill->spillAddr);
+ }
#endif // LEGACY_BACKEND
}
#endif
@@ -1753,6 +1772,11 @@ void RegSet::rsSpillTree(regNumber reg, GenTreePtr tree, unsigned regIdx /* =0 *
regFlags |= GTF_SPILLED;
splitArg->SetRegSpillFlagByIdx(regFlags, regIdx);
}
+ else if (multiReg != nullptr)
+ {
+ regFlags |= GTF_SPILLED;
+ multiReg->SetRegSpillFlagByIdx(regFlags, regIdx);
+ }
#endif // _TARGET_ARM_
#endif //! LEGACY_BACKEND
}
@@ -2336,9 +2360,13 @@ regNumber RegSet::rsUnspillOneReg(GenTreePtr tree, regNumber oldReg, KeepReg wil
rsMaskMult |= genRegMask(newReg);
}
- /* Free the temp, it's no longer used */
-
- m_rsCompiler->tmpRlsTemp(temp);
+ if (!multiUsed || (willKeepNewReg == KEEP_REG))
+ {
+ // Free the temp, it's no longer used.
+ // For multi-used regs that aren't (willKeepNewReg == KEEP_REG), we didn't unspill everything, so
+ // we need to leave the temp for future unspilling.
+ m_rsCompiler->tmpRlsTemp(temp);
+ }
return newReg;
}
@@ -2391,6 +2419,13 @@ TempDsc* RegSet::rsUnspillInPlace(GenTreePtr tree, regNumber oldReg, unsigned re
flags &= ~GTF_SPILLED;
splitArg->SetRegSpillFlagByIdx(flags, regIdx);
}
+ else if (tree->OperIsMultiRegOp())
+ {
+ GenTreeMultiRegOp* multiReg = tree->AsMultiRegOp();
+ unsigned flags = multiReg->GetRegSpillFlagByIdx(regIdx);
+ flags &= ~GTF_SPILLED;
+ multiReg->SetRegSpillFlagByIdx(flags, regIdx);
+ }
#endif // !LEGACY_BACKEND && _TARGET_ARM_
else
{
@@ -2666,17 +2701,24 @@ void RegSet::rsUnspillRegPair(GenTreePtr tree, regMaskTP needReg, KeepReg keepRe
if (rsIsTreeInReg(regHi, tree))
{
- /* Temporarily lock the high part */
-
- rsLockUsedReg(genRegMask(regHi));
+ // Temporarily lock the high part if necessary. If this register is a multi-use register that is shared
+ // with another tree, the register may already be locked.
+ const regMaskTP regHiMask = genRegMask(regHi);
+ const bool lockReg = (rsMaskLock & regHiMask) == 0;
+ if (lockReg)
+ {
+ rsLockUsedReg(regHiMask);
+ }
/* Pick a new home for the lower half */
regLo = rsUnspillOneReg(tree, regLo, keepReg, needReg);
/* We can unlock the high part now */
-
- rsUnlockUsedReg(genRegMask(regHi));
+ if (lockReg)
+ {
+ rsUnlockUsedReg(regHiMask);
+ }
}
else
{
@@ -2698,19 +2740,26 @@ void RegSet::rsUnspillRegPair(GenTreePtr tree, regMaskTP needReg, KeepReg keepRe
if (!rsIsTreeInReg(regHi, tree))
{
- regMaskTP regLoUsed;
+ regMaskTP regLoUsed = RBM_NONE;
- /* Temporarily lock the low part so it doesnt get spilled */
-
- rsLockReg(genRegMask(regLo), &regLoUsed);
+ // Temporarily lock the low part if necessary. If this register is a multi-use register that is shared
+ // with another tree, the register may already be locked.
+ const regMaskTP regLoMask = genRegMask(regLo);
+ const bool lockReg = (rsMaskLock & regLoMask) == 0;
+ if (lockReg)
+ {
+ rsLockReg(regLoMask, &regLoUsed);
+ }
/* Pick a new home for the upper half */
regHi = rsUnspillOneReg(tree, regHi, keepReg, needReg);
/* We can unlock the low register now */
-
- rsUnlockReg(genRegMask(regLo), regLoUsed);
+ if (lockReg)
+ {
+ rsUnlockReg(regLoMask, regLoUsed);
+ }
}
else
{
diff --git a/src/jit/simd.cpp b/src/jit/simd.cpp
index bbb9a57cc4..bd711dd04d 100644
--- a/src/jit/simd.cpp
+++ b/src/jit/simd.cpp
@@ -1784,10 +1784,9 @@ GenTreePtr Compiler::createAddressNodeForSIMDInit(GenTreePtr tree, unsigned simd
// The length for boundary check should be the maximum index number which should be
// (first argument's index number) + (how many array arguments we have) - 1
// = indexVal + arrayElementsCount - 1
- unsigned arrayElementsCount = simdSize / genTypeSize(baseType);
- checkIndexExpr = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, indexVal + arrayElementsCount - 1);
- GenTreeArrLen* arrLen =
- new (this, GT_ARR_LENGTH) GenTreeArrLen(TYP_INT, arrayRef, (int)offsetof(CORINFO_Array, length));
+ unsigned arrayElementsCount = simdSize / genTypeSize(baseType);
+ checkIndexExpr = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, indexVal + arrayElementsCount - 1);
+ GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, arrayRef, (int)offsetof(CORINFO_Array, length));
GenTreeBoundsChk* arrBndsChk = new (this, GT_ARR_BOUNDS_CHECK)
GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, checkIndexExpr, arrLen, SCK_RNGCHK_FAIL);
@@ -2240,8 +2239,8 @@ GenTreePtr Compiler::impSIMDIntrinsic(OPCODE opcode,
op3 = gtCloneExpr(index);
}
- GenTreeArrLen* arrLen = new (this, GT_ARR_LENGTH)
- GenTreeArrLen(TYP_INT, arrayRefForArgRngChk, (int)offsetof(CORINFO_Array, length));
+ GenTreeArrLen* arrLen =
+ gtNewArrLen(TYP_INT, arrayRefForArgRngChk, (int)offsetof(CORINFO_Array, length));
argRngChk = new (this, GT_ARR_BOUNDS_CHECK)
GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, index, arrLen, op3CheckKind);
// Now, clone op3 to create another node for the argChk
@@ -2261,8 +2260,7 @@ GenTreePtr Compiler::impSIMDIntrinsic(OPCODE opcode,
{
op2CheckKind = SCK_ARG_EXCPN;
}
- GenTreeArrLen* arrLen = new (this, GT_ARR_LENGTH)
- GenTreeArrLen(TYP_INT, arrayRefForArgChk, (int)offsetof(CORINFO_Array, length));
+ GenTreeArrLen* arrLen = gtNewArrLen(TYP_INT, arrayRefForArgChk, (int)offsetof(CORINFO_Array, length));
GenTreeBoundsChk* argChk = new (this, GT_ARR_BOUNDS_CHECK)
GenTreeBoundsChk(GT_ARR_BOUNDS_CHECK, TYP_VOID, checkIndexExpr, arrLen, op2CheckKind);
diff --git a/src/jit/ssabuilder.cpp b/src/jit/ssabuilder.cpp
index 4b9fb9f950..37adf50da7 100644
--- a/src/jit/ssabuilder.cpp
+++ b/src/jit/ssabuilder.cpp
@@ -264,7 +264,7 @@ int SsaBuilder::TopologicalSort(BasicBlock** postOrder, int count)
block->bbPostOrderNum = postIndex;
postIndex += 1;
- DBG_SSA_JITDUMP("postOrder[%d] = [%p] and BB%02u\n", postIndex, dspPtr(block), block->bbNum);
+ DBG_SSA_JITDUMP("postOrder[%d] = %s\n", postIndex, block->dspToString());
}
}
@@ -381,7 +381,7 @@ void SsaBuilder::ComputeImmediateDom(BasicBlock** postOrder, int count)
*/
void SsaBuilder::DomTreeWalk(BasicBlock* curBlock, BlkToBlkSetMap* domTree, int* preIndex, int* postIndex)
{
- JITDUMP("[SsaBuilder::DomTreeWalk] block [%p], BB%02u:\n", dspPtr(curBlock), curBlock->bbNum);
+ JITDUMP("[SsaBuilder::DomTreeWalk] block %s:\n", curBlock->dspToString());
// Store the order number at the block number in the pre order list.
m_pDomPreOrder[curBlock->bbNum] = *preIndex;
diff --git a/src/jit/ssaconfig.h b/src/jit/ssaconfig.h
index 6e81ad9fd6..8df61a232a 100644
--- a/src/jit/ssaconfig.h
+++ b/src/jit/ssaconfig.h
@@ -24,7 +24,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#ifdef DEBUG
#define DBG_SSA_JITDUMP(...) \
if (JitTls::GetCompiler()->verboseSsa) \
- JitDump(__VA_ARGS__)
+ logf(__VA_ARGS__)
#else
#define DBG_SSA_JITDUMP(...)
#endif
diff --git a/src/jit/target.h b/src/jit/target.h
index 9d7608a2bc..68b27f22f9 100644
--- a/src/jit/target.h
+++ b/src/jit/target.h
@@ -834,6 +834,13 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define RBM_FLT_CALLEE_SAVED (0)
#define RBM_FLT_CALLEE_TRASH (RBM_XMM0|RBM_XMM1|RBM_XMM2|RBM_XMM3|RBM_XMM4|RBM_XMM5|RBM_XMM6|RBM_XMM7| \
RBM_XMM8|RBM_XMM9|RBM_XMM10|RBM_XMM11|RBM_XMM12|RBM_XMM13|RBM_XMM14|RBM_XMM15)
+ #define REG_PROFILER_ENTER_ARG_0 REG_R14
+ #define RBM_PROFILER_ENTER_ARG_0 RBM_R14
+ #define REG_PROFILER_ENTER_ARG_1 REG_R15
+ #define RBM_PROFILER_ENTER_ARG_1 RBM_R15
+
+ #define REG_DEFAULT_PROFILER_CALL_TARGET REG_R11
+
#else // !UNIX_AMD64_ABI
#define MIN_ARG_AREA_FOR_CALL (4 * REGSIZE_BYTES) // Minimum required outgoing argument space for a call.
@@ -980,7 +987,7 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
// profiler.
#define REG_DEFAULT_HELPER_CALL_TARGET REG_RAX
- // GenericPInvokeCalliHelper VASigCookie Parameter
+ // GenericPInvokeCalliHelper VASigCookie Parameter
#define REG_PINVOKE_COOKIE_PARAM REG_R11
#define RBM_PINVOKE_COOKIE_PARAM RBM_R11
#define PREDICT_REG_PINVOKE_COOKIE_PARAM PREDICT_REG_R11
diff --git a/src/jit/utils.cpp b/src/jit/utils.cpp
index b3939cb0db..ffc9a753bf 100644
--- a/src/jit/utils.cpp
+++ b/src/jit/utils.cpp
@@ -1258,6 +1258,8 @@ void HelperCallProperties::init()
// This (or these) are not pure, in that they have "VM side effects"...but they don't mutate the heap.
case CORINFO_HELP_ENDCATCH:
+
+ noThrow = true;
break;
// Arithmetic helpers that may throw
@@ -1393,11 +1395,11 @@ void HelperCallProperties::init()
break;
// helpers that return internal handle
- // TODO-ARM64-Bug?: Can these throw or not?
case CORINFO_HELP_GETCLASSFROMMETHODPARAM:
case CORINFO_HELP_GETSYNCFROMCLASSHANDLE:
- isPure = true;
+ isPure = true;
+ noThrow = true;
break;
// Helpers that load the base address for static variables.
@@ -1472,6 +1474,8 @@ void HelperCallProperties::init()
case CORINFO_HELP_THROWNULLREF:
case CORINFO_HELP_THROW:
case CORINFO_HELP_RETHROW:
+ case CORINFO_HELP_THROW_ARGUMENTEXCEPTION:
+ case CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION:
break;
@@ -1480,6 +1484,7 @@ void HelperCallProperties::init()
case CORINFO_HELP_FIELD_ACCESS_CHECK:
case CORINFO_HELP_CLASS_ACCESS_CHECK:
case CORINFO_HELP_DELEGATE_SECURITY_CHECK:
+ case CORINFO_HELP_MON_EXIT_STATIC:
break;
@@ -1489,6 +1494,26 @@ void HelperCallProperties::init()
noThrow = true;
break;
+ case CORINFO_HELP_DBG_IS_JUST_MY_CODE:
+ case CORINFO_HELP_BBT_FCN_ENTER:
+ case CORINFO_HELP_POLL_GC:
+ case CORINFO_HELP_MON_ENTER:
+ case CORINFO_HELP_MON_EXIT:
+ case CORINFO_HELP_MON_ENTER_STATIC:
+ case CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER:
+ case CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT:
+ case CORINFO_HELP_SECURITY_PROLOG:
+ case CORINFO_HELP_SECURITY_PROLOG_FRAMED:
+ case CORINFO_HELP_VERIFICATION_RUNTIME_CHECK:
+ case CORINFO_HELP_GETFIELDADDR:
+ case CORINFO_HELP_INIT_PINVOKE_FRAME:
+ case CORINFO_HELP_JIT_PINVOKE_BEGIN:
+ case CORINFO_HELP_JIT_PINVOKE_END:
+ case CORINFO_HELP_GETCURRENTMANAGEDTHREADID:
+
+ noThrow = true;
+ break;
+
// Not sure how to handle optimization involving the rest of these helpers
default:
diff --git a/src/jit/valuenum.cpp b/src/jit/valuenum.cpp
index 0206173183..e4990d635a 100644
--- a/src/jit/valuenum.cpp
+++ b/src/jit/valuenum.cpp
@@ -7296,6 +7296,7 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN
}
break;
+ case VNF_Box:
case VNF_BoxNullable:
{
// Generate unique VN so, VNForFunc generates a uniq value number for box nullable.
@@ -7792,6 +7793,10 @@ VNFunc Compiler::fgValueNumberHelperMethVNFunc(CorInfoHelpFunc helpFunc)
vnf = VNF_LoopCloneChoiceAddr;
break;
+ case CORINFO_HELP_BOX:
+ vnf = VNF_Box;
+ break;
+
case CORINFO_HELP_BOX_NULLABLE:
vnf = VNF_BoxNullable;
break;
diff --git a/src/jit/valuenumfuncs.h b/src/jit/valuenumfuncs.h
index 2711b4f056..a1372182c8 100644
--- a/src/jit/valuenumfuncs.h
+++ b/src/jit/valuenumfuncs.h
@@ -126,6 +126,7 @@ ValueNumFuncDef(JitNew, 2, false, true, false)
ValueNumFuncDef(JitNewArr, 3, false, true, false)
ValueNumFuncDef(JitReadyToRunNew, 2, false, true, false)
ValueNumFuncDef(JitReadyToRunNewArr, 3, false, true, false)
+ValueNumFuncDef(Box, 3, false, false, false)
ValueNumFuncDef(BoxNullable, 3, false, false, false)
ValueNumFuncDef(LT_UN, 2, false, false, false)
diff --git a/src/mscorlib/ILLinkTrim.xml b/src/mscorlib/ILLinkTrim.xml
new file mode 100644
index 0000000000..fdd89b823e
--- /dev/null
+++ b/src/mscorlib/ILLinkTrim.xml
@@ -0,0 +1,40 @@
+<linker>
+ <!-- To run illink casually on S.P.CoreLib to find dead code, use a command similar to:
+
+C:\git\corefx\Tools/dotnetcli/dotnet.exe "C:\git\corefx\Tools/ILLink/illink.dll" -r System.Private.CoreLib -d C:\git\coreclr\bin\Product\Windows_NT.x64.Debug\ -t -out C:\out\ -b true -v true -h LdtokenTypeMethods,InstanceConstructors -x illinktrim.xml
+
+Then compare the result with an assembly differ.
+
+Note: it will retain all internal members unless you temporarily remove all InternalsVisibleTo attributes (in mscorlib.Friends.cs). Of course, you're then responsible for determining whether those internals were among those the other assemblies use.
+
+Also take care to not remove code that is Unix only, release only, etc.
+
+-->
+ <assembly fullname="System.Private.CoreLib">
+ <type fullname="System.AccessViolationException">
+ <!-- set by runtime -->
+ <field signature="System.Int32 _accessType"/>
+ <field signature="System.IntPtr _ip"/>
+ <field signature="System.IntPtr _target"/>
+ </type>
+ <type fullname="System.Threading.Tasks.Task">
+ <!-- used by debugger -->
+ <field signature="System.Threading.Task ParentForDebugger"/>
+ <field signature="System.Int32 StateFlagsForDebugger"/>
+ </type>
+ <type fullname="System.Runtime.Loader.AssemblyLoadContext">
+ <!-- invoked by runtime -->
+ <method signature="System.Reflection.Assembly Resolve(System.IntPtr,System.Reflection.AssemblyName)"/>
+ <method signature="System.Reflection.Assembly ResolveUsingResolvingEvent(System.IntPtr,System.Reflection.AssemblyName)"/>
+ <method signature="System.IntPtr ResolveUnmanagedDll(System.String,System.IntPtr)"/>
+ </type>
+ <type fullname="System.BadImageFormatException">
+ <!-- invoked by runtime -->
+ <method signature="System.Void .ctor(System.String,System.String,System.Int32)"/>
+ </type>
+ <type fullname="System.Environment">
+ <!-- invoked by runtime -->
+ <method signature="System.Void SetCommandLineArgs(string[])"/>
+ </type>
+ </assembly>
+</linker> \ No newline at end of file
diff --git a/src/mscorlib/MembersMustExist.AnalyzerData b/src/mscorlib/MembersMustExist.AnalyzerData
new file mode 100644
index 0000000000..b5e7a6d742
--- /dev/null
+++ b/src/mscorlib/MembersMustExist.AnalyzerData
@@ -0,0 +1,3 @@
+# Visual Studio
+private int System.Threading.Thread._managedThreadId
+
diff --git a/src/mscorlib/Resources/Strings.resx b/src/mscorlib/Resources/Strings.resx
index 52bd625c97..bbe3636170 100644
--- a/src/mscorlib/Resources/Strings.resx
+++ b/src/mscorlib/Resources/Strings.resx
@@ -1,5 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
@@ -100,8 +159,9 @@
<data name="AggregateException_DeserializationFailure" xml:space="preserve">
<value>The serialization stream contains no inner exceptions.</value>
</data>
- <data name="AggregateException_ToString" xml:space="preserve">
- <value>{0}{1}---&gt; (Inner Exception #{2}) {3}{4}{5}</value>
+ <data name="AggregateException_InnerException" xml:space="preserve">
+ <value>(Inner Exception #{0}) </value>
+ <comment>This text is prepended to each inner exception description during aggregate exception formatting</comment>
</data>
<data name="AppDomain_AppBaseNotSet" xml:space="preserve">
<value>The ApplicationBase must be set before retrieving this property.</value>
@@ -865,9 +925,6 @@
<data name="Argument_CannotSetParentToInterface" xml:space="preserve">
<value>Cannot set parent to an interface.</value>
</data>
- <data name="Argument_CantCallSecObjFunc" xml:space="preserve">
- <value>Cannot evaluate a security function.</value>
- </data>
<data name="Argument_CodepageNotSupported" xml:space="preserve">
<value>{0} is not a supported code page.</value>
</data>
@@ -1217,7 +1274,7 @@
<value>Cannot use type '{0}'. Only value types without pointers or references are supported.</value>
</data>
<data name="Argument_InvalidUnity" xml:space="preserve">
- <value>Invalid Unity type.</value>
+ <value>Type '{0}' is not deserializable.</value>
</data>
<data name="Argument_InvalidValue" xml:space="preserve">
<value>Value was invalid.</value>
@@ -1494,7 +1551,6 @@
</data>
<data name="Argument_UnclosedExceptionBlock" xml:space="preserve">
<value>The IL Generator cannot be used while there are unclosed exceptions.</value>
-
</data>
<data name="Argument_Unexpected_TypeSource" xml:space="preserve">
<value>Unexpected TypeKind when marshaling Windows.Foundation.TypeName.</value>
@@ -3169,7 +3225,7 @@
</data>
<data name="PlatformNotSupported_Remoting" xml:space="preserve">
<value>Remoting is not supported on this platform.</value>
- </data>
+ </data>
<data name="PlatformNotSupported_SecureBinarySerialization" xml:space="preserve">
<value>Secure binary serialization is not supported on this platform.</value>
</data>
@@ -3596,4 +3652,25 @@
<data name="SynchronizationLockException_MisMatchedWrite" xml:space="preserve">
<value>The write lock is being released without being held.</value>
</data>
+ <data name="ConcurrentStack_PushPopRange_CountOutOfRange" xml:space="preserve">
+ <value>The count argument must be greater than or equal to zero.</value>
+ </data>
+ <data name="ConcurrentStack_PushPopRange_InvalidCount" xml:space="preserve">
+ <value>The sum of the startIndex and count arguments must be less than or equal to the collection's Count.</value>
+ </data>
+ <data name="ConcurrentStack_PushPopRange_StartOutOfRange" xml:space="preserve">
+ <value>The startIndex argument must be greater than or equal to zero.</value>
+ </data>
+ <data name="ConcurrentDictionary_SourceContainsDuplicateKeys" xml:space="preserve">
+ <value>The source argument contains duplicate keys.</value>
+ </data>
+ <data name="NotSupported_SignatureType" xml:space="preserve">
+ <value>This method is not supported on signature types.</value>
+ </data>
+ <data name="Memory_ThrowIfDisposed" xml:space="preserve">
+ <value>Memory&lt;T&gt; has been disposed.</value>
+ </data>
+ <data name="Memory_OutstandingReferences" xml:space="preserve">
+ <value>Release all references before disposing this instance.</value>
+ </data>
</root>
diff --git a/src/mscorlib/System.Private.CoreLib.csproj b/src/mscorlib/System.Private.CoreLib.csproj
index 0889a4b854..f7206f98f5 100644
--- a/src/mscorlib/System.Private.CoreLib.csproj
+++ b/src/mscorlib/System.Private.CoreLib.csproj
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <!-- Include common build properties -->
+ <!-- Include common build properties -->
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<!-- Compilation options -->
@@ -44,6 +44,7 @@
<ItemGroup>
<AssemblyMetadata Include="Serviceable">
<Value>True</Value>
+ <Visible>false</Visible>
</AssemblyMetadata>
</ItemGroup>
<!-- Platform specific properties -->
@@ -96,13 +97,6 @@
<MinorVersion>6</MinorVersion>
<ExcludeAssemblyInfoPartialFile>true</ExcludeAssemblyInfoPartialFile>
</PropertyGroup>
- <ItemGroup>
- <AssemblyInfoLines Include="[assembly: System.Resources.SatelliteContractVersion(&quot;$(AssemblyVersion)&quot;)]" />
- <AssemblyInfoLines Include="[assembly: System.Security.AllowPartiallyTrustedCallers]" />
- <AssemblyInfoLines Include="[assembly: System.Runtime.InteropServices.ComVisible(false)]" />
- <AssemblyInfoLines Include="[assembly: System.Resources.NeutralResourcesLanguage(&quot;en-US&quot;)]" />
- <AssemblyInfoLines Include="[assembly: System.Runtime.InteropServices.DefaultDllImportSearchPathsAttribute(System.Runtime.InteropServices.DllImportSearchPath.AssemblyDirectory | System.Runtime.InteropServices.DllImportSearchPath.System32)]" />
- </ItemGroup>
<!--
Helper Paths
-->
@@ -114,8 +108,6 @@
</PropertyGroup>
<!-- Msbuild variables needed to get CoreCLR features to be set properly. -->
<PropertyGroup>
- <ClrProduct>core_clr</ClrProduct>
- <BuildForCoreSystem>true</BuildForCoreSystem>
<!-- These are needed to make sure we have the right set of defines -->
<TargetArch Condition="'$(Platform)'=='x86'">i386</TargetArch>
<TargetArch Condition="'$(Platform)'!='x86'">$(Platform)</TargetArch>
@@ -126,16 +118,11 @@
<!-- Sources -->
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CustomConstantAttribute.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\DateTimeConstantAttribute.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\DecimalConstantAttribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\FriendAccessAllowedAttribute.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\MethodImplAttribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\TypeDependencyAttribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\jithelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\Unsafe.cs" />
<Compile Condition="'$(FeatureICastable)' == 'true'" Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ICastable.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeWrappedException.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ConditionalWeakTable.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\AsyncMethodBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\TaskAwaiter.cs" />
@@ -143,7 +130,6 @@
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Runtime\Reliability\CriticalFinalizerObject.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Runtime\Reliability\PrePrepareMethodAttribute.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Runtime\MemoryFailPoint.cs" />
@@ -152,7 +138,6 @@
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Collections\Comparer.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\CompatibleComparer.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Collections\ListDictionaryInternal.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\EmptyReadOnlyDictionaryInternal.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\Hashtable.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\IHashCodeProvider.cs" />
@@ -277,7 +262,28 @@
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\WindowsRuntime\CLRIKeyValuePairImpl.cs" />
</ItemGroup>
<ItemGroup>
- <Compile Include="$(BclSourcesRoot)\System\AggregateException.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\Vector128.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\Vector256.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Aes.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Avx.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Avx2.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Bmi1.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Bmi2.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Enums.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Fma.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Lzcnt.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Pclmulqdq.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Popcnt.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Sse.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Sse2.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Sse3.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Sse41.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Sse42.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\Ssse3.cs" />
+ </ItemGroup>
+ <ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\AppContext\AppContext.cs" />
<Compile Include="$(BclSourcesRoot)\System\AppContext\AppContextSwitches.cs" />
<Compile Include="$(BclSourcesRoot)\System\AppContext\AppContextDefaultValues.cs" />
@@ -301,11 +307,8 @@
<Compile Include="$(BclSourcesRoot)\System\OutOfMemoryException.cs" />
<Compile Include="$(BclSourcesRoot)\System\Delegate.cs" />
<Compile Include="$(BclSourcesRoot)\System\MulticastDelegate.cs" />
- <Compile Include="$(BclSourcesRoot)\System\__HResults.cs" />
- <Compile Include="$(BclSourcesRoot)\System\HResults.cs" />
<Compile Include="$(BclSourcesRoot)\System\BCLDebug.cs" />
<Compile Include="$(BclSourcesRoot)\System\Activator.cs" />
- <Compile Include="$(BclSourcesRoot)\System\AccessViolationException.cs" />
<Compile Include="$(BclSourcesRoot)\System\AppDomain.cs" />
<Compile Include="$(BclSourcesRoot)\System\AppDomainSetup.cs" />
<Compile Include="$(BclSourcesRoot)\System\AppDomainManager.cs" />
@@ -313,23 +316,17 @@
<Compile Include="$(BclSourcesRoot)\System\ArgIterator.cs" />
<Compile Include="$(BclSourcesRoot)\System\Attribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\BadImageFormatException.CoreCLR.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Boolean.cs" />
<Compile Include="$(BclSourcesRoot)\System\Buffer.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Byte.cs" />
<Compile Include="$(BclSourcesRoot)\System\CompatibilitySwitches.cs" />
<Compile Include="$(BclSourcesRoot)\System\Currency.cs" />
<Compile Include="$(BclSourcesRoot)\System\Decimal.cs" />
<Compile Include="$(BclSourcesRoot)\System\DefaultBinder.CanConvert.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Double.cs" />
<Compile Include="$(BclSourcesRoot)\System\Empty.cs" />
<Compile Include="$(BclSourcesRoot)\System\Enum.cs" />
<Compile Include="$(BclSourcesRoot)\System\Environment.cs" />
<Compile Include="$(BclSourcesRoot)\System\GC.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Guid.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Guid.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\InsufficientMemoryException.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Int16.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Int32.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Int64.cs" />
<Compile Include="$(BclSourcesRoot)\System\IntPtr.cs" />
<Compile Include="$(BclSourcesRoot)\System\Internal.cs" />
<Compile Include="$(BclSourcesRoot)\System\Math.cs" />
@@ -337,17 +334,13 @@
<Compile Include="$(BclSourcesRoot)\System\mda.cs" />
<Compile Include="$(BclSourcesRoot)\System\MissingFieldException.cs" />
<Compile Include="$(BclSourcesRoot)\System\MissingMemberException.cs" />
- <Compile Include="$(BclSourcesRoot)\System\NonSerializedAttribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\Number.cs" />
<Compile Include="$(BclSourcesRoot)\System\ParseNumbers.cs" />
<Compile Include="$(BclSourcesRoot)\System\ResId.cs" />
<Compile Include="$(BclSourcesRoot)\System\RtType.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeArgumentHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\RuntimeHandles.cs" />
- <Compile Include="$(BclSourcesRoot)\System\SByte.cs" />
- <Compile Include="$(BclSourcesRoot)\System\SerializableAttribute.cs" />
<Compile Include="$(BclSourcesRoot)\System\SharedStatics.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Single.cs" />
<Compile Include="$(BclSourcesRoot)\System\StubHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\TimeZoneInfo.AdjustmentRule.cs" />
<Compile Include="$(BclSourcesRoot)\System\TimeZoneInfo.cs" />
@@ -357,9 +350,6 @@
<Compile Include="$(BclSourcesRoot)\System\TypeNameParser.cs" />
<Compile Include="$(BclSourcesRoot)\System\TypedReference.cs" />
<Compile Include="$(BclSourcesRoot)\System\TypeLoadException.cs" />
- <Compile Include="$(BclSourcesRoot)\System\UInt16.cs" />
- <Compile Include="$(BclSourcesRoot)\System\UInt32.cs" />
- <Compile Include="$(BclSourcesRoot)\System\UInt64.cs" />
<Compile Include="$(BclSourcesRoot)\System\UIntPtr.cs" />
<Compile Include="$(BclSourcesRoot)\System\ValueType.cs" />
<Compile Include="$(BclSourcesRoot)\System\WeakReference.cs" />
@@ -374,6 +364,7 @@
<Compile Include="$(BclSourcesRoot)\Internal\Runtime\Augments\EnvironmentAugments.cs" />
<Compile Include="$(BclSourcesRoot)\Internal\Runtime\Augments\RuntimeThread.cs" />
<Compile Include="$(BclSourcesRoot)\Internal\Console.cs" />
+ <Compile Include="$(BclSourcesRoot)\Internal\Padding.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Reflection\Assembly.CoreCLR.cs" />
@@ -463,13 +454,10 @@
<Compile Include="$(BclSourcesRoot)\System\Globalization\IdnMapping.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\RegionInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\TextInfo.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\TimeSpanFormat.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\TimeSpanParse.cs" />
</ItemGroup>
<ItemGroup Condition="'$(FeatureCoreFxGlobalization)' != 'true'">
<Compile Include="$(BclSourcesRoot)\System\Globalization\EncodingDataItem.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\EncodingTable.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Globalization\GlobalizationAssembly.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Threading\SynchronizationContext.cs" />
@@ -510,22 +498,18 @@
<Compile Condition="'$(FeatureCominterop)' == 'true'" Include="$(BclSourcesRoot)\System\Threading\Tasks\IAsyncCausalityTracerStatics.cs" />
</ItemGroup>
<ItemGroup>
- <Compile Include="$(BclSourcesRoot)\System\Buffers\ArrayPoolEventSource.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\ClrThreadPoolBoundHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\ClrThreadPoolBoundHandleOverlapped.cs" />
<Compile Include="$(BclSourcesRoot)\System\Threading\ClrThreadPoolPreAllocatedOverlapped.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\IO\__Error.cs" />
- <Compile Include="$(BclSourcesRoot)\System\IO\__HResults.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\BinaryReader.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\Directory.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\SearchOption.cs" />
- <Compile Include="$(BclSourcesRoot)\System\IO\DriveNotFoundException.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\File.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\FileLoadException.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\FileNotFoundException.CoreCLR.cs" />
- <Compile Include="$(BclSourcesRoot)\System\IO\IOException.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\MemoryStream.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\Stream.cs" />
<Compile Include="$(BclSourcesRoot)\System\IO\UnmanagedMemoryAccessor.cs" />
@@ -563,7 +547,6 @@
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\SymbolStore\Token.cs" />
</ItemGroup>
<ItemGroup>
- <Compile Include="$(BclSourcesRoot)\System\Runtime\ExceptionServices\CorruptingExceptionCommon.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\ExceptionServices\ExceptionServicesCommon.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\ExceptionServices\ExceptionNotification.cs" />
</ItemGroup>
@@ -586,7 +569,6 @@
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Resources\__FastResourceComparer.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Resources\__HResults.cs" />
<Compile Include="$(BclSourcesRoot)\System\Resources\FileBasedResourceGroveler.cs" />
<Compile Include="$(BclSourcesRoot)\System\Resources\IResourceGroveler.cs" />
<Compile Include="$(BclSourcesRoot)\System\Resources\ManifestBasedResourceGroveler.cs" />
@@ -603,8 +585,6 @@
<Compile Include="$(BclSourcesRoot)\System\Collections\Generic\DebugView.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\Generic\List.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\Generic\ArraySortHelper.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Collections\ObjectModel\Collection.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Collections\ObjectModel\ReadOnlyCollection.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\ObjectModel\ReadOnlyDictionary.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\Concurrent\ConcurrentStack.cs" />
<Compile Include="$(BclSourcesRoot)\System\Collections\Concurrent\IProducerConsumerCollection.cs" />
@@ -613,7 +593,6 @@
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\Microsoft\Win32\SafeHandles\SafeFindHandle.cs" />
- <Compile Include="$(BclSourcesRoot)\Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs" />
<Compile Include="$(BclSourcesRoot)\Microsoft\Win32\SafeHandles\SafeWaitHandle.cs" />
<Compile Condition="'$(FeatureWin32Registry)' == 'true'" Include="$(BclSourcesRoot)\Microsoft\Win32\SafeHandles\SafeRegistryHandle.cs" />
</ItemGroup>
@@ -621,6 +600,7 @@
<Compile Include="$(BclSourcesRoot)\System\Numerics\Hashing\HashHelpers.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
+ <Compile Include="$(BclSourcesRoot)\Interop\Unix\Interop.Libraries.cs" />
<Compile Include="$(BclSourcesRoot)\Interop\Unix\System.Globalization.Native\Interop.ICU.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\CompareInfo.Unix.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\CultureInfo.Unix.cs" />
@@ -664,19 +644,16 @@
<ItemGroup>
<Compile Include="src\System\Runtime\RuntimeImports.cs" />
</ItemGroup>
- <Import Project="shared\System.Private.CoreLib.Shared.projitems" Label="Shared" />
+ <Import Project="shared\System.Private.CoreLib.Shared.projitems" />
<PropertyGroup>
<CheckCDefines Condition="'$(CheckCDefines)'==''">true</CheckCDefines>
</PropertyGroup>
<Target Name="CDefineChecker" BeforeTargets="Build" Condition="'$(CheckCDefines)'=='true'">
<!-- Compiler Definition Verification -->
- <Message Importance="High" Text="============" />
<PropertyGroup>
- <IgnoreDefineConstants>FEATURE_IMPLICIT_TLS;FEATURE_HIJACK</IgnoreDefineConstants>
<CMakeDefinitionSaveFile>$(IntermediateOutputPath)\cmake.definitions</CMakeDefinitionSaveFile>
</PropertyGroup>
<Exec Command="python $(MSBuildThisFileDirectory)..\scripts\check-definitions.py &quot;$(CMakeDefinitionSaveFile)&quot; &quot;$(DefineConstants)&quot; &quot;$(IgnoreDefineConstants)&quot; " />
- <Message Importance="High" Text="============" />
</Target>
<PropertyGroup Condition="'$(BuildOS)' == 'Windows_NT'">
<EnableDotnetAnalyzers Condition="'$(EnableDotnetAnalyzers)'==''">true</EnableDotnetAnalyzers>
@@ -701,4 +678,4 @@
<Win32Resource Condition="'$(GenerateNativeVersionInfo)'=='true'">$(IntermediateOutputPath)\System.Private.CoreLib.res</Win32Resource>
</PropertyGroup>
<Import Project="GenerateCompilerResponseFile.targets" />
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/mscorlib/System.Private.CoreLib.sln b/src/mscorlib/System.Private.CoreLib.sln
index d5cdd02cf3..d4052f8058 100644
--- a/src/mscorlib/System.Private.CoreLib.sln
+++ b/src/mscorlib/System.Private.CoreLib.sln
@@ -1,17 +1,11 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.26208.0
+VisualStudioVersion = 15.0.26817.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Private.CoreLib", "System.Private.CoreLib.csproj", "{3DA06C3A-2E7B-4CB7-80ED-9B12916013F9}"
EndProject
-Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "System.Private.CoreLib.Shared", "shared\System.Private.CoreLib.Shared.shproj", "{C5ED3C1D-B572-46F1-8F96-522A85CE1179}"
-EndProject
Global
- GlobalSection(SharedMSBuildProjectFiles) = preSolution
- shared\System.Private.CoreLib.Shared.projitems*{3da06c3a-2e7b-4cb7-80ed-9b12916013f9}*SharedItemsImports = 4
- shared\System.Private.CoreLib.Shared.projitems*{c5ed3c1d-b572-46f1-8f96-522a85ce1179}*SharedItemsImports = 13
- EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Checked|amd64 = Checked|amd64
Checked|arm = Checked|arm
@@ -55,4 +49,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {DA05075A-7CDA-4F65-AF6A-CB5DB6CF936F}
+ EndGlobalSection
EndGlobal
diff --git a/src/mscorlib/Tools/Versioning/GenerateVersionInfo.targets b/src/mscorlib/Tools/Versioning/GenerateVersionInfo.targets
index 00f5282c7c..0e9a2263cc 100644
--- a/src/mscorlib/Tools/Versioning/GenerateVersionInfo.targets
+++ b/src/mscorlib/Tools/Versioning/GenerateVersionInfo.targets
@@ -25,37 +25,13 @@
<CoreCompileDependsOn>$(CoreCompileDependsOn);GenerateAssemblyInfo</CoreCompileDependsOn>
</PropertyGroup>
- <Target Name="GenerateAssemblyInfo"
- Inputs="$(MSBuildProjectFile)"
- Outputs="$(AssemblyInfoFile)"
- Condition="'$(GenerateAssemblyInfo)'=='true'">
-
- <Error Condition="!Exists('$(IntermediateOutputPath)')" Text="GenerateAssemblyInfo failed because IntermediateOutputPath isn't set to a valid directory" />
-
- <ItemGroup>
- <AssemblyInfoUsings Include="using System%3B" />
- <AssemblyInfoUsings Include="using System.Reflection%3B" />
- <AssemblyInfoLines Include="[assembly:AssemblyTitle(&quot;$(AssemblyName)&quot;)]" />
- <AssemblyInfoLines Include="[assembly:AssemblyDescription(&quot;$(AssemblyName)&quot;)]" />
- <AssemblyInfoLines Include="[assembly:AssemblyDefaultAlias(&quot;$(AssemblyName)&quot;)]" />
- <AssemblyInfoLines Include="[assembly:AssemblyCompany(&quot;Microsoft Corporation&quot;)]" />
- <AssemblyInfoLines Include="[assembly:AssemblyProduct(&quot;Microsoft\x00ae .NET Framework&quot;)]" />
- <AssemblyInfoLines Include="[assembly:AssemblyCopyright(&quot;\x00a9 Microsoft Corporation. All rights reserved.&quot;)]" />
- <AssemblyInfoLines Include="[assembly:AssemblyVersion(&quot;$(AssemblyVersion)&quot;)]" />
- <AssemblyInfoLines Include="[assembly:AssemblyFileVersion(&quot;$(AssemblyFileVersion)&quot;)]" />
- <AssemblyInfoLines Include="[assembly:AssemblyInformationalVersion(@&quot;$(AssemblyFileVersion)$(BuiltByString)&quot;)]" />
- <AssemblyInfoLines Condition="'$(CLSCompliant)'=='true'" Include="[assembly:CLSCompliant(true)]" />
- </ItemGroup>
-
- <WriteLinesToFile File="$(AssemblyInfoFile)"
- Lines="@(AssemblyInfoUsings);@(AssemblyInfoLines)"
- Overwrite="true" />
-
- <ItemGroup>
- <Compile Include="$(AssemblyInfoFile)" />
- <FileWrites Include="$(AssemblyInfoFile)" />
- </ItemGroup>
- </Target>
+ <ItemGroup>
+ <AssemblyInfoLines Include="[assembly:System.Resources.SatelliteContractVersion(&quot;$(AssemblyVersion)&quot;)]" />
+ <AssemblyInfoLines Include="[assembly:System.Security.AllowPartiallyTrustedCallers]" />
+ <AssemblyInfoLines Include="[assembly:System.Runtime.InteropServices.ComVisible(false)]" />
+ <AssemblyInfoLines Include="[assembly:System.Resources.NeutralResourcesLanguage(&quot;en-US&quot;)]" />
+ <AssemblyInfoLines Include="[assembly:System.Runtime.InteropServices.DefaultDllImportSearchPathsAttribute(System.Runtime.InteropServices.DllImportSearchPath.AssemblyDirectory | System.Runtime.InteropServices.DllImportSearchPath.System32)]" />
+ </ItemGroup>
<!-- #################################### -->
<!-- Generate Native Version Info -->
diff --git a/src/mscorlib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs b/src/mscorlib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs
index 79aedd74d3..683845dbc1 100644
--- a/src/mscorlib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs
+++ b/src/mscorlib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs
@@ -44,7 +44,7 @@ internal static partial class Interop
internal unsafe static extern int CompareStringOrdinalIgnoreCase(char* lpStr1, int cwStr1Len, char* lpStr2, int cwStr2Len);
[DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_GetSortVersion")]
- internal static extern int GetSortVersion();
+ internal static extern int GetSortVersion(SafeSortHandle sortHandle);
internal class SafeSortHandle : SafeHandle
{
diff --git a/src/mscorlib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs b/src/mscorlib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs
index 69e39b30d2..ad8b73aed2 100644
--- a/src/mscorlib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs
+++ b/src/mscorlib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs
@@ -16,7 +16,7 @@ internal static partial class Interop
POSIX_FADV_SEQUENTIAL = 2, /* sequential I/O access */
POSIX_FADV_WILLNEED = 3, /* will need specified pages */
POSIX_FADV_DONTNEED = 4, /* don't need the specified pages */
- POSIX_FADV_NOREUSE = 5, /* data will only be acessed once */
+ POSIX_FADV_NOREUSE = 5, /* data will only be accessed once */
}
/// <summary>
diff --git a/src/mscorlib/shared/Interop/Windows/Interop.Libraries.cs b/src/mscorlib/shared/Interop/Windows/Interop.Libraries.cs
index f65e05cfdd..45d910bfcc 100644
--- a/src/mscorlib/shared/Interop/Windows/Interop.Libraries.cs
+++ b/src/mscorlib/shared/Interop/Windows/Interop.Libraries.cs
@@ -9,6 +9,7 @@ internal static partial class Interop
internal const string BCrypt = "BCrypt.dll";
internal const string Crypt32 = "crypt32.dll";
internal const string Kernel32 = "kernel32.dll";
+ internal const string Ole32 = "ole32.dll";
internal const string OleAut32 = "oleaut32.dll";
internal const string User32 = "user32.dll";
}
diff --git a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs
new file mode 100644
index 0000000000..16365ee651
--- /dev/null
+++ b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs
@@ -0,0 +1,24 @@
+// 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 Microsoft.Win32.SafeHandles;
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+internal partial class Interop
+{
+ internal partial class Kernel32
+ {
+ internal unsafe struct CREATEFILE2_EXTENDED_PARAMETERS
+ {
+ internal uint dwSize;
+ internal uint dwFileAttributes;
+ internal uint dwFileFlags;
+ internal uint dwSecurityQosFlags;
+ internal SECURITY_ATTRIBUTES* lpSecurityAttributes;
+ internal IntPtr hTemplateFile;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs
index 0909d3a6c8..ddc18f6c42 100644
--- a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs
+++ b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs
@@ -3,29 +3,30 @@
// See the LICENSE file in the project root for more information.
using Microsoft.Win32.SafeHandles;
-using System;
+using System.IO;
using System.Runtime.InteropServices;
internal partial class Interop
{
internal partial class Kernel32
{
- [DllImport(Libraries.Kernel32, EntryPoint = "CreateFile2", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
- internal static extern unsafe SafeFileHandle CreateFile2(
+ [DllImport(Libraries.Kernel32, EntryPoint = "CreateFile2", SetLastError = true, CharSet = CharSet.Unicode)]
+ private static extern SafeFileHandle CreateFile2Private(
string lpFileName,
int dwDesiredAccess,
- System.IO.FileShare dwShareMode,
- System.IO.FileMode dwCreationDisposition,
- CREATEFILE2_EXTENDED_PARAMETERS* pCreateExParams);
+ FileShare dwShareMode,
+ FileMode dwCreationDisposition,
+ ref Kernel32.CREATEFILE2_EXTENDED_PARAMETERS pCreateExParams);
- internal unsafe struct CREATEFILE2_EXTENDED_PARAMETERS
+ internal static SafeFileHandle CreateFile2(
+ string lpFileName,
+ int dwDesiredAccess,
+ FileShare dwShareMode,
+ FileMode dwCreationDisposition,
+ ref Kernel32.CREATEFILE2_EXTENDED_PARAMETERS pCreateExParams)
{
- internal uint dwSize;
- internal uint dwFileAttributes;
- internal uint dwFileFlags;
- internal uint dwSecurityQosFlags;
- internal SECURITY_ATTRIBUTES* lpSecurityAttributes;
- internal IntPtr hTemplateFile;
+ lpFileName = PathInternal.EnsureExtendedPrefixOverMaxPath(lpFileName);
+ return CreateFile2Private(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, ref pCreateExParams);
}
}
-} \ No newline at end of file
+}
diff --git a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.FreeLibrary.cs b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.FreeLibrary.cs
new file mode 100644
index 0000000000..c70865350a
--- /dev/null
+++ b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.FreeLibrary.cs
@@ -0,0 +1,15 @@
+// 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.InteropServices;
+
+internal partial class Interop
+{
+ internal partial class Kernel32
+ {
+ [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
+ internal static extern bool FreeLibrary(IntPtr hModule);
+ }
+}
diff --git a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs
new file mode 100644
index 0000000000..4eef5852fe
--- /dev/null
+++ b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs
@@ -0,0 +1,18 @@
+// 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 Microsoft.Win32.SafeHandles;
+using System;
+using System.Runtime.InteropServices;
+
+internal partial class Interop
+{
+ internal partial class Kernel32
+ {
+ internal const int LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
+
+ [DllImport(Libraries.Kernel32, EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode, SetLastError = true)]
+ internal static extern SafeLibraryHandle LoadLibraryEx(string libFilename, IntPtr reserved, int flags);
+ }
+}
diff --git a/src/mscorlib/shared/Interop/Windows/User32/Interop.LoadString.cs b/src/mscorlib/shared/Interop/Windows/User32/Interop.LoadString.cs
new file mode 100644
index 0000000000..d3d575e221
--- /dev/null
+++ b/src/mscorlib/shared/Interop/Windows/User32/Interop.LoadString.cs
@@ -0,0 +1,16 @@
+// 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 Microsoft.Win32.SafeHandles;
+
+internal partial class Interop
+{
+ internal partial class User32
+ {
+ [DllImport(Libraries.User32, SetLastError = true, EntryPoint = "LoadStringW", CharSet = CharSet.Unicode)]
+ internal static extern int LoadString(SafeLibraryHandle handle, int id, [Out] StringBuilder buffer, int bufferLength);
+ }
+}
diff --git a/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs b/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs
index 256f611463..3be2e354ab 100644
--- a/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs
+++ b/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs
@@ -2,9 +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 Microsoft.Win32.SafeHandles;
-
-namespace Microsoft.Win32
+namespace Microsoft.Win32.SafeHandles
{
sealed internal class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid
{
@@ -12,7 +10,7 @@ namespace Microsoft.Win32
override protected bool ReleaseHandle()
{
- return UnsafeNativeMethods.FreeLibrary(handle);
+ return Interop.Kernel32.FreeLibrary(handle);
}
}
}
diff --git a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems
index 07e15cf65d..a13543ea93 100644
--- a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems
+++ b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems
@@ -13,13 +13,20 @@
<TargetsUnix Condition="'$(TargetsUnix)' != 'true'">false</TargetsUnix>
<TargetsOSX Condition="'$(TargetsOSX)' != 'true'">false</TargetsOSX>
</PropertyGroup>
+ <ItemDefinitionGroup>
+ <Compile>
+ <Visible>true</Visible>
+ </Compile>
+ </ItemDefinitionGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\CriticalHandleMinusOneIsInvalid.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\CriticalHandleZeroOrMinusOneIsInvalid.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeHandleMinusOneIsInvalid.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeHandleZeroOrMinusOneIsInvalid.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Action.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\AccessViolationException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ApplicationException.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\AggregateException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ArgumentException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ArgumentNullException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ArgumentOutOfRangeException.cs" />
@@ -32,10 +39,16 @@
<Compile Include="$(MSBuildThisFileDirectory)System\AttributeUsageAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\BadImageFormatException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\BitConverter.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Boolean.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ArrayPool.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ArrayPoolEventSource.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ConfigurableArrayPool.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\IRetainable.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\MemoryHandle.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\OwnedMemory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\TlsOverPerCoreLockedStacksArrayPool.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Utilities.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Byte.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Char.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\CharEnumerator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\CLSCompliantAttribute.cs" />
@@ -62,6 +75,9 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\IList.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\IStructuralComparable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Collections\IStructuralEquatable.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Collections\ListDictionaryInternal.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Collections\ObjectModel\Collection.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Collections\ObjectModel\ReadOnlyCollection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ComponentModel\DefaultValueAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ComponentModel\EditorBrowsableAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Configuration\Assemblies\AssemblyHashAlgorithm.cs" />
@@ -80,6 +96,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Debug.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\DivideByZeroException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\DllNotFoundException.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Double.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\DuplicateWaitObjectException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\EntryPointNotFoundException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\EventArgs.cs" />
@@ -127,9 +144,13 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TaiwanLunisolarCalendar.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextElementEnumerator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\ThaiBuddhistCalendar.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TimeSpanFormat.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TimeSpanParse.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TimeSpanStyles.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\UmAlQuraCalendar.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\UnicodeCategory.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Guid.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\HResults.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IAsyncResult.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ICloneable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IComparable.cs" />
@@ -147,6 +168,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\InvalidTimeZoneException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\BinaryWriter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\DirectoryNotFoundException.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveNotFoundException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\EncodingCache.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\EndOfStreamException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Error.cs" />
@@ -157,6 +179,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileShare.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\IOException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathTooLongException.cs" />
@@ -168,15 +191,20 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IObservable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IObserver.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IProgress.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Int16.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Int32.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Int64.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Lazy.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MarshalByRefObject.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemberAccessException.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Memory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MethodAccessException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MidpointRounding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MissingMethodException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MulticastNotSupportedException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\NotFiniteNumberException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\NotImplementedException.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\NonSerializedAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\NotSupportedException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\NullReferenceException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ObjectDisposedException.cs" />
@@ -190,6 +218,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Random.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\RankException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlySpan.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlyMemory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\AmbiguousMatchException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Assembly.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\AssemblyAlgorithmIdAttribute.cs" />
@@ -241,6 +270,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodImplAttributes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodInfo.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodInfo.Internal.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Missing.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Module.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ModuleResolveEventHandler.cs" />
@@ -258,6 +288,15 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ReflectionTypeLoadException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ResourceAttributes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ResourceLocation.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureArrayType.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureByRefType.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureConstructedGenericType.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureGenericMethodParameterType.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureGenericParameterType.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureHasElementType.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignaturePointerType.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureType.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureTypeExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\StrongNameKeyPair.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\TargetException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\TargetInvocationException.cs" />
@@ -278,7 +317,9 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Resources\SatelliteContractVersionAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Resources\UltimateResourceFallbackLocation.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\AccessedThroughPropertyAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\AsyncMethodBuilderAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\AsyncStateMachineAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\AsyncValueTaskMethodBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CallerFilePathAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CallerLineNumberAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CallerMemberNameAttribute.cs" />
@@ -286,6 +327,10 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompilationRelaxationsAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompilerGeneratedAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompilerGlobalScopeAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ConfiguredValueTaskAwaitable.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CustomConstantAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DateTimeConstantAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DecimalConstantAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DefaultDependencyAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DependencyAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DisablePrivateReflectionAttribute.cs" />
@@ -298,6 +343,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IndexerNameAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\INotifyCompletion.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\InternalsVisibleToAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IntrinsicAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IsConst.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IsByRefLikeAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IsVolatile.cs" />
@@ -305,11 +351,13 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ITuple.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\LoadHint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\MethodCodeType.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\MethodImplAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\MethodImplOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IsReadOnlyAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ReferenceAssemblyAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RuntimeCompatibilityAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RuntimeFeature.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RuntimeWrappedException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\SpecialNameAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\StateMachineAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\StringFreezingAttribute.cs" />
@@ -319,15 +367,29 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\TypeForwardedFromAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\TypeForwardedToAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\UnsafeValueTypeAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ValueTaskAwaiter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\Cer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\Consistency.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\ReliabilityContractAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ExceptionServices\HandleProcessCorruptedStateExceptionsAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\BestFitMappingAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\CallingConvention.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\CharSet.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ComVisibleAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\DefaultCharSetAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\DefaultDllImportSearchPathsAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\DllImportAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\DllImportSearchPath.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ExternalException.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\FieldOffsetAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\InAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\LayoutKind.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MarshalAsAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OptionalAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OutAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PreserveSigAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StringBuffer.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StructLayoutAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\UnmanagedFunctionPointerAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\UnmanagedType.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\VarEnum.cs" />
@@ -347,6 +409,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\StreamingContext.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Versioning\NonVersionableAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Versioning\TargetFrameworkAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\SByte.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\AllowPartiallyTrustedCallersAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\CryptographicException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\PartialTrustVisibilityLevel.cs" />
@@ -362,8 +425,11 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SuppressUnmanagedCodeSecurityAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\UnverifiableCodeAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\VerificationException.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\SerializableAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Single.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Span.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Span.NonGeneric.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\StringSpanHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\StackOverflowException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\StringComparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\StringComparison.cs" />
@@ -391,7 +457,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Text\StringBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Text\UnicodeEncoding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF32Encoding.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF7Encoding.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF7Encoding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF8Encoding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeSpan.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ThreadAttributes.cs" />
@@ -415,6 +481,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskCanceledException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskSchedulerException.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\ValueTask.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadAbortException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPriority.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadStart.cs" />
@@ -436,9 +503,13 @@
<Compile Include="$(MSBuildThisFileDirectory)System\TypeCode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TypeInitializationException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TypeUnloadedException.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\UInt16.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\UInt32.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\UInt64.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\UnauthorizedAccessException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\UnhandledExceptionEventArgs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\UnhandledExceptionEventHandler.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\UnitySerializationHolder.cs"/>
<Compile Include="$(MSBuildThisFileDirectory)System\ValueTuple.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Version.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Void.cs" />
@@ -488,19 +559,15 @@
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\BCrypt\Interop.BCryptGenRandom.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Crypt32\Interop.CryptProtectMemory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.BOOL.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.Errors.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.Libraries.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CancelIoEx.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CloseHandle.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile.cs" Condition="'$(EnableWinRT)' != 'true'" />
- <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile2.cs" Condition="'$(EnableWinRT)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileAttributes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FILE_INFO_BY_HANDLE_CLASS.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileTypes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FindClose.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FindFirstFileEx.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FlushFileBuffers.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FormatMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FreeEnvironmentStrings.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetCPInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetEnvironmentStrings.cs" />
@@ -528,25 +595,41 @@
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysAllocStringLen.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysFreeString.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysStringLen.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.Constants.cs" Condition="'$(EnableWinRT)' != 'true'" />
- <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.SendMessageTimeout.cs" Condition="'$(EnableWinRT)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Win32.cs" Condition="'$(EnableWinRT)' != 'true' and '$(EnableDummyGlobalizationImplementation)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.WinRT.cs" Condition="'$(EnableWinRT)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.Win32.cs" Condition="'$(EnableWinRT)' != 'true' and '$(EnableDummyGlobalizationImplementation)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.WinRT.cs" Condition="'$(EnableWinRT)' == 'true'" />
- <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Win32.cs" Condition="'$(EnableWinRT)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Windows.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.WinRT.cs" Condition="'$(EnableWinRT)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamCompletionSource.Win32.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathHelper.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Windows.StringBuffer.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\IO\Win32Marshal.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\DisableMediaInsertionPrompt.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SafeBSTRHandle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Windows.cs" />
</ItemGroup>
+ <ItemGroup Condition="$(TargetsWindows) and '$(EnableWinRT)' != 'true'">
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Win32.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.LoadLibraryEx.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FreeLibrary.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.Constants.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.SendMessageTimeout.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.LoadString.cs" />
+ </ItemGroup>
+ <ItemGroup Condition="$(TargetsWindows) and '$(EnableWinRT)' == 'true'">
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.WinRT.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile2.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs" />
+ </ItemGroup>
+ <ItemGroup Condition="$(TargetsWindows) or '$(FeaturePal)'=='true'">
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.Errors.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FormatMessage.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\Win32Marshal.cs" />
+ </ItemGroup>
<ItemGroup Condition="$(TargetsUnix)">
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\Interop.Errors.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\Interop.IOErrors.cs" />
diff --git a/src/mscorlib/shared/System.Private.CoreLib.Shared.shproj b/src/mscorlib/shared/System.Private.CoreLib.Shared.shproj
deleted file mode 100644
index 20c3de100b..0000000000
--- a/src/mscorlib/shared/System.Private.CoreLib.Shared.shproj
+++ /dev/null
@@ -1,17 +0,0 @@
-<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <!-- Set the properties to prevent any items from being excluded from the IDE view -->
- <PropertyGroup Label="Globals">
- <ProjectGuid>c5ed3c1d-b572-46f1-8f96-522a85ce1179</ProjectGuid>
- </PropertyGroup>
- <PropertyGroup>
- <TargetsWindows>true</TargetsWindows>
- <TargetsUnix>true</TargetsUnix>
- <TargetsOSX>true</TargetsOSX>
- </PropertyGroup>
- <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
- <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
- <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
- <PropertyGroup />
- <Import Project="System.Private.CoreLib.Shared.projitems" Label="Shared" />
- <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
-</Project> \ No newline at end of file
diff --git a/src/mscorlib/src/System/AccessViolationException.cs b/src/mscorlib/shared/System/AccessViolationException.cs
index 23d086fb42..103a4c0a9d 100644
--- a/src/mscorlib/src/System/AccessViolationException.cs
+++ b/src/mscorlib/shared/System/AccessViolationException.cs
@@ -21,19 +21,19 @@ namespace System
public AccessViolationException()
: base(SR.Arg_AccessViolationException)
{
- HResult = __HResults.E_POINTER;
+ HResult = HResults.E_POINTER;
}
public AccessViolationException(String message)
: base(message)
{
- HResult = __HResults.E_POINTER;
+ HResult = HResults.E_POINTER;
}
public AccessViolationException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.E_POINTER;
+ HResult = HResults.E_POINTER;
}
protected AccessViolationException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/Action.cs b/src/mscorlib/shared/System/Action.cs
index b82c14d9dc..da6813829e 100644
--- a/src/mscorlib/shared/System/Action.cs
+++ b/src/mscorlib/shared/System/Action.cs
@@ -33,3 +33,9 @@ namespace System
public delegate bool Predicate<in T>(T obj);
}
+
+namespace System.Buffers
+{
+ public delegate void SpanAction<T, in TArg>(Span<T> span, TArg arg);
+ public delegate void ReadOnlySpanAction<T, in TArg>(ReadOnlySpan<T> span, TArg arg);
+}
diff --git a/src/mscorlib/src/System/AggregateException.cs b/src/mscorlib/shared/System/AggregateException.cs
index 0b840972ef..36b9494980 100644
--- a/src/mscorlib/src/System/AggregateException.cs
+++ b/src/mscorlib/shared/System/AggregateException.cs
@@ -2,14 +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.
-// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
-//
-//
-//
-// Public type to communicate multiple failures to an end-user.
-//
-// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -41,7 +33,7 @@ namespace System
public AggregateException()
: base(SR.AggregateException_ctor_DefaultMessage)
{
- m_innerExceptions = new ReadOnlyCollection<Exception>(new Exception[0]);
+ m_innerExceptions = new ReadOnlyCollection<Exception>(Array.Empty<Exception>());
}
/// <summary>
@@ -52,7 +44,7 @@ namespace System
public AggregateException(string message)
: base(message)
{
- m_innerExceptions = new ReadOnlyCollection<Exception>(new Exception[0]);
+ m_innerExceptions = new ReadOnlyCollection<Exception>(Array.Empty<Exception>());
}
/// <summary>
@@ -285,11 +277,6 @@ namespace System
/// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> argument is null.</exception>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
- if (info == null)
- {
- throw new ArgumentNullException(nameof(info));
- }
-
base.GetObjectData(info, context);
Exception[] innerExceptions = new Exception[m_innerExceptions.Count];
@@ -384,7 +371,7 @@ namespace System
/// If any inner exceptions are themselves instances of
/// <see cref="AggregateException"/>, this method will recursively flatten all of them. The
/// inner exceptions returned in the new <see cref="AggregateException"/>
- /// will be the union of all of the the inner exceptions from exception tree rooted at the provided
+ /// will be the union of all of the inner exceptions from exception tree rooted at the provided
/// <see cref="AggregateException"/> instance.
/// </remarks>
public AggregateException Flatten()
@@ -461,17 +448,20 @@ namespace System
/// <returns>A string representation of the current exception.</returns>
public override string ToString()
{
- string text = base.ToString();
+ StringBuilder text = new StringBuilder();
+ text.Append(base.ToString());
for (int i = 0; i < m_innerExceptions.Count; i++)
{
- text = String.Format(
- CultureInfo.InvariantCulture,
- SR.AggregateException_ToString,
- text, Environment.NewLine, i, m_innerExceptions[i].ToString(), "<---", Environment.NewLine);
+ text.Append(Environment.NewLine);
+ text.Append("---> ");
+ text.Append(string.Format(CultureInfo.InvariantCulture, SR.AggregateException_InnerException, i));
+ text.Append(m_innerExceptions[i].ToString());
+ text.Append("<---");
+ text.Append(Environment.NewLine);
}
- return text;
+ return text.ToString();
}
/// <summary>
diff --git a/src/mscorlib/shared/System/ApplicationException.cs b/src/mscorlib/shared/System/ApplicationException.cs
index cb98902de1..83ced79876 100644
--- a/src/mscorlib/shared/System/ApplicationException.cs
+++ b/src/mscorlib/shared/System/ApplicationException.cs
@@ -31,7 +31,7 @@ namespace System
public ApplicationException()
: base(SR.Arg_ApplicationException)
{
- HResult = __HResults.COR_E_APPLICATION;
+ HResult = HResults.COR_E_APPLICATION;
}
// Creates a new ApplicationException with its message string set to
@@ -41,13 +41,13 @@ namespace System
public ApplicationException(String message)
: base(message)
{
- HResult = __HResults.COR_E_APPLICATION;
+ HResult = HResults.COR_E_APPLICATION;
}
public ApplicationException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_APPLICATION;
+ HResult = HResults.COR_E_APPLICATION;
}
protected ApplicationException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/ArgumentException.cs b/src/mscorlib/shared/System/ArgumentException.cs
index de2d775c84..fe65d6443a 100644
--- a/src/mscorlib/shared/System/ArgumentException.cs
+++ b/src/mscorlib/shared/System/ArgumentException.cs
@@ -29,7 +29,7 @@ namespace System
public ArgumentException()
: base(SR.Arg_ArgumentException)
{
- HResult = __HResults.COR_E_ARGUMENT;
+ HResult = HResults.COR_E_ARGUMENT;
}
// Creates a new ArgumentException with its message
@@ -38,27 +38,27 @@ namespace System
public ArgumentException(String message)
: base(message)
{
- HResult = __HResults.COR_E_ARGUMENT;
+ HResult = HResults.COR_E_ARGUMENT;
}
public ArgumentException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_ARGUMENT;
+ HResult = HResults.COR_E_ARGUMENT;
}
public ArgumentException(String message, String paramName, Exception innerException)
: base(message, innerException)
{
_paramName = paramName;
- HResult = __HResults.COR_E_ARGUMENT;
+ HResult = HResults.COR_E_ARGUMENT;
}
public ArgumentException(String message, String paramName)
: base(message)
{
_paramName = paramName;
- HResult = __HResults.COR_E_ARGUMENT;
+ HResult = HResults.COR_E_ARGUMENT;
}
protected ArgumentException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/ArgumentNullException.cs b/src/mscorlib/shared/System/ArgumentNullException.cs
index 74b39fed8e..9b0732b2d2 100644
--- a/src/mscorlib/shared/System/ArgumentNullException.cs
+++ b/src/mscorlib/shared/System/ArgumentNullException.cs
@@ -26,25 +26,25 @@ namespace System
: base(SR.ArgumentNull_Generic)
{
// Use E_POINTER - COM used that for null pointers. Description is "invalid pointer"
- HResult = __HResults.E_POINTER;
+ HResult = HResults.E_POINTER;
}
public ArgumentNullException(String paramName)
: base(SR.ArgumentNull_Generic, paramName)
{
- HResult = __HResults.E_POINTER;
+ HResult = HResults.E_POINTER;
}
public ArgumentNullException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.E_POINTER;
+ HResult = HResults.E_POINTER;
}
public ArgumentNullException(String paramName, String message)
: base(message, paramName)
{
- HResult = __HResults.E_POINTER;
+ HResult = HResults.E_POINTER;
}
protected ArgumentNullException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/ArgumentOutOfRangeException.cs b/src/mscorlib/shared/System/ArgumentOutOfRangeException.cs
index 4721a503b6..a25cd57763 100644
--- a/src/mscorlib/shared/System/ArgumentOutOfRangeException.cs
+++ b/src/mscorlib/shared/System/ArgumentOutOfRangeException.cs
@@ -27,25 +27,25 @@ namespace System
public ArgumentOutOfRangeException()
: base(SR.Arg_ArgumentOutOfRangeException)
{
- HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE;
+ HResult = HResults.COR_E_ARGUMENTOUTOFRANGE;
}
public ArgumentOutOfRangeException(String paramName)
: base(SR.Arg_ArgumentOutOfRangeException, paramName)
{
- HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE;
+ HResult = HResults.COR_E_ARGUMENTOUTOFRANGE;
}
public ArgumentOutOfRangeException(String paramName, String message)
: base(message, paramName)
{
- HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE;
+ HResult = HResults.COR_E_ARGUMENTOUTOFRANGE;
}
public ArgumentOutOfRangeException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE;
+ HResult = HResults.COR_E_ARGUMENTOUTOFRANGE;
}
// We will not use this in the classlibs, but we'll provide it for
@@ -55,7 +55,7 @@ namespace System
: base(message, paramName)
{
_actualValue = actualValue;
- HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE;
+ HResult = HResults.COR_E_ARGUMENTOUTOFRANGE;
}
protected ArgumentOutOfRangeException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/ArithmeticException.cs b/src/mscorlib/shared/System/ArithmeticException.cs
index 2c8abe51fa..6285c8120e 100644
--- a/src/mscorlib/shared/System/ArithmeticException.cs
+++ b/src/mscorlib/shared/System/ArithmeticException.cs
@@ -26,7 +26,7 @@ namespace System
public ArithmeticException()
: base(SR.Arg_ArithmeticException)
{
- HResult = __HResults.COR_E_ARITHMETIC;
+ HResult = HResults.COR_E_ARITHMETIC;
}
// Creates a new ArithmeticException with its message string set to
@@ -36,13 +36,13 @@ namespace System
public ArithmeticException(String message)
: base(message)
{
- HResult = __HResults.COR_E_ARITHMETIC;
+ HResult = HResults.COR_E_ARITHMETIC;
}
public ArithmeticException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_ARITHMETIC;
+ HResult = HResults.COR_E_ARITHMETIC;
}
protected ArithmeticException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/ArrayTypeMismatchException.cs b/src/mscorlib/shared/System/ArrayTypeMismatchException.cs
index d06a450603..6964b1fa12 100644
--- a/src/mscorlib/shared/System/ArrayTypeMismatchException.cs
+++ b/src/mscorlib/shared/System/ArrayTypeMismatchException.cs
@@ -26,7 +26,7 @@ namespace System
public ArrayTypeMismatchException()
: base(SR.Arg_ArrayTypeMismatchException)
{
- HResult = __HResults.COR_E_ARRAYTYPEMISMATCH;
+ HResult = HResults.COR_E_ARRAYTYPEMISMATCH;
}
// Creates a new ArrayMismatchException with its message string set to
@@ -36,13 +36,13 @@ namespace System
public ArrayTypeMismatchException(String message)
: base(message)
{
- HResult = __HResults.COR_E_ARRAYTYPEMISMATCH;
+ HResult = HResults.COR_E_ARRAYTYPEMISMATCH;
}
public ArrayTypeMismatchException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_ARRAYTYPEMISMATCH;
+ HResult = HResults.COR_E_ARRAYTYPEMISMATCH;
}
protected ArrayTypeMismatchException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/BadImageFormatException.cs b/src/mscorlib/shared/System/BadImageFormatException.cs
index adedcb2a3f..a4661fc799 100644
--- a/src/mscorlib/shared/System/BadImageFormatException.cs
+++ b/src/mscorlib/shared/System/BadImageFormatException.cs
@@ -25,31 +25,31 @@ namespace System
public BadImageFormatException()
: base(SR.Arg_BadImageFormatException)
{
- HResult = __HResults.COR_E_BADIMAGEFORMAT;
+ HResult = HResults.COR_E_BADIMAGEFORMAT;
}
public BadImageFormatException(String message)
: base(message)
{
- HResult = __HResults.COR_E_BADIMAGEFORMAT;
+ HResult = HResults.COR_E_BADIMAGEFORMAT;
}
public BadImageFormatException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_BADIMAGEFORMAT;
+ HResult = HResults.COR_E_BADIMAGEFORMAT;
}
public BadImageFormatException(String message, String fileName) : base(message)
{
- HResult = __HResults.COR_E_BADIMAGEFORMAT;
+ HResult = HResults.COR_E_BADIMAGEFORMAT;
_fileName = fileName;
}
public BadImageFormatException(String message, String fileName, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_BADIMAGEFORMAT;
+ HResult = HResults.COR_E_BADIMAGEFORMAT;
_fileName = fileName;
}
@@ -78,7 +78,7 @@ namespace System
if (_message == null)
{
if ((_fileName == null) &&
- (HResult == __HResults.COR_E_EXCEPTION))
+ (HResult == HResults.COR_E_EXCEPTION))
_message = SR.Arg_BadImageFormatException;
else
diff --git a/src/mscorlib/shared/System/BitConverter.cs b/src/mscorlib/shared/System/BitConverter.cs
index e19229dd37..edcdd1ec93 100644
--- a/src/mscorlib/shared/System/BitConverter.cs
+++ b/src/mscorlib/shared/System/BitConverter.cs
@@ -30,6 +30,16 @@ namespace System
return r;
}
+ // Converts a Boolean into a Span of bytes with length one.
+ public static bool TryWriteBytes(Span<byte> destination, bool value)
+ {
+ if (destination.Length < sizeof(byte))
+ return false;
+
+ Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value ? (byte)1: (byte)0);
+ return true;
+ }
+
// Converts a char into an array of bytes with length two.
public static byte[] GetBytes(char value)
{
@@ -38,6 +48,16 @@ namespace System
return bytes;
}
+ // Converts a char into a Span
+ public static bool TryWriteBytes(Span<byte> destination, char value)
+ {
+ if (destination.Length < sizeof(char))
+ return false;
+
+ Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value);
+ return true;
+ }
+
// Converts a short into an array of bytes with length
// two.
public static byte[] GetBytes(short value)
@@ -47,6 +67,16 @@ namespace System
return bytes;
}
+ // Converts a short into a Span
+ public static bool TryWriteBytes(Span<byte> destination, short value)
+ {
+ if (destination.Length < sizeof(short))
+ return false;
+
+ Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value);
+ return true;
+ }
+
// Converts an int into an array of bytes with length
// four.
public static byte[] GetBytes(int value)
@@ -56,6 +86,16 @@ namespace System
return bytes;
}
+ // Converts an int into a Span
+ public static bool TryWriteBytes(Span<byte> destination, int value)
+ {
+ if (destination.Length < sizeof(int))
+ return false;
+
+ Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value);
+ return true;
+ }
+
// Converts a long into an array of bytes with length
// eight.
public static byte[] GetBytes(long value)
@@ -65,6 +105,16 @@ namespace System
return bytes;
}
+ // Converts a long into a Span
+ public static bool TryWriteBytes(Span<byte> destination, long value)
+ {
+ if (destination.Length < sizeof(long))
+ return false;
+
+ Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value);
+ return true;
+ }
+
// Converts an ushort into an array of bytes with
// length two.
[CLSCompliant(false)]
@@ -75,6 +125,17 @@ namespace System
return bytes;
}
+ // Converts a ushort into a Span
+ [CLSCompliant(false)]
+ public static bool TryWriteBytes(Span<byte> destination, ushort value)
+ {
+ if (destination.Length < sizeof(ushort))
+ return false;
+
+ Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value);
+ return true;
+ }
+
// Converts an uint into an array of bytes with
// length four.
[CLSCompliant(false)]
@@ -85,6 +146,17 @@ namespace System
return bytes;
}
+ // Converts a uint into a Span
+ [CLSCompliant(false)]
+ public static bool TryWriteBytes(Span<byte> destination, uint value)
+ {
+ if (destination.Length < sizeof(uint))
+ return false;
+
+ Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value);
+ return true;
+ }
+
// Converts an unsigned long into an array of bytes with
// length eight.
[CLSCompliant(false)]
@@ -95,6 +167,17 @@ namespace System
return bytes;
}
+ // Converts a ulong into a Span
+ [CLSCompliant(false)]
+ public static bool TryWriteBytes(Span<byte> destination, ulong value)
+ {
+ if (destination.Length < sizeof(ulong))
+ return false;
+
+ Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value);
+ return true;
+ }
+
// Converts a float into an array of bytes with length
// four.
public static byte[] GetBytes(float value)
@@ -104,6 +187,16 @@ namespace System
return bytes;
}
+ // Converts a float into a Span
+ public static bool TryWriteBytes(Span<byte> destination, float value)
+ {
+ if (destination.Length < sizeof(float))
+ return false;
+
+ Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value);
+ return true;
+ }
+
// Converts a double into an array of bytes with length
// eight.
public static byte[] GetBytes(double value)
@@ -113,9 +206,27 @@ namespace System
return bytes;
}
+ // Converts a double into a Span
+ public static bool TryWriteBytes(Span<byte> destination, double value)
+ {
+ if (destination.Length < sizeof(double))
+ return false;
+
+ Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value);
+ return true;
+ }
+
// Converts an array of bytes into a char.
public static char ToChar(byte[] value, int startIndex) => unchecked((char)ReadInt16(value, startIndex));
+ // Converts a Span into a char
+ public static char ToChar(ReadOnlySpan<byte> value)
+ {
+ if (value.Length < sizeof(char))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
+ return Unsafe.ReadUnaligned<char>(ref value.DangerousGetPinnableReference());
+ }
+
private static short ReadInt16(byte[] value, int startIndex)
{
if (value == null)
@@ -155,27 +266,78 @@ namespace System
// Converts an array of bytes into a short.
public static short ToInt16(byte[] value, int startIndex) => ReadInt16(value, startIndex);
+ // Converts a Span into a short
+ public static short ToInt16(ReadOnlySpan<byte> value)
+ {
+ if (value.Length < sizeof(short))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
+ return Unsafe.ReadUnaligned<short>(ref value.DangerousGetPinnableReference());
+ }
+
// Converts an array of bytes into an int.
public static int ToInt32(byte[] value, int startIndex) => ReadInt32(value, startIndex);
+ // Converts a Span into an int
+ public static int ToInt32(ReadOnlySpan<byte> value)
+ {
+ if (value.Length < sizeof(int))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
+ return Unsafe.ReadUnaligned<int>(ref value.DangerousGetPinnableReference());
+ }
+
// Converts an array of bytes into a long.
public static long ToInt64(byte[] value, int startIndex) => ReadInt64(value, startIndex);
+ // Converts a Span into a long
+ public static long ToInt64(ReadOnlySpan<byte> value)
+ {
+ if (value.Length < sizeof(long))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
+ return Unsafe.ReadUnaligned<long>(ref value.DangerousGetPinnableReference());
+ }
+
// Converts an array of bytes into an ushort.
//
[CLSCompliant(false)]
public static ushort ToUInt16(byte[] value, int startIndex) => unchecked((ushort)ReadInt16(value, startIndex));
+ // Converts a Span into a ushort
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(ReadOnlySpan<byte> value)
+ {
+ if (value.Length < sizeof(ushort))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
+ return Unsafe.ReadUnaligned<ushort>(ref value.DangerousGetPinnableReference());
+ }
+
// Converts an array of bytes into an uint.
//
[CLSCompliant(false)]
public static uint ToUInt32(byte[] value, int startIndex) => unchecked((uint)ReadInt32(value, startIndex));
+ // Convert a Span into a uint
+ [CLSCompliant(false)]
+ public static uint ToUInt32(ReadOnlySpan<byte> value)
+ {
+ if (value.Length < sizeof(uint))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
+ return Unsafe.ReadUnaligned<uint>(ref value.DangerousGetPinnableReference());
+ }
+
// Converts an array of bytes into an unsigned long.
//
[CLSCompliant(false)]
public static ulong ToUInt64(byte[] value, int startIndex) => unchecked((ulong)ReadInt64(value, startIndex));
+ // Converts a Span into an unsigned long
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(ReadOnlySpan<byte> value)
+ {
+ if (value.Length < sizeof(ulong))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
+ return Unsafe.ReadUnaligned<ulong>(ref value.DangerousGetPinnableReference());
+ }
+
// Converts an array of bytes into a float.
public static unsafe float ToSingle(byte[] value, int startIndex)
{
@@ -183,6 +345,14 @@ namespace System
return *(float*)&val;
}
+ // Converts a Span into a float
+ public static float ToSingle(ReadOnlySpan<byte> value)
+ {
+ if (value.Length < sizeof(float))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
+ return Unsafe.ReadUnaligned<float>(ref value.DangerousGetPinnableReference());
+ }
+
// Converts an array of bytes into a double.
public static unsafe double ToDouble(byte[] value, int startIndex)
{
@@ -190,6 +360,14 @@ namespace System
return *(double*)&val;
}
+ // Converts a Span into a double
+ public static double ToDouble(ReadOnlySpan<byte> value)
+ {
+ if (value.Length < sizeof(double))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
+ return Unsafe.ReadUnaligned<double>(ref value.DangerousGetPinnableReference());
+ }
+
private static char GetHexValue(int i)
{
Debug.Assert(i >= 0 && i < 16, "i is out of range.");
@@ -298,6 +476,13 @@ namespace System
return value[startIndex] != 0;
}
+ public static bool ToBoolean(ReadOnlySpan<byte> value)
+ {
+ if (value.Length < sizeof(byte))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
+ return Unsafe.ReadUnaligned<byte>(ref value.DangerousGetPinnableReference()) != 0;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe long DoubleToInt64Bits(double value)
{
diff --git a/src/mscorlib/src/System/Boolean.cs b/src/mscorlib/shared/System/Boolean.cs
index a1f5064964..a6ffb6de83 100644
--- a/src/mscorlib/src/System/Boolean.cs
+++ b/src/mscorlib/shared/System/Boolean.cs
@@ -12,16 +12,15 @@
**
===========================================================*/
-using System;
-using System.Globalization;
using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
namespace System
{
- // The Boolean class provides the
- // object representation of the boolean primitive type.
[Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public struct Boolean : IComparable, IConvertible, IComparable<Boolean>, IEquatable<Boolean>
{
//
@@ -34,7 +33,7 @@ namespace System
internal const int True = 1;
// The false value.
- //
+ //
internal const int False = 0;
@@ -95,11 +94,7 @@ namespace System
public String ToString(IFormatProvider provider)
{
- if (false == m_value)
- {
- return FalseLiteral;
- }
- return TrueLiteral;
+ return ToString();
}
// Determines whether two Boolean objects are equal.
@@ -114,7 +109,7 @@ namespace System
return (m_value == ((Boolean)obj).m_value);
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public bool Equals(Boolean obj)
{
return m_value == obj;
@@ -171,35 +166,36 @@ namespace System
public static Boolean Parse(String value)
{
if (value == null) throw new ArgumentNullException(nameof(value));
- Contract.EndContractBlock();
- Boolean result = false;
- if (!TryParse(value, out result))
- {
- throw new FormatException(SR.Format_BadBoolean);
- }
- else
- {
- return result;
- }
+ return Parse(value.AsReadOnlySpan());
}
+ public static bool Parse(ReadOnlySpan<char> value) =>
+ TryParse(value, out bool result) ? result : throw new FormatException(SR.Format_BadBoolean);
+
// Determines whether a String represents true or false.
//
public static Boolean TryParse(String value, out Boolean result)
{
- result = false;
if (value == null)
{
+ result = false;
return false;
}
- // For perf reasons, let's first see if they're equal, then do the
- // trim to get rid of white space, and check again.
- if (TrueLiteral.Equals(value, StringComparison.OrdinalIgnoreCase))
+
+ return TryParse(value.AsReadOnlySpan(), out result);
+ }
+
+ public static bool TryParse(ReadOnlySpan<char> value, out bool result)
+ {
+ ReadOnlySpan<char> trueSpan = TrueLiteral.AsReadOnlySpan();
+ if (StringSpanHelpers.Equals(trueSpan, value, StringComparison.OrdinalIgnoreCase))
{
result = true;
return true;
}
- if (FalseLiteral.Equals(value, StringComparison.OrdinalIgnoreCase))
+
+ ReadOnlySpan<char> falseSpan = FalseLiteral.AsReadOnlySpan();
+ if (StringSpanHelpers.Equals(falseSpan, value, StringComparison.OrdinalIgnoreCase))
{
result = false;
return true;
@@ -208,27 +204,27 @@ namespace System
// Special case: Trim whitespace as well as null characters.
value = TrimWhiteSpaceAndNull(value);
- if (TrueLiteral.Equals(value, StringComparison.OrdinalIgnoreCase))
+ if (StringSpanHelpers.Equals(trueSpan, value, StringComparison.OrdinalIgnoreCase))
{
result = true;
return true;
}
- if (FalseLiteral.Equals(value, StringComparison.OrdinalIgnoreCase))
+ if (StringSpanHelpers.Equals(falseSpan, value, StringComparison.OrdinalIgnoreCase))
{
result = false;
return true;
}
+ result = false;
return false;
}
- private static String TrimWhiteSpaceAndNull(String value)
+ private static ReadOnlySpan<char> TrimWhiteSpaceAndNull(ReadOnlySpan<char> value)
{
- int start = 0;
- int end = value.Length - 1;
- char nullChar = (char)0x0000;
+ const char nullChar = (char)0x0000;
+ int start = 0;
while (start < value.Length)
{
if (!Char.IsWhiteSpace(value[start]) && value[start] != nullChar)
@@ -238,6 +234,7 @@ namespace System
start++;
}
+ int end = value.Length - 1;
while (end >= start)
{
if (!Char.IsWhiteSpace(value[end]) && value[end] != nullChar)
@@ -247,7 +244,7 @@ namespace System
end--;
}
- return value.Substring(start, end - start + 1);
+ return value.Slice(start, end - start + 1);
}
//
diff --git a/src/mscorlib/src/System/Buffers/ArrayPoolEventSource.cs b/src/mscorlib/shared/System/Buffers/ArrayPoolEventSource.cs
index 9482744144..9482744144 100644
--- a/src/mscorlib/src/System/Buffers/ArrayPoolEventSource.cs
+++ b/src/mscorlib/shared/System/Buffers/ArrayPoolEventSource.cs
diff --git a/src/mscorlib/shared/System/Buffers/IRetainable.cs b/src/mscorlib/shared/System/Buffers/IRetainable.cs
new file mode 100644
index 0000000000..8d71fc614a
--- /dev/null
+++ b/src/mscorlib/shared/System/Buffers/IRetainable.cs
@@ -0,0 +1,15 @@
+// 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;
+using System.Runtime.CompilerServices;
+
+namespace System.Buffers
+{
+ public interface IRetainable
+ {
+ void Retain();
+ bool Release();
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/Buffers/MemoryHandle.cs b/src/mscorlib/shared/System/Buffers/MemoryHandle.cs
new file mode 100644
index 0000000000..fcdc7fe17f
--- /dev/null
+++ b/src/mscorlib/shared/System/Buffers/MemoryHandle.cs
@@ -0,0 +1,44 @@
+// 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;
+using System.Runtime.InteropServices;
+
+namespace System.Buffers
+{
+ public unsafe struct MemoryHandle : IDisposable
+ {
+ private IRetainable _owner;
+ private void* _pointer;
+ private GCHandle _handle;
+
+ [CLSCompliant(false)]
+ public MemoryHandle(IRetainable owner, void* pinnedPointer = null, GCHandle handle = default(GCHandle))
+ {
+ _owner = owner;
+ _pointer = pinnedPointer;
+ _handle = handle;
+ }
+
+ [CLSCompliant(false)]
+ public void* PinnedPointer => _pointer;
+
+ public void Dispose()
+ {
+ if (_handle.IsAllocated)
+ {
+ _handle.Free();
+ }
+
+ if (_owner != null)
+ {
+ _owner.Release();
+ _owner = null;
+ }
+
+ _pointer = null;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/Buffers/OwnedMemory.cs b/src/mscorlib/shared/System/Buffers/OwnedMemory.cs
new file mode 100644
index 0000000000..e3a89f7ef4
--- /dev/null
+++ b/src/mscorlib/shared/System/Buffers/OwnedMemory.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.Runtime;
+using System.Runtime.CompilerServices;
+
+namespace System.Buffers
+{
+ public abstract class OwnedMemory<T> : IDisposable, IRetainable
+ {
+ public abstract int Length { get; }
+
+ public abstract Span<T> AsSpan();
+
+ public Memory<T> AsMemory
+ {
+ get
+ {
+ if (IsDisposed)
+ {
+ ThrowHelper.ThrowObjectDisposedException(nameof(OwnedMemory<T>), ExceptionResource.Memory_ThrowIfDisposed);
+ }
+ return new Memory<T>(this, 0, Length);
+ }
+ }
+
+ public abstract MemoryHandle Pin();
+
+ protected internal abstract bool TryGetArray(out ArraySegment<T> arraySegment);
+
+ public void Dispose()
+ {
+ if (IsRetained)
+ {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Memory_OutstandingReferences);
+ }
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected abstract void Dispose(bool disposing);
+
+ protected abstract bool IsRetained { get; }
+
+ public abstract bool IsDisposed { get; }
+
+ public abstract void Retain();
+
+ public abstract bool Release();
+
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/src/System/Byte.cs b/src/mscorlib/shared/System/Byte.cs
index fafdcbb535..a0f8ff8c29 100644
--- a/src/mscorlib/src/System/Byte.cs
+++ b/src/mscorlib/shared/System/Byte.cs
@@ -12,22 +12,18 @@
**
===========================================================*/
-
-using System;
+using System.Diagnostics.Contracts;
using System.Globalization;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Diagnostics.Contracts;
+using System.Runtime.Versioning;
namespace System
{
- // The Byte class extends the Value class and
- // provides object representation of the byte primitive type.
- //
[Serializable]
- [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct Byte : IComparable, IFormattable, IConvertible
- , IComparable<Byte>, IEquatable<Byte>
+ [StructLayout(LayoutKind.Sequential)]
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public struct Byte : IComparable, IConvertible, IFormattable, IComparable<Byte>, IEquatable<Byte>
{
private byte m_value; // Do not rename (binary serialization)
@@ -73,7 +69,7 @@ namespace System
return m_value == ((Byte)obj).m_value;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public bool Equals(Byte obj)
{
return m_value == obj;
@@ -88,23 +84,25 @@ namespace System
[Pure]
public static byte Parse(String s)
{
- return Parse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
[Pure]
public static byte Parse(String s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
- return Parse(s, style, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo);
}
[Pure]
public static byte Parse(String s, IFormatProvider provider)
{
- return Parse(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
-
// Parses an unsigned byte from a String in the given style. If
// a NumberFormatInfo isn't specified, the current culture's
// NumberFormatInfo is assumed.
@@ -112,10 +110,17 @@ namespace System
public static byte Parse(String s, NumberStyles style, IFormatProvider provider)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ public static byte Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Parse(s, style, NumberFormatInfo.GetInstance(provider));
}
- private static byte Parse(String s, NumberStyles style, NumberFormatInfo info)
+ private static byte Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
{
int i = 0;
try
@@ -133,16 +138,35 @@ namespace System
public static bool TryParse(String s, out Byte result)
{
- return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return TryParse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Byte result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ public static bool TryParse(ReadOnlySpan<char> s, out byte result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result);
}
- private static bool TryParse(String s, NumberStyles style, NumberFormatInfo info, out Byte result)
+ private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out Byte result)
{
result = 0;
int i;
diff --git a/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs b/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs
index cdd6faf030..c32bc623ba 100644
--- a/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs
+++ b/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs
@@ -12,19 +12,19 @@ namespace System.Collections.Generic
public KeyNotFoundException()
: base(SR.Arg_KeyNotFound)
{
- HResult = __HResults.COR_E_KEYNOTFOUND;
+ HResult = HResults.COR_E_KEYNOTFOUND;
}
public KeyNotFoundException(String message)
: base(message)
{
- HResult = __HResults.COR_E_KEYNOTFOUND;
+ HResult = HResults.COR_E_KEYNOTFOUND;
}
public KeyNotFoundException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_KEYNOTFOUND;
+ HResult = HResults.COR_E_KEYNOTFOUND;
}
protected KeyNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/Collections/ICollection.cs b/src/mscorlib/shared/System/Collections/ICollection.cs
index 80ea092363..8fd6a96b48 100644
--- a/src/mscorlib/shared/System/Collections/ICollection.cs
+++ b/src/mscorlib/shared/System/Collections/ICollection.cs
@@ -29,7 +29,7 @@ namespace System.Collections
// implementation of a collection, and use one of the internal objects
// found in that code.
//
- // In the absense of a static Synchronized method on a collection,
+ // In the absence of a static Synchronized method on a collection,
// the expected usage for SyncRoot would look like this:
//
// ICollection col = ...
diff --git a/src/mscorlib/src/System/Collections/ListDictionaryInternal.cs b/src/mscorlib/shared/System/Collections/ListDictionaryInternal.cs
index 17eb89a2b2..681e51a329 100644
--- a/src/mscorlib/src/System/Collections/ListDictionaryInternal.cs
+++ b/src/mscorlib/shared/System/Collections/ListDictionaryInternal.cs
@@ -21,7 +21,12 @@ namespace System.Collections
/// This should not be used if performance is important for large numbers of elements.
[Serializable]
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- internal class ListDictionaryInternal : IDictionary
+#if CORECLR
+ internal
+#else
+ public
+#endif
+ class ListDictionaryInternal : IDictionary
{
private DictionaryNode head; // Do not rename (binary serialization)
private int version; // Do not rename (binary serialization)
diff --git a/src/mscorlib/src/System/Collections/ObjectModel/Collection.cs b/src/mscorlib/shared/System/Collections/ObjectModel/Collection.cs
index 8e5de70e72..75e88eccb3 100644
--- a/src/mscorlib/src/System/Collections/ObjectModel/Collection.cs
+++ b/src/mscorlib/shared/System/Collections/ObjectModel/Collection.cs
@@ -2,13 +2,8 @@
// 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.Collections;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Runtime;
namespace System.Collections.ObjectModel
{
@@ -243,7 +238,7 @@ namespace System.Collections.ObjectModel
{
//
// Catch the obvious case assignment will fail.
- // We can found all possible problems by doing the check though.
+ // We can't find all possible problems by doing the check though.
// For example, if the element type of the Array is derived from T,
// we can't figure out if we can successfully copy the element beforehand.
//
diff --git a/src/mscorlib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/mscorlib/shared/System/Collections/ObjectModel/ReadOnlyCollection.cs
index 03c7d45e16..f1d2a0969b 100644
--- a/src/mscorlib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs
+++ b/src/mscorlib/shared/System/Collections/ObjectModel/ReadOnlyCollection.cs
@@ -2,13 +2,8 @@
// 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.Collections;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Runtime;
namespace System.Collections.ObjectModel
{
@@ -175,7 +170,7 @@ namespace System.Collections.ObjectModel
{
//
// Catch the obvious case assignment will fail.
- // We can found all possible problems by doing the check though.
+ // We can't find all possible problems by doing the check though.
// For example, if the element type of the Array is derived from T,
// we can't figure out if we can successfully copy the element beforehand.
//
diff --git a/src/mscorlib/shared/System/Convert.cs b/src/mscorlib/shared/System/Convert.cs
index 576f78f1f1..02fbbdbc3e 100644
--- a/src/mscorlib/shared/System/Convert.cs
+++ b/src/mscorlib/shared/System/Convert.cs
@@ -84,7 +84,7 @@ namespace System
// of the conversion method.
// Constant representing the database null value. This value is used in
- // database applications to indicate the absense of a known value. Note
+ // database applications to indicate the absence of a known value. Note
// that Value.DBNull is NOT the same as a null object reference, which is
// represented by Value.Empty.
//
@@ -2670,7 +2670,7 @@ namespace System
/// Converts the specified string, which encodes binary data as Base64 digits, to the equivalent byte array.
/// </summary>
/// <param name="s">The string to convert</param>
- /// <returns>The array of bytes represented by the specifed Base64 string.</returns>
+ /// <returns>The array of bytes represented by the specified Base64 string.</returns>
public static Byte[] FromBase64String(String s)
{
// "s" is an unfortunate parameter name, but we need to keep it for backward compat.
diff --git a/src/mscorlib/shared/System/DBNull.cs b/src/mscorlib/shared/System/DBNull.cs
index 4f4d64bf66..3cee2b15c8 100644
--- a/src/mscorlib/shared/System/DBNull.cs
+++ b/src/mscorlib/shared/System/DBNull.cs
@@ -6,17 +6,23 @@ using System.Runtime.Serialization;
namespace System
{
+ [Serializable]
public sealed class DBNull : ISerializable, IConvertible
{
private DBNull()
{
}
-
+
+ private DBNull(SerializationInfo info, StreamingContext context)
+ {
+ throw new NotSupportedException(SR.NotSupported_DBNullSerial);
+ }
+
public static readonly DBNull Value = new DBNull();
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
- throw new PlatformNotSupportedException();
+ UnitySerializationHolder.GetUnitySerializationInfo(info, UnitySerializationHolder.NullUnity);
}
public override string ToString()
diff --git a/src/mscorlib/shared/System/DataMisalignedException.cs b/src/mscorlib/shared/System/DataMisalignedException.cs
index ff5b29f1cf..d8d36b5cb7 100644
--- a/src/mscorlib/shared/System/DataMisalignedException.cs
+++ b/src/mscorlib/shared/System/DataMisalignedException.cs
@@ -18,19 +18,19 @@ namespace System
public DataMisalignedException()
: base(SR.Arg_DataMisalignedException)
{
- HResult = __HResults.COR_E_DATAMISALIGNED;
+ HResult = HResults.COR_E_DATAMISALIGNED;
}
public DataMisalignedException(String message)
: base(message)
{
- HResult = __HResults.COR_E_DATAMISALIGNED;
+ HResult = HResults.COR_E_DATAMISALIGNED;
}
public DataMisalignedException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_DATAMISALIGNED;
+ HResult = HResults.COR_E_DATAMISALIGNED;
}
}
}
diff --git a/src/mscorlib/shared/System/DateTime.cs b/src/mscorlib/shared/System/DateTime.cs
index 16a75fdfa6..e72654a8d6 100644
--- a/src/mscorlib/shared/System/DateTime.cs
+++ b/src/mscorlib/shared/System/DateTime.cs
@@ -129,7 +129,7 @@ namespace System
private const String TicksField = "ticks"; // Do not rename (binary serialization)
private const String DateDataField = "dateData"; // Do not rename (binary serialization)
- // The data is stored as an unsigned 64-bit integeter
+ // The data is stored as an unsigned 64-bit integer
// Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1/1/0001 12:00am, up until the value
// 12/31/9999 23:59:59.9999999
// Bits 63-64: A four-state value that describes the DateTimeKind value of the date time, with a 2nd
@@ -1025,11 +1025,7 @@ namespace System
}
// Returns a DateTime representing the current date and time. The
- // resolution of the returned value depends on the system timer. For
- // Windows NT 3.5 and later the timer resolution is approximately 10ms,
- // for Windows NT 3.1 it is approximately 16ms, and for Windows 95 and 98
- // it is approximately 55ms.
- //
+ // resolution of the returned value depends on the system timer.
public static DateTime Now
{
get
diff --git a/src/mscorlib/shared/System/DateTimeOffset.cs b/src/mscorlib/shared/System/DateTimeOffset.cs
index ab35bdb0fe..e3366e2a7d 100644
--- a/src/mscorlib/shared/System/DateTimeOffset.cs
+++ b/src/mscorlib/shared/System/DateTimeOffset.cs
@@ -129,11 +129,7 @@ namespace System
}
// Returns a DateTimeOffset representing the current date and time. The
- // resolution of the returned value depends on the system timer. For
- // Windows NT 3.5 and later the timer resolution is approximately 10ms,
- // for Windows NT 3.1 it is approximately 16ms, and for Windows 95 and 98
- // it is approximately 55ms.
- //
+ // resolution of the returned value depends on the system timer.
public static DateTimeOffset Now
{
get
diff --git a/src/mscorlib/shared/System/DefaultBinder.cs b/src/mscorlib/shared/System/DefaultBinder.cs
index 9adf702a02..b6c12b121d 100644
--- a/src/mscorlib/shared/System/DefaultBinder.cs
+++ b/src/mscorlib/shared/System/DefaultBinder.cs
@@ -42,7 +42,7 @@ namespace System
state = null;
-#region Map named parameters to candidate parameter postions
+#region Map named parameters to candidate parameter positions
// We are creating an paramOrder array to act as a mapping
// between the order of the args and the actual order of the
// parameters in the method. This order may differ because
@@ -530,7 +530,7 @@ namespace System
for (i = 0; i < types.Length; i++)
{
realTypes[i] = types[i].UnderlyingSystemType;
- if (!(realTypes[i].IsRuntimeImplemented()))
+ if (!(realTypes[i].IsRuntimeImplemented() || realTypes[i] is SignatureType))
throw new ArgumentException(SR.Arg_MustBeType, nameof(types));
}
types = realTypes;
@@ -552,19 +552,30 @@ namespace System
for (j = 0; j < types.Length; j++)
{
Type pCls = par[j].ParameterType;
- if (pCls == types[j])
+ if (types[j].MatchesParameterTypeExactly(par[j]))
continue;
if (pCls == typeof(object))
continue;
+
+ Type type = types[j];
+ if (type is SignatureType signatureType)
+ {
+ if (!(candidates[i] is MethodInfo methodInfo))
+ break;
+ type = signatureType.TryResolveAgainstGenericMethod(methodInfo);
+ if (type == null)
+ break;
+ }
+
if (pCls.IsPrimitive)
{
- if (!(types[j].UnderlyingSystemType.IsRuntimeImplemented()) ||
- !CanChangePrimitive(types[j].UnderlyingSystemType, pCls.UnderlyingSystemType))
+ if (!(type.UnderlyingSystemType.IsRuntimeImplemented()) ||
+ !CanChangePrimitive(type.UnderlyingSystemType, pCls.UnderlyingSystemType))
break;
}
else
{
- if (!pCls.IsAssignableFrom(types[j]))
+ if (!pCls.IsAssignableFrom(type))
break;
}
}
@@ -918,11 +929,22 @@ namespace System
if (c1 == c2)
return 0;
- if (c1 == t)
- return 1;
+ if (t is SignatureType signatureType)
+ {
+ if (signatureType.MatchesExactly(c1))
+ return 1;
- if (c2 == t)
- return 2;
+ if (signatureType.MatchesExactly(c2))
+ return 2;
+ }
+ else
+ {
+ if (c1 == t)
+ return 1;
+
+ if (c2 == t)
+ return 2;
+ }
bool c1FromC2;
bool c2FromC1;
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs
index 5292551314..a74125a35a 100644
--- a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs
@@ -87,7 +87,8 @@ namespace System.Diagnostics.Tracing
private long m_allKeywordMask; // Match all keyword
private List<SessionInfo> m_liveSessions; // current live sessions (Tuple<sessionIdBit, etwSessionId>)
private bool m_enabled; // Enabled flag from Trace callback
- private Guid m_providerId; // Control Guid
+ private string m_providerName; // Control name
+ private Guid m_providerId; // Control Guid
internal bool m_disposed; // when true provider has unregistered
[ThreadStatic]
@@ -140,16 +141,19 @@ namespace System.Diagnostics.Tracing
// <SatisfiesLinkDemand Name="Win32Exception..ctor(System.Int32)" />
// <ReferencesCritical Name="Method: EtwEnableCallBack(Guid&, Int32, Byte, Int64, Int64, Void*, Void*):Void" Ring="1" />
// </SecurityKernel>
- internal unsafe void Register(Guid providerGuid)
+ internal unsafe void Register(EventSource eventSource)
{
- m_providerId = providerGuid;
uint status;
m_etwCallback = new UnsafeNativeMethods.ManifestEtw.EtwEnableCallback(EtwEnableCallBack);
- status = EventRegister(ref m_providerId, m_etwCallback);
+ status = EventRegister(eventSource, m_etwCallback);
if (status != 0)
{
- throw new ArgumentException(Win32Native.GetMessage(unchecked((int)status)));
+#if PLATFORM_WINDOWS
+ throw new ArgumentException(Interop.Kernel32.GetMessage(unchecked((int)status)));
+#else
+ throw new ArgumentException(Convert.ToString(unchecked((int)status)));
+#endif
}
}
@@ -488,7 +492,7 @@ namespace System.Diagnostics.Tracing
// at least some of the time.
// Determine our session from what is in the registry.
- string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}";
+ string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerName + "}";
if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8)
regKey = @"Software" + @"\Wow6432Node" + regKey;
else
@@ -563,7 +567,7 @@ namespace System.Diagnostics.Tracing
if (filterData == null)
{
#if (!ES_BUILD_PCL && !ES_BUILD_PN && PLATFORM_WINDOWS)
- string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}";
+ string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerName + "}";
if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8)
regKey = @"HKEY_LOCAL_MACHINE\Software" + @"\Wow6432Node" + regKey;
else
@@ -1184,11 +1188,12 @@ namespace System.Diagnostics.Tracing
// These are look-alikes to the Manifest based ETW OS APIs that have been shimmed to work
// either with Manifest ETW or Classic ETW (if Manifest based ETW is not available).
- private unsafe uint EventRegister(ref Guid providerId, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback)
+ private unsafe uint EventRegister(EventSource eventSource, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback)
{
- m_providerId = providerId;
+ m_providerName = eventSource.Name;
+ m_providerId = eventSource.Guid;
m_etwCallback = enableCallback;
- return m_eventProvider.EventRegister(ref providerId, enableCallback, null, ref m_regHandle);
+ return m_eventProvider.EventRegister(eventSource, enableCallback, null, ref m_regHandle);
}
private uint EventUnregister(long registrationHandle)
@@ -1221,11 +1226,12 @@ namespace System.Diagnostics.Tracing
{
// Register an event provider.
unsafe uint IEventProvider.EventRegister(
- ref Guid providerId,
+ EventSource eventSource,
UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback,
void* callbackContext,
ref long registrationHandle)
{
+ Guid providerId = eventSource.Guid;
return UnsafeNativeMethods.ManifestEtw.EventRegister(
ref providerId,
enableCallback,
@@ -1278,7 +1284,7 @@ namespace System.Diagnostics.Tracing
internal sealed class NoOpEventProvider : IEventProvider
{
unsafe uint IEventProvider.EventRegister(
- ref Guid providerId,
+ EventSource eventSource,
UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback,
void* callbackContext,
ref long registrationHandle)
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs
index d8eec73931..4e436e7baf 100644
--- a/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs
@@ -265,7 +265,7 @@ namespace System.Diagnostics.Tracing
public Guid Guid { get { return m_guid; } }
/// <summary>
- /// Returns true if the eventSource has been enabled at all. This is the prefered test
+ /// Returns true if the eventSource has been enabled at all. This is the preferred test
/// to be performed before a relatively expensive EventSource operation.
/// </summary>
[SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
@@ -572,11 +572,11 @@ namespace System.Diagnostics.Tracing
/// <summary>
/// EventSources can have arbitrary string key-value pairs associated with them called Traits.
/// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
- /// (e.g. like the built in ETW listener). These traits are specififed at EventSource
+ /// (e.g. like the built in ETW listener). These traits are specified at EventSource
/// construction time and can be retrieved by using this GetTrait API.
/// </summary>
/// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
- /// <returns>The value string associated iwth key. Will return null if there is no such key.</returns>
+ /// <returns>The value string associated with key. Will return null if there is no such key.</returns>
public string GetTrait(string key)
{
if (m_traits != null)
@@ -661,7 +661,7 @@ namespace System.Diagnostics.Tracing
///
/// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
/// The first string is the key and the second is the value. These are not interpreted by EventSource
- /// itself but may be interprated the listeners. Can be fetched with GetTrait(string).
+ /// itself but may be interpreted the listeners. Can be fetched with GetTrait(string).
/// </summary>
/// <param name="settings">See EventSourceSettings for more.</param>
/// <param name="traits">A collection of key-value strings (must be an even number).</param>
@@ -1560,7 +1560,7 @@ namespace System.Diagnostics.Tracing
// Register the provider with ETW
var provider = new OverideEventProvider(this);
- provider.Register(eventSourceGuid);
+ provider.Register(this);
#endif
// Add the eventSource to the global (weak) list.
// This also sets m_id, which is the index in the list.
@@ -4942,10 +4942,10 @@ namespace System.Diagnostics.Tracing
get
{
// For contract based events we create the list lazily.
- if (m_payloadNames == null)
+ // You can have m_payloadNames be null in the TraceLogging case (EventID < 0) so only
+ // do the lazy init if you know it is contract based (EventID >= 0)
+ if (EventId >= 0 && m_payloadNames == null)
{
- // Self described events are identified by id -1.
- Debug.Assert(EventId != -1);
var names = new List<string>();
foreach (var parameter in m_eventSource.m_eventData[EventId].Parameters)
@@ -5521,7 +5521,7 @@ namespace System.Diagnostics.Tracing
/// Returns a session mask representing all sessions in which the activity
/// associated with the current thread is allowed through the activity filter.
/// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally
- /// most of the time this is false as you can guarentee this event is NOT a
+ /// most of the time this is false as you can guarantee this event is NOT a
/// triggering event. If 'triggeringEvent' is true, then it checks the
/// 'EventSource' and 'eventID' of the event being logged to see if it is actually
/// a trigger. If so it activates the current activity.
@@ -5622,7 +5622,7 @@ namespace System.Diagnostics.Tracing
/// <summary>
/// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid'
/// to list of active activities IF 'currentActivityId' is also active. Passing in a null
- /// value for 'currentActivityid' is an indication tha caller has already verified
+ /// value for 'currentActivityid' is an indication the caller has already verified
/// that the current activity is active.
/// </summary>
unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID)
@@ -5642,7 +5642,7 @@ namespace System.Diagnostics.Tracing
// make sure current activity is still in the set:
activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount;
}
- // add child activity to list of actives
+ // add child activity to list of activities
activeActivities[*childActivityID] = Environment.TickCount;
}
@@ -6070,11 +6070,11 @@ namespace System.Diagnostics.Tracing
/// (m_EventEnabled) for a particular EventSource X EventListener tuple
///
/// Thus a single EventListener may have many EventDispatchers (one for every EventSource
- /// that that EventListener has activate) and a Single EventSource may also have many
+ /// that EventListener has activate) and a Single EventSource may also have many
/// event Dispatchers (one for every EventListener that has activated it).
///
/// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
- /// one EventListener (alhtough EventDispatcher does not 'remember' the EventSource it is
+ /// one EventListener (although EventDispatcher does not 'remember' the EventSource it is
/// associated with.
/// </summary>
internal class EventDispatcher
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/IEventProvider.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/IEventProvider.cs
index 71a2fe4d44..966dac2fef 100644
--- a/src/mscorlib/shared/System/Diagnostics/Tracing/IEventProvider.cs
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/IEventProvider.cs
@@ -15,7 +15,7 @@ namespace System.Diagnostics.Tracing
{
// Register an event provider.
unsafe uint EventRegister(
- ref Guid providerId,
+ EventSource eventSource,
UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback,
void* callbackContext,
ref long registrationHandle);
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs
index 38c1767462..acc3eeb233 100644
--- a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs
@@ -77,8 +77,8 @@ namespace System.Diagnostics.Tracing
/// <summary>
/// Writes a Start event with the specified name and data. If the start event is not active (because the provider
- /// is not on or keyword-level indiates the event is off, then the returned activity is simply the 'this' poitner
- /// and it is effectively like the Start d
+ /// is not on or keyword-level indicates the event is off, then the returned activity is simply the 'this' pointer
+ /// and it is effectively like start did not get called.
///
/// A new activityID GUID is generated and the returned
/// EventSourceActivity remembers this activity and will mark every event (including the start stop and any writes)
@@ -139,7 +139,7 @@ namespace System.Diagnostics.Tracing
}
/// <summary>
/// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop')
- /// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that
+ /// This can be useful to indicate unusual ways of stopping (but it is still STRONGLY recommended that
/// you start with the same prefix used for the start event and you end with the 'Stop' suffix.
/// </summary>
public void Stop<T>(string eventName)
@@ -149,7 +149,7 @@ namespace System.Diagnostics.Tracing
}
/// <summary>
/// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop')
- /// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that
+ /// This can be useful to indicate unusual ways of stopping (but it is still STRONGLY recommended that
/// you start with the same prefix used for the start event and you end with the 'Stop' suffix.
/// </summary>
public void Stop<T>(string eventName, T data)
@@ -158,7 +158,7 @@ namespace System.Diagnostics.Tracing
}
/// <summary>
- /// Writes an event associated with this activity to the eventSource associted with this activity.
+ /// Writes an event associated with this activity to the eventSource associated with this activity.
/// May only be called when the activity is in the Started state.
/// </summary>
/// <param name="eventName">
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs
index 529948daf8..cc416a96d9 100644
--- a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs
@@ -182,7 +182,7 @@ namespace System.Diagnostics.Tracing
/// <summary>
/// Core type.
- /// Special case: Struct indicates that this field plus the the
+ /// Special case: Struct indicates that this field plus the
/// subsequent N logical fields are to be considered as one logical
/// field (i.e. a nested structure). The OutType is used to encode N.
/// The maximum value for N is 127. This field has no payload by
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
index aba1671ae2..a1218d1a7f 100644
--- a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
@@ -80,7 +80,7 @@ namespace System.Diagnostics.Tracing
///
/// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
/// The first string is the key and the second is the value. These are not interpreted by EventSource
- /// itself but may be interprated the listeners. Can be fetched with GetTrait(string).
+ /// itself but may be interpreted the listeners. Can be fetched with GetTrait(string).
/// </summary>
/// <param name="eventSourceName">
/// The name of the event source. Must not be null.
@@ -442,7 +442,10 @@ namespace System.Diagnostics.Tracing
var pinCount = eventTypes.pinCount;
var scratch = stackalloc byte[eventTypes.scratchSize];
var descriptors = stackalloc EventData[eventTypes.dataCount + 3];
+
var pins = stackalloc GCHandle[pinCount];
+ for (int i = 0; i < pinCount; i++)
+ pins[i] = default(GCHandle);
fixed (byte*
pMetadata0 = this.providerMetadata,
@@ -619,7 +622,10 @@ namespace System.Diagnostics.Tracing
var pinCount = eventTypes.pinCount;
var scratch = stackalloc byte[eventTypes.scratchSize];
var descriptors = stackalloc EventData[eventTypes.dataCount + 3];
+
var pins = stackalloc GCHandle[pinCount];
+ for (int i = 0; i < pinCount; i++)
+ pins[i] = default(GCHandle);
fixed (byte*
pMetadata0 = this.providerMetadata,
@@ -744,9 +750,9 @@ namespace System.Diagnostics.Tracing
{
DataCollector.ThreadInstance.Disable();
- for (int i = 0; i != cPins; i++)
+ for (int i = 0; i < cPins; i++)
{
- if (IntPtr.Zero != (IntPtr)pPins[i])
+ if (pPins[i].IsAllocated)
{
pPins[i].Free();
}
diff --git a/src/mscorlib/shared/System/DivideByZeroException.cs b/src/mscorlib/shared/System/DivideByZeroException.cs
index 0fad2f8d56..ad74bde0fd 100644
--- a/src/mscorlib/shared/System/DivideByZeroException.cs
+++ b/src/mscorlib/shared/System/DivideByZeroException.cs
@@ -20,19 +20,19 @@ namespace System
public DivideByZeroException()
: base(SR.Arg_DivideByZero)
{
- HResult = __HResults.COR_E_DIVIDEBYZERO;
+ HResult = HResults.COR_E_DIVIDEBYZERO;
}
public DivideByZeroException(String message)
: base(message)
{
- HResult = __HResults.COR_E_DIVIDEBYZERO;
+ HResult = HResults.COR_E_DIVIDEBYZERO;
}
public DivideByZeroException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_DIVIDEBYZERO;
+ HResult = HResults.COR_E_DIVIDEBYZERO;
}
protected DivideByZeroException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/DllNotFoundException.cs b/src/mscorlib/shared/System/DllNotFoundException.cs
index 8c69e45eda..82d5bdd0c5 100644
--- a/src/mscorlib/shared/System/DllNotFoundException.cs
+++ b/src/mscorlib/shared/System/DllNotFoundException.cs
@@ -21,19 +21,19 @@ namespace System
public DllNotFoundException()
: base(SR.Arg_DllNotFoundException)
{
- HResult = __HResults.COR_E_DLLNOTFOUND;
+ HResult = HResults.COR_E_DLLNOTFOUND;
}
public DllNotFoundException(String message)
: base(message)
{
- HResult = __HResults.COR_E_DLLNOTFOUND;
+ HResult = HResults.COR_E_DLLNOTFOUND;
}
public DllNotFoundException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_DLLNOTFOUND;
+ HResult = HResults.COR_E_DLLNOTFOUND;
}
protected DllNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/src/System/Double.cs b/src/mscorlib/shared/System/Double.cs
index ee5ffa0ccf..7ee52027f3 100644
--- a/src/mscorlib/src/System/Double.cs
+++ b/src/mscorlib/shared/System/Double.cs
@@ -12,20 +12,18 @@
**
===========================================================*/
-using System;
+using System.Diagnostics.Contracts;
using System.Globalization;
-using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
-using System.Runtime.ConstrainedExecution;
-using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
namespace System
{
[Serializable]
[StructLayout(LayoutKind.Sequential)]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct Double : IComparable, IFormattable, IConvertible
- , IComparable<Double>, IEquatable<Double>
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public struct Double : IComparable, IConvertible, IFormattable, IComparable<Double>, IEquatable<Double>
{
private double m_value; // Do not rename (binary serialization)
@@ -42,11 +40,12 @@ namespace System
public const double PositiveInfinity = (double)1.0 / (double)(0.0);
public const double NaN = (double)0.0 / (double)0.0;
- internal static double NegativeZero = BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000));
+ // We use this explicit definition to avoid the confusion between 0.0 and -0.0.
+ internal const double NegativeZero = -0.0;
/// <summary>Determines whether the specified value is finite (zero, subnormal, or normal).</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static bool IsFinite(double d)
{
@@ -56,7 +55,7 @@ namespace System
/// <summary>Determines whether the specified value is infinite.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static bool IsInfinity(double d)
{
@@ -66,7 +65,7 @@ namespace System
/// <summary>Determines whether the specified value is NaN.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static bool IsNaN(double d)
{
@@ -76,7 +75,7 @@ namespace System
/// <summary>Determines whether the specified value is negative.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static bool IsNegative(double d)
{
@@ -86,7 +85,7 @@ namespace System
/// <summary>Determines whether the specified value is negative infinity.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNegativeInfinity(double d)
{
@@ -95,7 +94,7 @@ namespace System
/// <summary>Determines whether the specified value is normal.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
// This is probably not worth inlining, it has branches and should be rarely called
public unsafe static bool IsNormal(double d)
{
@@ -106,7 +105,7 @@ namespace System
/// <summary>Determines whether the specified value is positive infinity.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsPositiveInfinity(double d)
{
@@ -115,7 +114,7 @@ namespace System
/// <summary>Determines whether the specified value is subnormal.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
// This is probably not worth inlining, it has branches and should be rarely called
public unsafe static bool IsSubnormal(double d)
{
@@ -183,37 +182,37 @@ namespace System
return IsNaN(temp) && IsNaN(m_value);
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator ==(Double left, Double right)
{
return left == right;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator !=(Double left, Double right)
{
return left != right;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator <(Double left, Double right)
{
return left < right;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator >(Double left, Double right)
{
return left > right;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator <=(Double left, Double right)
{
return left <= right;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator >=(Double left, Double right)
{
return left >= right;
@@ -269,24 +268,28 @@ namespace System
public static double Parse(String s)
{
- return Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseDouble(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo);
}
public static double Parse(String s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
- return Parse(s, style, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseDouble(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo);
}
public static double Parse(String s, IFormatProvider provider)
{
- return Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseDouble(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider));
}
public static double Parse(String s, NumberStyles style, IFormatProvider provider)
{
NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
- return Parse(s, style, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseDouble(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider));
}
// Parses a double from a String in the given style. If
@@ -296,48 +299,67 @@ namespace System
// This method will not throw an OverflowException, but will return
// PositiveInfinity or NegativeInfinity for a number that is too
// large or too small.
- //
- private static double Parse(String s, NumberStyles style, NumberFormatInfo info)
+
+ public static double Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
{
- return Number.ParseDouble(s, style, info);
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return Number.ParseDouble(s, style, NumberFormatInfo.GetInstance(provider));
}
+
+
public static bool TryParse(String s, out double result)
{
- return TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result);
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return TryParse(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result);
}
public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out double result)
{
NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
- return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result);
- }
- private static bool TryParse(String s, NumberStyles style, NumberFormatInfo info, out double result)
- {
if (s == null)
{
result = 0;
return false;
}
+
+ return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ public static bool TryParse(ReadOnlySpan<char> s, out double result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out double result)
+ {
bool success = Number.TryParseDouble(s, style, info, out result);
if (!success)
{
- String sTrim = s.Trim();
- if (sTrim.Equals(info.PositiveInfinitySymbol))
+ ReadOnlySpan<char> sTrim = StringSpanHelpers.Trim(s);
+ if (StringSpanHelpers.Equals(sTrim, info.PositiveInfinitySymbol))
{
result = PositiveInfinity;
}
- else if (sTrim.Equals(info.NegativeInfinitySymbol))
+ else if (StringSpanHelpers.Equals(sTrim, info.NegativeInfinitySymbol))
{
result = NegativeInfinity;
}
- else if (sTrim.Equals(info.NaNSymbol))
+ else if (StringSpanHelpers.Equals(sTrim, info.NaNSymbol))
{
result = NaN;
}
else
+ {
return false; // We really failed
+ }
}
return true;
}
diff --git a/src/mscorlib/shared/System/DuplicateWaitObjectException.cs b/src/mscorlib/shared/System/DuplicateWaitObjectException.cs
index 7eadead8c7..95bdedd526 100644
--- a/src/mscorlib/shared/System/DuplicateWaitObjectException.cs
+++ b/src/mscorlib/shared/System/DuplicateWaitObjectException.cs
@@ -37,25 +37,25 @@ namespace System
public DuplicateWaitObjectException()
: base(DuplicateWaitObjectMessage)
{
- HResult = __HResults.COR_E_DUPLICATEWAITOBJECT;
+ HResult = HResults.COR_E_DUPLICATEWAITOBJECT;
}
public DuplicateWaitObjectException(String parameterName)
: base(DuplicateWaitObjectMessage, parameterName)
{
- HResult = __HResults.COR_E_DUPLICATEWAITOBJECT;
+ HResult = HResults.COR_E_DUPLICATEWAITOBJECT;
}
public DuplicateWaitObjectException(String parameterName, String message)
: base(message, parameterName)
{
- HResult = __HResults.COR_E_DUPLICATEWAITOBJECT;
+ HResult = HResults.COR_E_DUPLICATEWAITOBJECT;
}
public DuplicateWaitObjectException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_DUPLICATEWAITOBJECT;
+ HResult = HResults.COR_E_DUPLICATEWAITOBJECT;
}
protected DuplicateWaitObjectException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/EntryPointNotFoundException.cs b/src/mscorlib/shared/System/EntryPointNotFoundException.cs
index 0b881cec05..e62ca0e11d 100644
--- a/src/mscorlib/shared/System/EntryPointNotFoundException.cs
+++ b/src/mscorlib/shared/System/EntryPointNotFoundException.cs
@@ -21,19 +21,19 @@ namespace System
public EntryPointNotFoundException()
: base(SR.Arg_EntryPointNotFoundException)
{
- HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND;
+ HResult = HResults.COR_E_ENTRYPOINTNOTFOUND;
}
public EntryPointNotFoundException(String message)
: base(message)
{
- HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND;
+ HResult = HResults.COR_E_ENTRYPOINTNOTFOUND;
}
public EntryPointNotFoundException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND;
+ HResult = HResults.COR_E_ENTRYPOINTNOTFOUND;
}
protected EntryPointNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/ExecutionEngineException.cs b/src/mscorlib/shared/System/ExecutionEngineException.cs
index b89f6d9a8d..c33435875e 100644
--- a/src/mscorlib/shared/System/ExecutionEngineException.cs
+++ b/src/mscorlib/shared/System/ExecutionEngineException.cs
@@ -26,19 +26,19 @@ namespace System
public ExecutionEngineException()
: base(SR.Arg_ExecutionEngineException)
{
- HResult = __HResults.COR_E_EXECUTIONENGINE;
+ HResult = HResults.COR_E_EXECUTIONENGINE;
}
public ExecutionEngineException(String message)
: base(message)
{
- HResult = __HResults.COR_E_EXECUTIONENGINE;
+ HResult = HResults.COR_E_EXECUTIONENGINE;
}
public ExecutionEngineException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_EXECUTIONENGINE;
+ HResult = HResults.COR_E_EXECUTIONENGINE;
}
}
}
diff --git a/src/mscorlib/shared/System/FieldAccessException.cs b/src/mscorlib/shared/System/FieldAccessException.cs
index b56d749771..883bbd8bc2 100644
--- a/src/mscorlib/shared/System/FieldAccessException.cs
+++ b/src/mscorlib/shared/System/FieldAccessException.cs
@@ -18,19 +18,19 @@ namespace System
public FieldAccessException()
: base(SR.Arg_FieldAccessException)
{
- HResult = __HResults.COR_E_FIELDACCESS;
+ HResult = HResults.COR_E_FIELDACCESS;
}
public FieldAccessException(String message)
: base(message)
{
- HResult = __HResults.COR_E_FIELDACCESS;
+ HResult = HResults.COR_E_FIELDACCESS;
}
public FieldAccessException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_FIELDACCESS;
+ HResult = HResults.COR_E_FIELDACCESS;
}
protected FieldAccessException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/FormatException.cs b/src/mscorlib/shared/System/FormatException.cs
index 9baaac2353..4af45cdd94 100644
--- a/src/mscorlib/shared/System/FormatException.cs
+++ b/src/mscorlib/shared/System/FormatException.cs
@@ -20,19 +20,19 @@ namespace System
public FormatException()
: base(SR.Arg_FormatException)
{
- HResult = __HResults.COR_E_FORMAT;
+ HResult = HResults.COR_E_FORMAT;
}
public FormatException(String message)
: base(message)
{
- HResult = __HResults.COR_E_FORMAT;
+ HResult = HResults.COR_E_FORMAT;
}
public FormatException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_FORMAT;
+ HResult = HResults.COR_E_FORMAT;
}
protected FormatException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs
index 5d3239ebfc..9f8c072c93 100644
--- a/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs
+++ b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs
@@ -31,7 +31,7 @@ namespace System.Globalization
// This is an internal flag.
//
// This flag is different from MonthNameStyles because this flag
- // can be expanded to accomodate parsing behaviors like CJK month names
+ // can be expanded to accommodate parsing behaviors like CJK month names
// or alternative month names, etc.
[Flags]
@@ -2645,7 +2645,7 @@ namespace System.Globalization
{
String str;
// We have to call public methods here to work with inherited DTFI.
- // Insert the month name first, so that they are at the front of abbrevaited
+ // Insert the month name first, so that they are at the front of abbreviated
// month names.
str = InvariantInfo.GetMonthName(i);
InsertHash(temp, str, TokenType.MonthToken, i);
@@ -2688,7 +2688,7 @@ namespace System.Globalization
String str;
//str = internalGetMonthName(i, MonthNameStyles.Regular, false);
// We have to call public methods here to work with inherited DTFI.
- // Insert the month name first, so that they are at the front of abbrevaited
+ // Insert the month name first, so that they are at the front of abbreviated
// month names.
str = GetMonthName(i);
if (str.Length > 0)
diff --git a/src/mscorlib/shared/System/Globalization/DateTimeParse.cs b/src/mscorlib/shared/System/Globalization/DateTimeParse.cs
index 910fbf2ff0..f821a95412 100644
--- a/src/mscorlib/shared/System/Globalization/DateTimeParse.cs
+++ b/src/mscorlib/shared/System/Globalization/DateTimeParse.cs
@@ -81,23 +81,23 @@ namespace System
{
if (s == null)
{
- result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s));
+ result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s));
return false;
}
if (format == null)
{
- result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(format));
+ result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(format));
return false;
}
if (s.Length == 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
if (format.Length == 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
return false;
}
@@ -177,24 +177,24 @@ namespace System
{
if (s == null)
{
- result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s));
+ result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s));
return false;
}
if (formats == null)
{
- result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(formats));
+ result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(formats));
return false;
}
if (s.Length == 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
if (formats.Length == 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
return false;
}
@@ -208,7 +208,7 @@ namespace System
{
if (formats[i] == null || formats[i].Length == 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
return false;
}
// Create a new result each time to ensure the runs are independent. Carry through
@@ -223,7 +223,7 @@ namespace System
return (true);
}
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
@@ -605,13 +605,13 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.TimeZoneUsed) != 0)
{
// Should not have two timezone offsets.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
result.flags |= ParseFlags.TimeZoneUsed;
if (!ParseTimeZone(ref str, ref result.timeZoneOffset))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
}
@@ -657,7 +657,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
case TokenType.YearNumberToken:
if (raw.numCount == 3 || tokenValue == -1)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0010", dps);
return false;
}
@@ -724,7 +724,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
else
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0030 (TM.AM/TM.PM Happened more than 1x)", dps);
}
break;
@@ -737,7 +737,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
case TokenType.SEP_Time:
if (!raw.hasSameDateAndTimeSeparators)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0040 (Invalid separator after number)", dps);
return false;
}
@@ -775,7 +775,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
break;
default:
// Invalid separator after number number.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0040 (Invalid separator after number)", dps);
return false;
}
@@ -785,7 +785,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
LexTraceExit("0050 (success)", dps);
return true;
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0060", dps);
return false;
}
@@ -818,7 +818,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
else
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
break;
}
if (dps == DS.T_NNt || dps == DS.T_Nt)
@@ -873,7 +873,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
catch (ArgumentOutOfRangeException e)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", e);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), e);
LexTraceExit("0075 (Calendar.ToFourDigitYear failed)", dps);
return false;
}
@@ -897,7 +897,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
break;
default:
// Invalid separator after number number.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0080", dps);
return false;
}
@@ -935,7 +935,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
goto default;
default:
// Invalid separator after number number.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0090", dps);
return false;
}
@@ -943,7 +943,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
else
{
// Invalid separator after number number.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0100", dps);
return false;
}
@@ -984,7 +984,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
break;
default:
// Invalid separator after number number.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0110", dps);
return false;
}
@@ -1001,7 +1001,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
else
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0120 (DayOfWeek seen more than 1x)", dps);
return false;
}
@@ -1026,7 +1026,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
case TokenType.SEP_Time:
if (!raw.hasSameDateAndTimeSeparators)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0130 (Invalid separator after month name)", dps);
return false;
}
@@ -1052,7 +1052,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
break;
default:
//Invalid separator after month name
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0130 (Invalid separator after month name)", dps);
return false;
}
@@ -1060,7 +1060,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
else
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0140 (MonthToken seen more than 1x)", dps);
return false;
}
@@ -1073,7 +1073,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
else
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0150 (EraToken seen when result.era already set)", dps);
return false;
}
@@ -1089,7 +1089,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
else
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0160 (JapaneseEraToken seen when result.era already set)", dps);
return false;
}
@@ -1104,7 +1104,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
else
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0170 (TEraToken seen when result.era already set)", dps);
return false;
}
@@ -1118,7 +1118,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.TimeZoneUsed) != 0)
{
// Should not have two timezone offsets.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0180 (seen GMT or Z more than 1x)", dps);
return false;
}
@@ -1142,7 +1142,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
else
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0190 (AM/PM timeMark already set)", dps);
return false;
}
@@ -1150,7 +1150,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
case TokenType.UnknownToken:
if (Char.IsLetter(str.m_current))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_UnknowDateTimeWord", str.Index);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_UnknowDateTimeWord), str.Index);
LexTraceExit("0200", dps);
return (false);
}
@@ -1180,7 +1180,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
return true;
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
LexTraceExit("0240", dps);
return false;
}
@@ -1573,7 +1573,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveDate) != 0)
{
// Multiple dates in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1585,7 +1585,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int order;
if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out order))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.MonthDayPattern);
return false;
}
@@ -1606,7 +1606,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
return true;
}
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1616,7 +1616,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveDate) != 0)
{
// Multiple dates in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1627,7 +1627,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int order;
if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.ShortDatePattern);
return false;
}
int year;
@@ -1664,7 +1664,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
return true;
}
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1673,7 +1673,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveDate) != 0)
{
// Multiple dates in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1692,7 +1692,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int monthDayOrder;
if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.MonthDayPattern);
return false;
}
if (monthDayOrder == ORDER_DM)
@@ -1700,7 +1700,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int yearMonthOrder;
if (!GetYearMonthOrder(dtfi.YearMonthPattern, dtfi, out yearMonthOrder))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.YearMonthPattern);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.YearMonthPattern);
return false;
}
if (yearMonthOrder == ORDER_MY)
@@ -1708,7 +1708,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int year;
if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
return true;
@@ -1718,7 +1718,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
GetDefaultYear(ref result, ref styles);
if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0)))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
return true;
@@ -1735,7 +1735,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int monthDayOrder;
if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.MonthDayPattern);
return false;
}
result.Month = raw.month;
@@ -1747,7 +1747,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
return true;
}
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1756,7 +1756,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveDate) != 0)
{
// Multiple dates in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1775,7 +1775,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int monthDayOrder;
if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.MonthDayPattern);
return false;
}
if (monthDayOrder == ORDER_MD)
@@ -1783,7 +1783,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int yearMonthOrder;
if (!GetYearMonthOrder(dtfi.YearMonthPattern, dtfi, out yearMonthOrder))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.YearMonthPattern);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.YearMonthPattern);
return false;
}
if (yearMonthOrder == ORDER_YM)
@@ -1791,7 +1791,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int year;
if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
return true;
@@ -1801,7 +1801,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
GetDefaultYear(ref result, ref styles);
if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0)))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
return true;
@@ -1812,7 +1812,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveDate) != 0)
{
// Multiple dates in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1822,7 +1822,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int order;
if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.ShortDatePattern);
return false;
}
int year;
@@ -1873,7 +1873,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1882,7 +1882,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveDate) != 0)
{
// Multiple dates in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1908,7 +1908,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
return true; // Year + MD
}
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1917,7 +1917,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveDate) != 0)
{
// Multiple dates in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1927,7 +1927,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int order;
if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.ShortDatePattern);
return false;
}
@@ -1947,7 +1947,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
return true; // DM + Year
}
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1957,7 +1957,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveDate) != 0)
{
// Multiple dates in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1966,7 +1966,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
result.flags |= ParseFlags.HaveDate;
return true;
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1975,7 +1975,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveDate) != 0)
{
// Multiple dates in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1984,7 +1984,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
result.flags |= ParseFlags.HaveDate;
return true;
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -1993,7 +1993,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveDate) != 0)
{
// Multiple dates in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -2002,7 +2002,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
result.flags |= ParseFlags.HaveDate;
return true;
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -2065,7 +2065,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveTime) != 0)
{
// Multiple times in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
//
@@ -2073,7 +2073,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
//
if (raw.timeMark == TM.NotSet)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
result.Hour = raw.GetNumber(0);
@@ -2087,7 +2087,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveTime) != 0)
{
// Multiple times in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -2102,7 +2102,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((result.flags & ParseFlags.HaveTime) != 0)
{
// Multiple times in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
Debug.Assert(raw.numCount >= 3, "raw.numCount >= 3");
@@ -2120,7 +2120,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (raw.numCount != 1 || result.Day != -1)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
result.Day = raw.GetNumber(0);
@@ -2132,19 +2132,19 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (result.Month == -1)
{
//Should have a month suffix
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
if (result.Year != -1)
{
// Aleady has a year suffix
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
if (!TryAdjustYear(ref result, raw.GetNumber(0), out result.Year))
{
// the year value is out of range
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
result.Day = 1;
@@ -2174,7 +2174,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
int order;
if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.ShortDatePattern);
return false;
}
int year;
@@ -2194,7 +2194,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
}
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -2275,7 +2275,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
raw.year = raw.GetNumber(1);
if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true))
{
- result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
return false;
}
if (!GetDayOfMNN(ref result, ref raw, dtfi))
@@ -2287,7 +2287,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// Deal with the default long/short date format when the year number is NOT ambigous (i.e. year >= 100).
if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true))
{
- result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
return false;
}
if (!GetDayOfYMN(ref result, ref raw, dtfi))
@@ -2295,13 +2295,30 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
return false;
}
break;
+ case DS.DX_NNY:
+ // When formatting, we only format up to the hundred digit of the Hebrew year, although Hebrew year is now over 5000.
+ // E.g. if the year is 5763, we only format as 763. so we do the reverse when parsing.
+ if (raw.year < 1000)
+ {
+ raw.year += 5000;
+ }
+ if (!GetDayOfNNY(ref result, ref raw, dtfi))
+ {
+ return false;
+ }
+ if (!dtfi.YearMonthAdjustment(ref result.Year, ref raw.month, true))
+ {
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
+ return false;
+ }
+ break;
case DS.DX_NM:
case DS.DX_MN:
// Deal with Month/Day pattern.
GetDefaultYear(ref result, ref styles);
if (!dtfi.YearMonthAdjustment(ref result.Year, ref raw.month, true))
{
- result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
return false;
}
if (!GetHebrewDayOfNM(ref result, ref raw, dtfi))
@@ -2313,7 +2330,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// Deal with Year/Month pattern.
if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true))
{
- result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
return false;
}
if (!GetDayOfYM(ref result, ref raw, dtfi))
@@ -2341,7 +2358,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
break;
default:
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
if (dps > DS.ERROR)
@@ -2505,12 +2522,12 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (s == null)
{
- result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s));
+ result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s));
return false;
}
if (s.Length == 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -2579,7 +2596,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (!ProcessDateTimeSuffix(ref result, ref raw, ref dtok))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
TPTraceExit("0010", dps);
return false;
}
@@ -2598,7 +2615,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
else
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
TPTraceExit("0030", dps);
return false;
}
@@ -2643,7 +2660,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (dps == DS.ERROR)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
TPTraceExit("0040 (invalid state transition)", dps);
return false;
}
@@ -2679,7 +2696,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (!reachTerminalState)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
TPTraceExit("0070 (did not reach terminal state)", dps);
return false;
}
@@ -2687,7 +2704,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
AdjustTimeMark(dtfi, ref raw);
if (!AdjustHour(ref result.Hour, raw.timeMark))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
TPTraceExit("0080 (AdjustHour)", dps);
return false;
}
@@ -2708,7 +2725,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (!result.calendar.TryToDateTime(result.Year, result.Month, result.Day,
result.Hour, result.Minute, result.Second, 0, result.era, out time))
{
- result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
TPTraceExit("0100 (result.calendar.TryToDateTime)", dps);
return false;
}
@@ -2728,7 +2745,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
//
if (raw.dayOfWeek != (int)result.calendar.GetDayOfWeek(time))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDayOfWeek", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDayOfWeek), null);
TPTraceExit("0110 (dayOfWeek check)", dps);
return false;
}
@@ -2762,7 +2779,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// the DateTime offset must be within +- 14:00 hours.
if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset)
{
- result.SetFailure(ParseFailureKind.Format, "Format_OffsetOutOfRange", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_OffsetOutOfRange), null);
return false;
}
}
@@ -2853,14 +2870,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// of a DateTime instance.
if (utcTicks < DateTime.MinTicks || utcTicks > DateTime.MaxTicks)
{
- result.SetFailure(ParseFailureKind.Format, "Format_UTCOutOfRange", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_UTCOutOfRange), null);
return false;
}
// the offset must be within +- 14:00 hours.
if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset)
{
- result.SetFailure(ParseFailureKind.Format, "Format_OffsetOutOfRange", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_OffsetOutOfRange), null);
return false;
}
@@ -2903,7 +2920,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks)
{
- result.SetFailure(ParseFailureKind.Format, "Format_DateOutOfRange", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_DateOutOfRange), null);
return false;
}
result.parsedDate = new DateTime(resultTicks, DateTimeKind.Utc);
@@ -2960,7 +2977,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks)
{
result.parsedDate = DateTime.MinValue;
- result.SetFailure(ParseFailureKind.Format, "Format_DateOutOfRange", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_DateOutOfRange), null);
return false;
}
result.parsedDate = new DateTime(resultTicks, DateTimeKind.Local, isAmbiguousLocalDst);
@@ -2984,19 +3001,19 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
str.SkipWhiteSpaces();
if (!ParseDigits(ref str, 2, out hour))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
str.SkipWhiteSpaces();
if (!str.Match(':'))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
str.SkipWhiteSpaces();
if (!ParseDigits(ref str, 2, out minute))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
str.SkipWhiteSpaces();
@@ -3005,14 +3022,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
str.SkipWhiteSpaces();
if (!ParseDigits(ref str, 2, out second))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
if (str.Match('.'))
{
if (!ParseFraction(ref str, out partSecond))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
str.Index--;
@@ -3027,7 +3044,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
result.flags |= ParseFlags.TimeZoneUsed;
if (!ParseTimeZone(ref str, ref result.timeZoneOffset))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
}
@@ -3046,7 +3063,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (!VerifyValidPunctuation(ref str))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
str.SkipWhiteSpaces();
@@ -3055,14 +3072,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (!VerifyValidPunctuation(ref str))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
}
if (str.GetNext())
{
// If this is true, there were non-white space characters remaining in the DateTime
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
}
@@ -3072,7 +3089,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (!calendar.TryToDateTime(raw.year, raw.GetNumber(0), raw.GetNumber(1),
hour, minute, second, 0, result.era, out time))
{
- result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
return false;
}
@@ -3658,7 +3675,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (newValue != currentValue)
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", patternChar);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), patternChar);
return (false);
}
}
@@ -3702,7 +3719,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (((result.Month != -1) || (result.Day != -1))
&& ((result.Year == -1 || ((result.flags & ParseFlags.YearDefault) != 0)) && (result.flags & ParseFlags.TimeZoneUsed) != 0))
{
- result.SetFailure(ParseFailureKind.Format, "Format_MissingIncompleteDate", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_MissingIncompleteDate), null);
return false;
}
}
@@ -3885,7 +3902,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
if (!parseResult)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
if (!CheckNewValue(ref result.Year, tempYear, ch, ref result))
@@ -3902,7 +3919,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (!parseInfo.fCustomNumberParser ||
!parseInfo.parseNumberDelegate(ref str, tokenLen, out tempMonth))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
}
@@ -3913,7 +3930,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (!MatchAbbreviatedMonthName(ref str, dtfi, ref tempMonth))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
}
@@ -3921,7 +3938,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (!MatchMonthName(ref str, dtfi, ref tempMonth))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
}
@@ -3944,7 +3961,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (!parseInfo.fCustomNumberParser ||
!parseInfo.parseNumberDelegate(ref str, tokenLen, out tempDay))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
}
@@ -3960,7 +3977,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// "ddd"
if (!MatchAbbreviatedDayName(ref str, dtfi, ref tempDayOfWeek))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
}
@@ -3969,7 +3986,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// "dddd*"
if (!MatchDayName(ref str, dtfi, ref tempDayOfWeek))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
}
@@ -3984,7 +4001,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// Put the era value in result.era.
if (!MatchEraName(ref str, dtfi, ref result.era))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
break;
@@ -3993,7 +4010,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
tokenLen = format.GetRepeatCount();
if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempHour))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result))
@@ -4005,7 +4022,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
tokenLen = format.GetRepeatCount();
if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempHour))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result))
@@ -4017,7 +4034,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
tokenLen = format.GetRepeatCount();
if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempMinute))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
if (!CheckNewValue(ref result.Minute, tempMinute, ch, ref result))
@@ -4029,7 +4046,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
tokenLen = format.GetRepeatCount();
if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempSecond))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
if (!CheckNewValue(ref result.Second, tempSecond, ch, ref result))
@@ -4046,7 +4063,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (ch == 'f')
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
}
@@ -4058,14 +4075,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (tempFraction != result.fraction)
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", ch);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), ch);
return (false);
}
}
}
else
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
break;
@@ -4076,7 +4093,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (!MatchAbbreviatedTimeMark(ref str, dtfi, ref tempTimeMark))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
}
@@ -4084,7 +4101,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (!MatchTimeMark(ref str, dtfi, ref tempTimeMark))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
}
@@ -4097,7 +4114,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (parseInfo.timeMark != tempTimeMark)
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", ch);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), ch);
return (false);
}
}
@@ -4109,12 +4126,12 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
TimeSpan tempTimeZoneOffset = new TimeSpan(0);
if (!ParseTimeZoneOffset(ref str, tokenLen, ref tempTimeZoneOffset))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset)
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'z');
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), 'z');
return (false);
}
result.timeZoneOffset = tempTimeZoneOffset;
@@ -4124,7 +4141,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
case 'Z':
if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && result.timeZoneOffset != TimeSpan.Zero)
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'Z');
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), 'Z');
return (false);
}
@@ -4139,7 +4156,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
str.Index++;
if (!GetTimeZoneName(ref str))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
str.Index--;
@@ -4150,7 +4167,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && result.timeZoneOffset != TimeSpan.Zero)
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'K');
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), 'K');
return (false);
}
@@ -4164,12 +4181,12 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
TimeSpan tempTimeZoneOffset = new TimeSpan(0);
if (!ParseTimeZoneOffset(ref str, 3, ref tempTimeZoneOffset))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return (false);
}
if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset)
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'K');
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), 'K');
return (false);
}
result.timeZoneOffset = tempTimeZoneOffset;
@@ -4184,7 +4201,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
!str.Match(dtfi.TimeSeparator))
{
// A time separator is expected.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
break;
@@ -4195,7 +4212,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
!str.Match(dtfi.DateSeparator))
{
// A date separator is expected.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
break;
@@ -4205,7 +4222,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// Use ParseQuoteString so that we can handle escape characters within the quoted string.
if (!TryParseQuoteString(format.Value, format.Index, enquotedString, out tokenLen))
{
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadQuote", ch);
+ result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadQuote), ch);
return (false);
}
format.Index += tokenLen - 1;
@@ -4224,7 +4241,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
else if (!str.Match(quotedStr[i]))
{
// Can not find the matching quoted string.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
}
@@ -4255,7 +4272,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// Make sure the next character is not a '%' again.
if (format.Index >= format.Value.Length - 1 || format.Value[format.Index + 1] == '%')
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
return false;
}
break;
@@ -4268,13 +4285,13 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (!str.Match(format.GetChar()))
{
// Can not find a match for the escaped character.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
}
else
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
return false;
}
break;
@@ -4291,7 +4308,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
break;
}
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
break;
@@ -4326,7 +4343,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
}
}
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
// Found a macth.
@@ -4343,14 +4360,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
result.timeZoneOffset = TimeSpan.Zero;
if (!str.Match(GMTName))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
}
else if (!str.Match(ch))
{
// ch is expected.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
}
@@ -4461,7 +4478,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (((result.flags & ParseFlags.CaptureOffset) != 0) && formatParam[0] == 'U')
{
// The 'U' format is not allowed for DateTimeOffset
- result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null);
return false;
}
formatParam = ExpandPredefinedFormat(formatParam, ref dtfi, ref parseInfo, ref result);
@@ -4518,7 +4535,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (str.Index < str.Value.Length - 1)
{
// There are still remaining character in str.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
@@ -4527,7 +4544,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// A two digit year value is expected. Check if the parsed year value is valid.
if (result.Year >= 100)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
try
@@ -4536,7 +4553,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
}
catch (ArgumentOutOfRangeException e)
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", e);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), e);
return false;
}
}
@@ -4555,7 +4572,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if (result.Hour > 12)
{
// AM/PM is used, but the value for HH is too big.
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
if (parseInfo.timeMark == TM.AM)
@@ -4579,7 +4596,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
if ((parseInfo.timeMark == TM.AM && result.Hour >= 12)
|| (parseInfo.timeMark == TM.PM && result.Hour < 12))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null);
return false;
}
}
@@ -4596,14 +4613,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
{
if (!dtfi.YearMonthAdjustment(ref result.Year, ref result.Month, ((result.flags & ParseFlags.ParsedMonthName) != 0)))
{
- result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
return false;
}
}
if (!parseInfo.calendar.TryToDateTime(result.Year, result.Month, result.Day,
result.Hour, result.Minute, result.Second, 0, result.era, out result.parsedDate))
{
- result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null);
return false;
}
if (result.fraction > 0)
@@ -4623,7 +4640,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
//
if (parseInfo.dayOfWeek != (int)parseInfo.calendar.GetDayOfWeek(result.parsedDate))
{
- result.SetFailure(ParseFailureKind.Format, "Format_BadDayOfWeek", null);
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDayOfWeek), null);
return false;
}
}
diff --git a/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs
index 0697b602db..383e0cd17c 100644
--- a/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs
+++ b/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs
@@ -67,7 +67,7 @@ namespace System.Globalization
return ((sexagenaryYear - 1) % 10) + 1;
}
- // Return the Terrestial Branch from the the 60-year cycle.
+ // Return the Terrestial Branch from the 60-year cycle.
// The returned value is from 1 ~ 12.
//
diff --git a/src/mscorlib/shared/System/Globalization/HijriCalendar.Win32.cs b/src/mscorlib/shared/System/Globalization/HijriCalendar.Win32.cs
index 869b809bff..09b1f20c48 100644
--- a/src/mscorlib/shared/System/Globalization/HijriCalendar.Win32.cs
+++ b/src/mscorlib/shared/System/Globalization/HijriCalendar.Win32.cs
@@ -45,7 +45,6 @@ namespace System.Globalization
try
{
// Open in read-only mode.
- // Use InternalOpenSubKey so that we avoid the security check.
key = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_USER).OpenSubKey(InternationalRegKey, false);
}
//If this fails for any reason, we'll just return 0.
@@ -68,10 +67,9 @@ namespace System.Globalization
hijriAdvance = -1;
else
{
- str = str.Substring(HijriAdvanceRegKeyEntry.Length);
try
{
- int advance = Int32.Parse(str.ToString(), CultureInfo.InvariantCulture);
+ int advance = Int32.Parse(str.AsReadOnlySpan().Slice(HijriAdvanceRegKeyEntry.Length), provider:CultureInfo.InvariantCulture);
if ((advance >= MinAdvancedHijri) && (advance <= MaxAdvancedHijri))
{
hijriAdvance = advance;
diff --git a/src/mscorlib/shared/System/Globalization/HijriCalendar.cs b/src/mscorlib/shared/System/Globalization/HijriCalendar.cs
index 125248a685..59b354f534 100644
--- a/src/mscorlib/shared/System/Globalization/HijriCalendar.cs
+++ b/src/mscorlib/shared/System/Globalization/HijriCalendar.cs
@@ -187,7 +187,7 @@ namespace System.Globalization
set
{
- // NOTE: Check the value of Min/MaxAdavncedHijri with Arabic speakers to see if the assumption is good.
+ // NOTE: Check the value of Min/MaxAdvancedHijri with Arabic speakers to see if the assumption is good.
if (value < MinAdvancedHijri || value > MaxAdvancedHijri)
{
throw new ArgumentOutOfRangeException(
@@ -303,7 +303,7 @@ namespace System.Globalization
//
HijriYear = (int)(((NumDays - 227013) * 30) / 10631) + 1;
- long daysToHijriYear = DaysUpToHijriYear(HijriYear); // The absoulte date for HijriYear
+ long daysToHijriYear = DaysUpToHijriYear(HijriYear); // The absolute date for HijriYear
long daysOfHijriYear = GetDaysInYear(HijriYear, CurrentEra); // The number of days for (HijriYear+1) year.
if (NumDays < daysToHijriYear)
diff --git a/src/mscorlib/shared/System/Globalization/JapaneseCalendar.Win32.cs b/src/mscorlib/shared/System/Globalization/JapaneseCalendar.Win32.cs
index a83c4fad9e..94e0668de1 100644
--- a/src/mscorlib/shared/System/Globalization/JapaneseCalendar.Win32.cs
+++ b/src/mscorlib/shared/System/Globalization/JapaneseCalendar.Win32.cs
@@ -159,9 +159,10 @@ namespace System.Globalization
int month;
int day;
- if (!Int32.TryParse(value.Substring(0, 4), NumberStyles.None, NumberFormatInfo.InvariantInfo, out year) ||
- !Int32.TryParse(value.Substring(5, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out month) ||
- !Int32.TryParse(value.Substring(8, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out day))
+ ReadOnlySpan<char> valueSpan = value.AsReadOnlySpan();
+ if (!Int32.TryParse(valueSpan.Slice(0, 4), out year, style:NumberStyles.None, provider: NumberFormatInfo.InvariantInfo) ||
+ !Int32.TryParse(valueSpan.Slice(5, 2), out month, style:NumberStyles.None, provider: NumberFormatInfo.InvariantInfo) ||
+ !Int32.TryParse(valueSpan.Slice(8, 2), out day, style:NumberStyles.None, provider: NumberFormatInfo.InvariantInfo))
{
// Couldn't convert integer, fail
return null;
diff --git a/src/mscorlib/shared/System/Globalization/NumberFormatInfo.cs b/src/mscorlib/shared/System/Globalization/NumberFormatInfo.cs
index 9fea694cca..d7412bfcf3 100644
--- a/src/mscorlib/shared/System/Globalization/NumberFormatInfo.cs
+++ b/src/mscorlib/shared/System/Globalization/NumberFormatInfo.cs
@@ -238,35 +238,30 @@ namespace System.Globalization
public static NumberFormatInfo GetInstance(IFormatProvider formatProvider)
{
- // Fast case for a regular CultureInfo
- NumberFormatInfo info;
- CultureInfo cultureProvider = formatProvider as CultureInfo;
- if (cultureProvider != null && !cultureProvider._isInherited)
+ if (formatProvider != null)
{
- info = cultureProvider.numInfo;
- if (info != null)
+ // Fast case for a regular CultureInfo
+ NumberFormatInfo info;
+ CultureInfo cultureProvider = formatProvider as CultureInfo;
+ if (cultureProvider != null && !cultureProvider._isInherited)
{
- return info;
+ return cultureProvider.numInfo ?? cultureProvider.NumberFormat;
}
- else
+
+ // Fast case for an NFI;
+ info = formatProvider as NumberFormatInfo;
+ if (info != null)
{
- return cultureProvider.NumberFormat;
+ return info;
}
- }
- // Fast case for an NFI;
- info = formatProvider as NumberFormatInfo;
- if (info != null)
- {
- return info;
- }
- if (formatProvider != null)
- {
+
info = formatProvider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo;
if (info != null)
{
return info;
}
}
+
return CurrentInfo;
}
diff --git a/src/mscorlib/src/System/Globalization/TimeSpanFormat.cs b/src/mscorlib/shared/System/Globalization/TimeSpanFormat.cs
index ca053eded3..6801ea8e1b 100644
--- a/src/mscorlib/src/System/Globalization/TimeSpanFormat.cs
+++ b/src/mscorlib/shared/System/Globalization/TimeSpanFormat.cs
@@ -2,25 +2,37 @@
// 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.Text;
-using System;
using System.Diagnostics;
-using System.Diagnostics.Contracts;
-using System.Globalization;
namespace System.Globalization
{
internal static class TimeSpanFormat
{
- private static String IntToString(int n, int digits)
+ private static unsafe void AppendNonNegativeInt32(StringBuilder sb, int n, int digits)
{
- return ParseNumbers.IntToString(n, 10, digits, '0', 0);
+ Debug.Assert(n >= 0);
+ uint value = (uint)n;
+
+ const int MaxUInt32Digits = 10;
+ char* buffer = stackalloc char[MaxUInt32Digits];
+
+ int index = 0;
+ do
+ {
+ uint div = value / 10;
+ buffer[index++] = (char)(value - (div * 10) + '0');
+ value = div;
+ }
+ while (value != 0);
+ Debug.Assert(index <= MaxUInt32Digits);
+
+ for (int i = digits - index; i > 0; --i) sb.Append('0');
+ for (int i = index - 1; i >= 0; --i) sb.Append(buffer[i]);
}
- internal static readonly FormatLiterals PositiveInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(false /*isNegative*/);
- internal static readonly FormatLiterals NegativeInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(true /*isNegative*/);
+ internal static readonly FormatLiterals PositiveInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(isNegative: false);
+ internal static readonly FormatLiterals NegativeInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(isNegative: true);
internal enum Pattern
{
@@ -29,57 +41,77 @@ namespace System.Globalization
Full = 2,
}
- //
- // Format
- //
- // Actions: Main method called from TimeSpan.ToString
- //
- internal static String Format(TimeSpan value, String format, IFormatProvider formatProvider)
+ /// <summary>Main method called from TimeSpan.ToString.</summary>
+ internal static string Format(TimeSpan value, string format, IFormatProvider formatProvider) =>
+ StringBuilderCache.GetStringAndRelease(FormatToBuilder(value, format, formatProvider));
+
+ /// <summary>Main method called from TimeSpan.TryFormat.</summary>
+ internal static bool TryFormat(TimeSpan value, Span<char> destination, out int charsWritten, string format, IFormatProvider formatProvider)
+ {
+ StringBuilder sb = FormatToBuilder(value, format, formatProvider);
+ if (sb.Length <= destination.Length)
+ {
+ charsWritten = sb.Length;
+ sb.CopyTo(0, destination, sb.Length);
+ StringBuilderCache.Release(sb);
+ return true;
+ }
+ else
+ {
+ StringBuilderCache.Release(sb);
+ charsWritten = 0;
+ return false;
+ }
+ }
+
+ private static StringBuilder FormatToBuilder(TimeSpan value, string format, IFormatProvider formatProvider)
{
if (format == null || format.Length == 0)
+ {
format = "c";
+ }
- // standard formats
+ // Standard formats
if (format.Length == 1)
{
char f = format[0];
-
- if (f == 'c' || f == 't' || f == 'T')
- return FormatStandard(value, true, format, Pattern.Minimum);
- if (f == 'g' || f == 'G')
+ switch (f)
{
- Pattern pattern;
- DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(formatProvider);
+ case 'c':
+ case 't':
+ case 'T':
+ return FormatStandard(
+ value,
+ isInvariant: true,
+ format: format,
+ pattern: Pattern.Minimum);
+
+ case 'g':
+ case 'G':
+ DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(formatProvider);
+ return FormatStandard(
+ value,
+ isInvariant: false,
+ format: value.Ticks < 0 ? dtfi.FullTimeSpanNegativePattern : dtfi.FullTimeSpanPositivePattern,
+ pattern: f == 'g' ? Pattern.Minimum : Pattern.Full);
- if (value._ticks < 0)
- format = dtfi.FullTimeSpanNegativePattern;
- else
- format = dtfi.FullTimeSpanPositivePattern;
- if (f == 'g')
- pattern = Pattern.Minimum;
- else
- pattern = Pattern.Full;
-
- return FormatStandard(value, false, format, pattern);
+ default:
+ throw new FormatException(SR.Format_InvalidString);
}
- throw new FormatException(SR.Format_InvalidString);
}
+ // Custom formats
return FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider));
}
- //
- // FormatStandard
- //
- // Actions: Format the TimeSpan instance using the specified format.
- //
- private static String FormatStandard(TimeSpan value, bool isInvariant, String format, Pattern pattern)
+ /// <summary>Format the TimeSpan instance using the specified format.</summary>
+ private static StringBuilder FormatStandard(TimeSpan value, bool isInvariant, string format, Pattern pattern)
{
- StringBuilder sb = StringBuilderCache.Acquire();
- int day = (int)(value._ticks / TimeSpan.TicksPerDay);
- long time = value._ticks % TimeSpan.TicksPerDay;
+ StringBuilder sb = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity);
+ int day = (int)(value.Ticks / TimeSpan.TicksPerDay);
+ long time = value.Ticks % TimeSpan.TicksPerDay;
- if (value._ticks < 0)
+ if (value.Ticks < 0)
{
day = -day;
time = -time;
@@ -92,19 +124,20 @@ namespace System.Globalization
FormatLiterals literal;
if (isInvariant)
{
- if (value._ticks < 0)
- literal = NegativeInvariantFormatLiterals;
- else
- literal = PositiveInvariantFormatLiterals;
+ literal = value.Ticks < 0 ?
+ NegativeInvariantFormatLiterals :
+ PositiveInvariantFormatLiterals;
}
else
{
literal = new FormatLiterals();
literal.Init(format, pattern == Pattern.Full);
}
+
if (fraction != 0)
- { // truncate the partial second to the specified length
- fraction = (int)((long)fraction / (long)Math.Pow(10, DateTimeFormat.MaxSecondsFractionDigits - literal.ff));
+ {
+ // truncate the partial second to the specified length
+ fraction = (int)(fraction / TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - literal.ff));
}
// Pattern.Full: [-]dd.hh:mm:ss.fffffff
@@ -112,15 +145,15 @@ namespace System.Globalization
sb.Append(literal.Start); // [-]
if (pattern == Pattern.Full || day != 0)
- { //
+ {
sb.Append(day); // [dd]
sb.Append(literal.DayHourSep); // [.]
} //
- sb.Append(IntToString(hours, literal.hh)); // hh
+ AppendNonNegativeInt32(sb, hours, literal.hh); // hh
sb.Append(literal.HourMinuteSep); // :
- sb.Append(IntToString(minutes, literal.mm)); // mm
+ AppendNonNegativeInt32(sb, minutes, literal.mm); // mm
sb.Append(literal.MinuteSecondSep); // :
- sb.Append(IntToString(seconds, literal.ss)); // ss
+ AppendNonNegativeInt32(sb, seconds, literal.ss); // ss
if (!isInvariant && pattern == Pattern.Minimum)
{
int effectiveDigits = literal.ff;
@@ -144,30 +177,23 @@ namespace System.Globalization
}
else if (pattern == Pattern.Full || fraction != 0)
{
- sb.Append(literal.SecondFractionSep); // [.]
- sb.Append(IntToString(fraction, literal.ff)); // [fffffff]
- } //
- sb.Append(literal.End); //
+ sb.Append(literal.SecondFractionSep); // [.]
+ AppendNonNegativeInt32(sb, fraction, literal.ff); // [fffffff]
+ }
+ sb.Append(literal.End);
- return StringBuilderCache.GetStringAndRelease(sb);
+ return sb;
}
-
-
-
- //
- // FormatCustomized
- //
- // Actions: Format the TimeSpan instance using the specified format.
- //
- internal static String FormatCustomized(TimeSpan value, String format, DateTimeFormatInfo dtfi)
+ /// <summary>Format the TimeSpan instance using the specified format.</summary>
+ private static StringBuilder FormatCustomized(TimeSpan value, string format, DateTimeFormatInfo dtfi)
{
- Debug.Assert(dtfi != null, "dtfi == null");
+ Debug.Assert(dtfi != null);
- int day = (int)(value._ticks / TimeSpan.TicksPerDay);
- long time = value._ticks % TimeSpan.TicksPerDay;
+ int day = (int)(value.Ticks / TimeSpan.TicksPerDay);
+ long time = value.Ticks % TimeSpan.TicksPerDay;
- if (value._ticks < 0)
+ if (value.Ticks < 0)
{
day = -day;
time = -time;
@@ -180,7 +206,7 @@ namespace System.Globalization
long tmp = 0;
int i = 0;
int tokenLen;
- StringBuilder result = StringBuilderCache.Acquire();
+ StringBuilder result = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity);
while (i < format.Length)
{
@@ -191,19 +217,25 @@ namespace System.Globalization
case 'h':
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
if (tokenLen > 2)
+ {
throw new FormatException(SR.Format_InvalidString);
+ }
DateTimeFormat.FormatDigits(result, hours, tokenLen);
break;
case 'm':
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
if (tokenLen > 2)
+ {
throw new FormatException(SR.Format_InvalidString);
+ }
DateTimeFormat.FormatDigits(result, minutes, tokenLen);
break;
case 's':
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
if (tokenLen > 2)
+ {
throw new FormatException(SR.Format_InvalidString);
+ }
DateTimeFormat.FormatDigits(result, seconds, tokenLen);
break;
case 'f':
@@ -212,10 +244,12 @@ namespace System.Globalization
//
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits)
+ {
throw new FormatException(SR.Format_InvalidString);
+ }
- tmp = (long)fraction;
- tmp /= (long)Math.Pow(10, DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
+ tmp = fraction;
+ tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
result.Append((tmp).ToString(DateTimeFormat.fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture));
break;
case 'F':
@@ -224,10 +258,12 @@ namespace System.Globalization
//
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits)
+ {
throw new FormatException(SR.Format_InvalidString);
+ }
- tmp = (long)fraction;
- tmp /= (long)Math.Pow(10, DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
+ tmp = fraction;
+ tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
int effectiveDigits = tokenLen;
while (effectiveDigits > 0)
{
@@ -253,7 +289,10 @@ namespace System.Globalization
//
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
if (tokenLen > 8)
+ {
throw new FormatException(SR.Format_InvalidString);
+ }
+
DateTimeFormat.FormatDigits(result, day, tokenLen, true);
break;
case '\'':
@@ -304,77 +343,38 @@ namespace System.Globalization
}
i += tokenLen;
}
- return StringBuilderCache.GetStringAndRelease(result);
+ return result;
}
-
-
-
internal struct FormatLiterals
{
- internal String Start
- {
- get
- {
- return literals[0];
- }
- }
- internal String DayHourSep
- {
- get
- {
- return literals[1];
- }
- }
- internal String HourMinuteSep
- {
- get
- {
- return literals[2];
- }
- }
- internal String MinuteSecondSep
- {
- get
- {
- return literals[3];
- }
- }
- internal String SecondFractionSep
- {
- get
- {
- return literals[4];
- }
- }
- internal String End
- {
- get
- {
- return literals[5];
- }
- }
- internal String AppCompatLiteral;
+ internal string AppCompatLiteral;
internal int dd;
internal int hh;
internal int mm;
internal int ss;
internal int ff;
- private String[] literals;
+ private string[] _literals;
+ internal string Start => _literals[0];
+ internal string DayHourSep => _literals[1];
+ internal string HourMinuteSep => _literals[2];
+ internal string MinuteSecondSep => _literals[3];
+ internal string SecondFractionSep => _literals[4];
+ internal string End => _literals[5];
/* factory method for static invariant FormatLiterals */
internal static FormatLiterals InitInvariant(bool isNegative)
{
FormatLiterals x = new FormatLiterals();
- x.literals = new String[6];
- x.literals[0] = isNegative ? "-" : String.Empty;
- x.literals[1] = ".";
- x.literals[2] = ":";
- x.literals[3] = ":";
- x.literals[4] = ".";
- x.literals[5] = String.Empty;
+ x._literals = new string[6];
+ x._literals[0] = isNegative ? "-" : string.Empty;
+ x._literals[1] = ".";
+ x._literals[2] = ":";
+ x._literals[3] = ":";
+ x._literals[4] = ".";
+ x._literals[5] = string.Empty;
x.AppCompatLiteral = ":."; // MinuteSecondSep+SecondFractionSep;
x.dd = 2;
x.hh = 2;
@@ -388,18 +388,16 @@ namespace System.Globalization
// the constants guaranteed to include DHMSF ordered greatest to least significant.
// Once the data becomes more complex than this we will need to write a proper tokenizer for
// parsing and formatting
- internal void Init(String format, bool useInvariantFieldLengths)
+ internal void Init(string format, bool useInvariantFieldLengths)
{
- literals = new String[6];
- for (int i = 0; i < literals.Length; i++)
- literals[i] = String.Empty;
- dd = 0;
- hh = 0;
- mm = 0;
- ss = 0;
- ff = 0;
-
- StringBuilder sb = StringBuilderCache.Acquire();
+ dd = hh = mm = ss = ff = 0;
+ _literals = new string[6];
+ for (int i = 0; i < _literals.Length; i++)
+ {
+ _literals[i] = string.Empty;
+ }
+
+ StringBuilder sb = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity);
bool inQuote = false;
char quote = '\'';
int field = 0;
@@ -413,15 +411,15 @@ namespace System.Globalization
if (inQuote && (quote == format[i]))
{
/* we were in a quote and found a matching exit quote, so we are outside a quote now */
- Debug.Assert(field >= 0 && field <= 5, "field >= 0 && field <= 5");
if (field >= 0 && field <= 5)
{
- literals[field] = sb.ToString();
+ _literals[field] = sb.ToString();
sb.Length = 0;
inQuote = false;
}
else
{
+ Debug.Fail($"Unexpected field value: {field}");
return; // how did we get here?
}
}
@@ -437,7 +435,7 @@ namespace System.Globalization
}
break;
case '%':
- Debug.Assert(false, "Unexpected special token '%', Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
+ Debug.Fail("Unexpected special token '%', Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
goto default;
case '\\':
if (!inQuote)
@@ -449,8 +447,7 @@ namespace System.Globalization
case 'd':
if (!inQuote)
{
- Debug.Assert((field == 0 && sb.Length == 0) || field == 1,
- "field == 0 || field == 1, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
+ Debug.Assert((field == 0 && sb.Length == 0) || field == 1, "field == 0 || field == 1, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
field = 1; // DayHourSep
dd++;
}
@@ -458,8 +455,7 @@ namespace System.Globalization
case 'h':
if (!inQuote)
{
- Debug.Assert((field == 1 && sb.Length == 0) || field == 2,
- "field == 1 || field == 2, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
+ Debug.Assert((field == 1 && sb.Length == 0) || field == 2, "field == 1 || field == 2, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
field = 2; // HourMinuteSep
hh++;
}
@@ -467,8 +463,7 @@ namespace System.Globalization
case 'm':
if (!inQuote)
{
- Debug.Assert((field == 2 && sb.Length == 0) || field == 3,
- "field == 2 || field == 3, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
+ Debug.Assert((field == 2 && sb.Length == 0) || field == 3, "field == 2 || field == 3, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
field = 3; // MinuteSecondSep
mm++;
}
@@ -476,8 +471,7 @@ namespace System.Globalization
case 's':
if (!inQuote)
{
- Debug.Assert((field == 3 && sb.Length == 0) || field == 4,
- "field == 3 || field == 4, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
+ Debug.Assert((field == 3 && sb.Length == 0) || field == 4, "field == 3 || field == 4, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
field = 4; // SecondFractionSep
ss++;
}
@@ -486,8 +480,7 @@ namespace System.Globalization
case 'F':
if (!inQuote)
{
- Debug.Assert((field == 4 && sb.Length == 0) || field == 5,
- "field == 4 || field == 5, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
+ Debug.Assert((field == 4 && sb.Length == 0) || field == 5, "field == 4 || field == 5, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
field = 5; // End
ff++;
}
@@ -525,6 +518,6 @@ namespace System.Globalization
}
StringBuilderCache.Release(sb);
}
- } //end of struct FormatLiterals
+ }
}
}
diff --git a/src/mscorlib/shared/System/Globalization/TimeSpanParse.cs b/src/mscorlib/shared/System/Globalization/TimeSpanParse.cs
new file mode 100644
index 0000000000..f8443a1ea6
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/TimeSpanParse.cs
@@ -0,0 +1,1675 @@
+// 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.
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Purpose: Used by TimeSpan to parse a time interval string.
+//
+// Standard Format:
+// -=-=-=-=-=-=-=-
+// "c": Constant format. [-][d'.']hh':'mm':'ss['.'fffffff]
+// Not culture sensitive. Default format (and null/empty format string) map to this format.
+//
+// "g": General format, short: [-][d':']h':'mm':'ss'.'FFFFFFF
+// Only print what's needed. Localized (if you want Invariant, pass in Invariant).
+// The fractional seconds separator is localized, equal to the culture's DecimalSeparator.
+//
+// "G": General format, long: [-]d':'hh':'mm':'ss'.'fffffff
+// Always print days and 7 fractional digits. Localized (if you want Invariant, pass in Invariant).
+// The fractional seconds separator is localized, equal to the culture's DecimalSeparator.
+//
+// * "TryParseTimeSpan" is the main method for Parse/TryParse
+//
+// - TimeSpanTokenizer.GetNextToken() is used to split the input string into number and literal tokens.
+// - TimeSpanRawInfo.ProcessToken() adds the next token into the parsing intermediary state structure
+// - ProcessTerminalState() uses the fully initialized TimeSpanRawInfo to find a legal parse match.
+// The terminal states are attempted as follows:
+// foreach (+InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern) try
+// 1 number => d
+// 2 numbers => h:m
+// 3 numbers => h:m:s | d.h:m | h:m:.f
+// 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f
+// 5 numbers => d.h:m:s.f
+//
+// Custom Format:
+// -=-=-=-=-=-=-=
+//
+// * "TryParseExactTimeSpan" is the main method for ParseExact/TryParseExact methods
+// * "TryParseExactMultipleTimeSpan" is the main method for ParseExact/TryparseExact
+// methods that take a string[] of formats
+//
+// - For single-letter formats "TryParseTimeSpan" is called (see above)
+// - For multi-letter formats "TryParseByFormat" is called
+// - TryParseByFormat uses helper methods (ParseExactLiteral, ParseExactDigits, etc)
+// which drive the underlying TimeSpanTokenizer. However, unlike standard formatting which
+// operates on whole-tokens, ParseExact operates at the character-level. As such,
+// TimeSpanTokenizer.NextChar and TimeSpanTokenizer.BackOne() are called directly.
+//
+////////////////////////////////////////////////////////////////////////////
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace System.Globalization
+{
+ internal static class TimeSpanParse
+ {
+ private const int MaxFractionDigits = 7;
+ private const int MaxDays = 10675199;
+ private const int MaxHours = 23;
+ private const int MaxMinutes = 59;
+ private const int MaxSeconds = 59;
+ private const int MaxFraction = 9999999;
+
+ private enum ParseFailureKind : byte
+ {
+ None = 0,
+ ArgumentNull = 1,
+ Format = 2,
+ FormatWithParameter = 3,
+ Overflow = 4,
+ }
+
+ [Flags]
+ private enum TimeSpanStandardStyles : byte
+ {
+ // Standard Format Styles
+ None = 0x00000000,
+ Invariant = 0x00000001, //Allow Invariant Culture
+ Localized = 0x00000002, //Allow Localized Culture
+ RequireFull = 0x00000004, //Require the input to be in DHMSF format
+ Any = Invariant | Localized,
+ }
+
+ // TimeSpan Token Types
+ private enum TTT : byte
+ {
+ None = 0, // None of the TimeSpanToken fields are set
+ End = 1, // '\0'
+ Num = 2, // Number
+ Sep = 3, // literal
+ NumOverflow = 4, // Number that overflowed
+ }
+
+ [IsByRefLike]
+ private struct TimeSpanToken
+ {
+ internal TTT _ttt;
+ internal int _num; // Store the number that we are parsing (if any)
+ internal int _zeroes; // Store the number of leading zeroes (if any)
+ internal ReadOnlySpan<char> _sep; // Store the literal that we are parsing (if any)
+
+ public TimeSpanToken(TTT type) : this(type, 0, 0, default(ReadOnlySpan<char>)) { }
+
+ public TimeSpanToken(int number) : this(TTT.Num, number, 0, default(ReadOnlySpan<char>)) { }
+
+ public TimeSpanToken(int number, int leadingZeroes) : this(TTT.Num, number, leadingZeroes, default(ReadOnlySpan<char>)) { }
+
+ public TimeSpanToken(TTT type, int number, int leadingZeroes, ReadOnlySpan<char> separator)
+ {
+ _ttt = type;
+ _num = number;
+ _zeroes = leadingZeroes;
+ _sep = separator;
+ }
+
+ public bool IsInvalidFraction()
+ {
+ Debug.Assert(_ttt == TTT.Num);
+ Debug.Assert(_num > -1);
+
+ if (_num > MaxFraction || _zeroes > MaxFractionDigits)
+ return true;
+
+ if (_num == 0 || _zeroes == 0)
+ return false;
+
+ // num > 0 && zeroes > 0 && num <= maxValue && zeroes <= maxPrecision
+ return _num >= MaxFraction / Pow10(_zeroes - 1);
+ }
+ }
+
+ [IsByRefLike]
+ private struct TimeSpanTokenizer
+ {
+ private ReadOnlySpan<char> _value;
+ private int _pos;
+
+ internal TimeSpanTokenizer(ReadOnlySpan<char> input) : this(input, 0) { }
+
+ internal TimeSpanTokenizer(ReadOnlySpan<char> input, int startPosition)
+ {
+ _value = input;
+ _pos = startPosition;
+ }
+
+ /// <summary>Returns the next token in the input string</summary>
+ /// <remarks>Used by the parsing routines that operate on standard-formats.</remarks>
+ internal TimeSpanToken GetNextToken()
+ {
+ // Get the position of the next character to be processed. If there is no
+ // next character, we're at the end.
+ int pos = _pos;
+ Debug.Assert(pos > -1);
+ if (pos >= _value.Length)
+ {
+ return new TimeSpanToken(TTT.End);
+ }
+
+ // Now retrieve that character. If it's a digit, we're processing a number.
+ int num = _value[pos] - '0';
+ if ((uint)num <= 9)
+ {
+ int zeroes = 0;
+ if (num == 0)
+ {
+ // Read all leading zeroes.
+ zeroes = 1;
+ while (true)
+ {
+ int digit;
+ if (++_pos >= _value.Length || (uint)(digit = _value[_pos] - '0') > 9)
+ {
+ return new TimeSpanToken(TTT.Num, 0, zeroes, default(ReadOnlySpan<char>));
+ }
+
+ if (digit == 0)
+ {
+ zeroes++;
+ continue;
+ }
+
+ num = digit;
+ break;
+ }
+ }
+
+ // Continue to read as long as we're reading digits.
+ while (++_pos < _value.Length)
+ {
+ int digit = _value[_pos] - '0';
+ if ((uint)digit > 9)
+ {
+ break;
+ }
+
+ num = num * 10 + digit;
+ if ((num & 0xF0000000) != 0)
+ {
+ return new TimeSpanToken(TTT.NumOverflow);
+ }
+ }
+
+ return new TimeSpanToken(TTT.Num, num, zeroes, default(ReadOnlySpan<char>));
+ }
+
+ // Otherwise, we're processing a separator, and we've already processed the first
+ // character of it. Continue processing characters as long as they're not digits.
+ int length = 1;
+ while (true)
+ {
+ if (++_pos >= _value.Length || (uint)(_value[_pos] - '0') <= 9)
+ {
+ break;
+ }
+ length++;
+ }
+
+ // Return the separator.
+ return new TimeSpanToken(TTT.Sep, 0, 0, _value.Slice(pos, length));
+ }
+
+ internal bool EOL => _pos >= (_value.Length - 1);
+
+ internal void BackOne()
+ {
+ if (_pos > 0) --_pos;
+ }
+
+ internal char NextChar
+ {
+ get
+ {
+ int pos = ++_pos;
+ return (uint)pos < (uint)_value.Length ?
+ _value[pos] :
+ (char)0;
+ }
+ }
+ }
+
+ /// <summary>Stores intermediary parsing state for the standard formats.</summary>
+ [IsByRefLike]
+ private struct TimeSpanRawInfo
+ {
+ internal TimeSpanFormat.FormatLiterals PositiveInvariant => TimeSpanFormat.PositiveInvariantFormatLiterals;
+ internal TimeSpanFormat.FormatLiterals NegativeInvariant => TimeSpanFormat.NegativeInvariantFormatLiterals;
+
+ internal TimeSpanFormat.FormatLiterals PositiveLocalized
+ {
+ get
+ {
+ if (!_posLocInit)
+ {
+ _posLoc = new TimeSpanFormat.FormatLiterals();
+ _posLoc.Init(_fullPosPattern, false);
+ _posLocInit = true;
+ }
+ return _posLoc;
+ }
+ }
+
+ internal TimeSpanFormat.FormatLiterals NegativeLocalized
+ {
+ get
+ {
+ if (!_negLocInit)
+ {
+ _negLoc = new TimeSpanFormat.FormatLiterals();
+ _negLoc.Init(_fullNegPattern, false);
+ _negLocInit = true;
+ }
+ return _negLoc;
+ }
+ }
+
+ internal bool FullAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) =>
+ _sepCount == 5
+ && _numCount == 4
+ && StringSpanHelpers.Equals(_literals0, pattern.Start)
+ && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep)
+ && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep)
+ && StringSpanHelpers.Equals(_literals3, pattern.AppCompatLiteral)
+ && StringSpanHelpers.Equals(_literals4, pattern.End);
+
+ internal bool PartialAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) =>
+ _sepCount == 4
+ && _numCount == 3
+ && StringSpanHelpers.Equals(_literals0, pattern.Start)
+ && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep)
+ && StringSpanHelpers.Equals(_literals2, pattern.AppCompatLiteral)
+ && StringSpanHelpers.Equals(_literals3, pattern.End);
+
+ /// <summary>DHMSF (all values matched)</summary>
+ internal bool FullMatch(TimeSpanFormat.FormatLiterals pattern) =>
+ _sepCount == MaxLiteralTokens
+ && _numCount == MaxNumericTokens
+ && StringSpanHelpers.Equals(_literals0, pattern.Start)
+ && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep)
+ && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep)
+ && StringSpanHelpers.Equals(_literals3, pattern.MinuteSecondSep)
+ && StringSpanHelpers.Equals(_literals4, pattern.SecondFractionSep)
+ && StringSpanHelpers.Equals(_literals5, pattern.End);
+
+ /// <summary>D (no hours, minutes, seconds, or fractions)</summary>
+ internal bool FullDMatch(TimeSpanFormat.FormatLiterals pattern) =>
+ _sepCount == 2
+ && _numCount == 1
+ && StringSpanHelpers.Equals(_literals0, pattern.Start)
+ && StringSpanHelpers.Equals(_literals1, pattern.End);
+
+ /// <summary>HM (no days, seconds, or fractions)</summary>
+ internal bool FullHMMatch(TimeSpanFormat.FormatLiterals pattern) =>
+ _sepCount == 3
+ && _numCount == 2
+ && StringSpanHelpers.Equals(_literals0, pattern.Start)
+ && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep)
+ && StringSpanHelpers.Equals(_literals2, pattern.End);
+
+ /// <summary>DHM (no seconds or fraction)</summary>
+ internal bool FullDHMMatch(TimeSpanFormat.FormatLiterals pattern) =>
+ _sepCount == 4
+ && _numCount == 3
+ && StringSpanHelpers.Equals(_literals0, pattern.Start)
+ && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep)
+ && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep)
+ && StringSpanHelpers.Equals(_literals3, pattern.End);
+
+ /// <summary>HMS (no days or fraction)</summary>
+ internal bool FullHMSMatch(TimeSpanFormat.FormatLiterals pattern) =>
+ _sepCount == 4
+ && _numCount == 3
+ && StringSpanHelpers.Equals(_literals0, pattern.Start)
+ && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep)
+ && StringSpanHelpers.Equals(_literals2, pattern.MinuteSecondSep)
+ && StringSpanHelpers.Equals(_literals3, pattern.End);
+
+ /// <summary>DHMS (no fraction)</summary>
+ internal bool FullDHMSMatch(TimeSpanFormat.FormatLiterals pattern) =>
+ _sepCount == 5
+ && _numCount == 4
+ && StringSpanHelpers.Equals(_literals0, pattern.Start)
+ && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep)
+ && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep)
+ && StringSpanHelpers.Equals(_literals3, pattern.MinuteSecondSep)
+ && StringSpanHelpers.Equals(_literals4, pattern.End);
+
+ /// <summary>HMSF (no days)</summary>
+ internal bool FullHMSFMatch(TimeSpanFormat.FormatLiterals pattern) =>
+ _sepCount == 5
+ && _numCount == 4
+ && StringSpanHelpers.Equals(_literals0, pattern.Start)
+ && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep)
+ && StringSpanHelpers.Equals(_literals2, pattern.MinuteSecondSep)
+ && StringSpanHelpers.Equals(_literals3, pattern.SecondFractionSep)
+ && StringSpanHelpers.Equals(_literals4, pattern.End);
+
+ internal TTT _lastSeenTTT;
+ internal int _tokenCount;
+ internal int _sepCount;
+ internal int _numCount;
+
+ private TimeSpanFormat.FormatLiterals _posLoc;
+ private TimeSpanFormat.FormatLiterals _negLoc;
+ private bool _posLocInit;
+ private bool _negLocInit;
+ private string _fullPosPattern;
+ private string _fullNegPattern;
+
+ private const int MaxTokens = 11;
+ private const int MaxLiteralTokens = 6;
+ private const int MaxNumericTokens = 5;
+
+ internal TimeSpanToken _numbers0, _numbers1, _numbers2, _numbers3, _numbers4; // MaxNumbericTokens = 5
+ internal ReadOnlySpan<char> _literals0, _literals1, _literals2, _literals3, _literals4, _literals5; // MaxLiteralTokens=6
+
+ internal void Init(DateTimeFormatInfo dtfi)
+ {
+ Debug.Assert(dtfi != null);
+
+ _lastSeenTTT = TTT.None;
+ _tokenCount = 0;
+ _sepCount = 0;
+ _numCount = 0;
+
+ _fullPosPattern = dtfi.FullTimeSpanPositivePattern;
+ _fullNegPattern = dtfi.FullTimeSpanNegativePattern;
+ _posLocInit = false;
+ _negLocInit = false;
+ }
+
+ internal bool ProcessToken(ref TimeSpanToken tok, ref TimeSpanResult result)
+ {
+ switch (tok._ttt)
+ {
+ case TTT.Num:
+ if ((_tokenCount == 0 && !AddSep(default(ReadOnlySpan<char>), ref result)) || !AddNum(tok, ref result))
+ {
+ return false;
+ }
+ break;
+
+ case TTT.Sep:
+ if (!AddSep(tok._sep, ref result))
+ {
+ return false;
+ }
+ break;
+
+ case TTT.NumOverflow:
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+
+ default:
+ // Some unknown token or a repeat token type in the input
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ _lastSeenTTT = tok._ttt;
+ Debug.Assert(_tokenCount == (_sepCount + _numCount), "tokenCount == (SepCount + NumCount)");
+ return true;
+ }
+
+ private bool AddSep(ReadOnlySpan<char> sep, ref TimeSpanResult result)
+ {
+ if (_sepCount >= MaxLiteralTokens || _tokenCount >= MaxTokens)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ switch (_sepCount++)
+ {
+ case 0: _literals0 = sep; break;
+ case 1: _literals1 = sep; break;
+ case 2: _literals2 = sep; break;
+ case 3: _literals3 = sep; break;
+ case 4: _literals4 = sep; break;
+ default: _literals5 = sep; break;
+ }
+
+ _tokenCount++;
+ return true;
+ }
+ private bool AddNum(TimeSpanToken num, ref TimeSpanResult result)
+ {
+ if (_numCount >= MaxNumericTokens || _tokenCount >= MaxTokens)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ switch (_numCount++)
+ {
+ case 0: _numbers0 = num; break;
+ case 1: _numbers1 = num; break;
+ case 2: _numbers2 = num; break;
+ case 3: _numbers3 = num; break;
+ default: _numbers4 = num; break;
+ }
+
+ _tokenCount++;
+ return true;
+ }
+ }
+
+ /// <summary>Store the result of the parsing.</summary>
+ private struct TimeSpanResult
+ {
+ internal TimeSpan parsedTimeSpan;
+ private readonly bool _throwOnFailure;
+
+ internal TimeSpanResult(bool throwOnFailure)
+ {
+ parsedTimeSpan = default(TimeSpan);
+ _throwOnFailure = throwOnFailure;
+ }
+
+ internal bool SetFailure(ParseFailureKind kind, string resourceKey, object messageArgument = null, string argumentName = null)
+ {
+ if (!_throwOnFailure)
+ {
+ return false;
+ }
+
+ string message = SR.GetResourceString(resourceKey);
+ switch (kind)
+ {
+ case ParseFailureKind.ArgumentNull:
+ Debug.Assert(argumentName != null);
+ throw new ArgumentNullException(argumentName, message);
+
+ case ParseFailureKind.FormatWithParameter:
+ throw new FormatException(SR.Format(message, messageArgument));
+
+ case ParseFailureKind.Overflow:
+ throw new OverflowException(message);
+
+ default:
+ Debug.Assert(kind == ParseFailureKind.Format, $"Unexpected failure {kind}");
+ throw new FormatException(message);
+ }
+ }
+ }
+
+ internal static long Pow10(int pow)
+ {
+ switch (pow)
+ {
+ case 0: return 1;
+ case 1: return 10;
+ case 2: return 100;
+ case 3: return 1000;
+ case 4: return 10000;
+ case 5: return 100000;
+ case 6: return 1000000;
+ case 7: return 10000000;
+ default: return (long)Math.Pow(10, pow);
+ }
+ }
+
+ private static bool TryTimeToTicks(bool positive, TimeSpanToken days, TimeSpanToken hours, TimeSpanToken minutes, TimeSpanToken seconds, TimeSpanToken fraction, out long result)
+ {
+ if (days._num > MaxDays ||
+ hours._num > MaxHours ||
+ minutes._num > MaxMinutes ||
+ seconds._num > MaxSeconds ||
+ fraction.IsInvalidFraction())
+ {
+ result = 0;
+ return false;
+ }
+
+ long ticks = ((long)days._num * 3600 * 24 + (long)hours._num * 3600 + (long)minutes._num * 60 + seconds._num) * 1000;
+ if (ticks > InternalGlobalizationHelper.MaxMilliSeconds || ticks < InternalGlobalizationHelper.MinMilliSeconds)
+ {
+ result = 0;
+ return false;
+ }
+
+ // Normalize the fraction component
+ //
+ // string representation => (zeroes,num) => resultant fraction ticks
+ // --------------------- ------------ ------------------------
+ // ".9999999" => (0,9999999) => 9,999,999 ticks (same as constant maxFraction)
+ // ".1" => (0,1) => 1,000,000 ticks
+ // ".01" => (1,1) => 100,000 ticks
+ // ".001" => (2,1) => 10,000 ticks
+ long f = fraction._num;
+ if (f != 0)
+ {
+ long lowerLimit = InternalGlobalizationHelper.TicksPerTenthSecond;
+ if (fraction._zeroes > 0)
+ {
+ long divisor = Pow10(fraction._zeroes);
+ lowerLimit = lowerLimit / divisor;
+ }
+
+ while (f < lowerLimit)
+ {
+ f *= 10;
+ }
+ }
+
+ result = ticks * TimeSpan.TicksPerMillisecond + f;
+ if (positive && result < 0)
+ {
+ result = 0;
+ return false;
+ }
+
+ return true;
+ }
+
+ internal static TimeSpan Parse(ReadOnlySpan<char> input, IFormatProvider formatProvider)
+ {
+ var parseResult = new TimeSpanResult(throwOnFailure: true);
+ bool success = TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult);
+ Debug.Assert(success, "Should have thrown on failure");
+ return parseResult.parsedTimeSpan;
+ }
+
+ internal static bool TryParse(ReadOnlySpan<char> input, IFormatProvider formatProvider, out TimeSpan result)
+ {
+ var parseResult = new TimeSpanResult(throwOnFailure: false);
+
+ if (TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult))
+ {
+ result = parseResult.parsedTimeSpan;
+ return true;
+ }
+
+ result = default(TimeSpan);
+ return false;
+ }
+
+ internal static TimeSpan ParseExact(ReadOnlySpan<char> input, string format, IFormatProvider formatProvider, TimeSpanStyles styles)
+ {
+ var parseResult = new TimeSpanResult(throwOnFailure: true);
+ bool success = TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult);
+ Debug.Assert(success, "Should have thrown on failure");
+ return parseResult.parsedTimeSpan;
+ }
+
+ internal static bool TryParseExact(ReadOnlySpan<char> input, string format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
+ {
+ var parseResult = new TimeSpanResult(throwOnFailure: false);
+
+ if (TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult))
+ {
+ result = parseResult.parsedTimeSpan;
+ return true;
+ }
+
+ result = default(TimeSpan);
+ return false;
+ }
+
+ internal static TimeSpan ParseExactMultiple(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles)
+ {
+ var parseResult = new TimeSpanResult(throwOnFailure: true);
+ bool success = TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult);
+ Debug.Assert(success, "Should have thrown on failure");
+ return parseResult.parsedTimeSpan;
+ }
+
+ internal static bool TryParseExactMultiple(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
+ {
+ var parseResult = new TimeSpanResult(throwOnFailure: false);
+
+ if (TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult))
+ {
+ result = parseResult.parsedTimeSpan;
+ return true;
+ }
+
+ result = default(TimeSpan);
+ return false;
+ }
+
+ /// <summary>Common private Parse method called by both Parse and TryParse.</summary>
+ private static bool TryParseTimeSpan(ReadOnlySpan<char> input, TimeSpanStandardStyles style, IFormatProvider formatProvider, ref TimeSpanResult result)
+ {
+ input = input.Trim();
+ if (input.IsEmpty)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ var tokenizer = new TimeSpanTokenizer(input);
+
+ var raw = new TimeSpanRawInfo();
+ raw.Init(DateTimeFormatInfo.GetInstance(formatProvider));
+
+ TimeSpanToken tok = tokenizer.GetNextToken();
+
+ // The following loop will break out when we reach the end of the str or
+ // when we can determine that the input is invalid.
+ while (tok._ttt != TTT.End)
+ {
+ if (!raw.ProcessToken(ref tok, ref result))
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+ tok = tokenizer.GetNextToken();
+ }
+ Debug.Assert(tokenizer.EOL);
+
+ if (!ProcessTerminalState(ref raw, style, ref result))
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Validate the terminal state of a standard format parse.
+ /// Sets result.parsedTimeSpan on success.
+ /// Calculates the resultant TimeSpan from the TimeSpanRawInfo.
+ /// </summary>
+ /// <remarks>
+ /// try => +InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern
+ /// 1) Verify Start matches
+ /// 2) Verify End matches
+ /// 3) 1 number => d
+ /// 2 numbers => h:m
+ /// 3 numbers => h:m:s | d.h:m | h:m:.f
+ /// 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f
+ /// 5 numbers => d.h:m:s.f
+ /// </remarks>
+ private static bool ProcessTerminalState(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
+ {
+ if (raw._lastSeenTTT == TTT.Num)
+ {
+ TimeSpanToken tok = new TimeSpanToken();
+ tok._ttt = TTT.Sep;
+ if (!raw.ProcessToken(ref tok, ref result))
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+ }
+
+ switch (raw._numCount)
+ {
+ case 1: return ProcessTerminal_D(ref raw, style, ref result);
+ case 2: return ProcessTerminal_HM(ref raw, style, ref result);
+ case 3: return ProcessTerminal_HM_S_D(ref raw, style, ref result);
+ case 4: return ProcessTerminal_HMS_F_D(ref raw, style, ref result);
+ case 5: return ProcessTerminal_DHMSF(ref raw, style, ref result);
+ default: return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+ }
+
+ /// <summary>Validate the 5-number "Days.Hours:Minutes:Seconds.Fraction" terminal case.</summary>
+ private static bool ProcessTerminal_DHMSF(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
+ {
+ if (raw._sepCount != 6)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+ Debug.Assert(raw._numCount == 5);
+
+ bool inv = (style & TimeSpanStandardStyles.Invariant) != 0;
+ bool loc = (style & TimeSpanStandardStyles.Localized) != 0;
+ bool positive = false;
+ bool match = false;
+
+ if (inv)
+ {
+ if (raw.FullMatch(raw.PositiveInvariant))
+ {
+ match = true;
+ positive = true;
+ }
+ if (!match && raw.FullMatch(raw.NegativeInvariant))
+ {
+ match = true;
+ positive = false;
+ }
+ }
+
+ if (loc)
+ {
+ if (!match && raw.FullMatch(raw.PositiveLocalized))
+ {
+ match = true;
+ positive = true;
+ }
+ if (!match && raw.FullMatch(raw.NegativeLocalized))
+ {
+ match = true;
+ positive = false;
+ }
+ }
+
+ if (match)
+ {
+ long ticks;
+
+ if (!TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, raw._numbers4, out ticks))
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+
+ if (!positive)
+ {
+ ticks = -ticks;
+ if (ticks > 0)
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+ }
+
+ result.parsedTimeSpan._ticks = ticks;
+ return true;
+ }
+
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+
+ /// <summary>
+ /// Validate the ambiguous 4-number "Hours:Minutes:Seconds.Fraction", "Days.Hours:Minutes:Seconds",
+ /// or "Days.Hours:Minutes:.Fraction" terminal case.
+ /// </summary>
+ private static bool ProcessTerminal_HMS_F_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
+ {
+ if (raw._sepCount != 5 || (style & TimeSpanStandardStyles.RequireFull) != 0)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+ Debug.Assert(raw._numCount == 4);
+
+ bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0);
+ bool loc = ((style & TimeSpanStandardStyles.Localized) != 0);
+
+ long ticks = 0;
+ bool positive = false, match = false, overflow = false;
+ var zero = new TimeSpanToken(0);
+
+ if (inv)
+ {
+ if (raw.FullHMSFMatch(raw.PositiveInvariant))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullDHMSMatch(raw.PositiveInvariant))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullAppCompatMatch(raw.PositiveInvariant))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullHMSFMatch(raw.NegativeInvariant))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullDHMSMatch(raw.NegativeInvariant))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullAppCompatMatch(raw.NegativeInvariant))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks);
+ overflow = overflow || !match;
+ }
+ }
+
+ if (loc)
+ {
+ if (!match && raw.FullHMSFMatch(raw.PositiveLocalized))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullDHMSMatch(raw.PositiveLocalized))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullAppCompatMatch(raw.PositiveLocalized))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullHMSFMatch(raw.NegativeLocalized))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullDHMSMatch(raw.NegativeLocalized))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullAppCompatMatch(raw.NegativeLocalized))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks);
+ overflow = overflow || !match;
+ }
+ }
+
+ if (match)
+ {
+ if (!positive)
+ {
+ ticks = -ticks;
+ if (ticks > 0)
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+ }
+
+ result.parsedTimeSpan._ticks = ticks;
+ return true;
+ }
+
+ return overflow ?
+ result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)) : // we found at least one literal pattern match but the numbers just didn't fit
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); // we couldn't find a thing
+ }
+
+ /// <summary>Validate the ambiguous 3-number "Hours:Minutes:Seconds", "Days.Hours:Minutes", or "Hours:Minutes:.Fraction" terminal case.</summary>
+ private static bool ProcessTerminal_HM_S_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
+ {
+ if (raw._sepCount != 4 || (style & TimeSpanStandardStyles.RequireFull) != 0)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+ Debug.Assert(raw._numCount == 3);
+
+ bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0);
+ bool loc = ((style & TimeSpanStandardStyles.Localized) != 0);
+
+ bool positive = false, match = false, overflow = false;
+ var zero = new TimeSpanToken(0);
+ long ticks = 0;
+
+ if (inv)
+ {
+ if (raw.FullHMSMatch(raw.PositiveInvariant))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullDHMMatch(raw.PositiveInvariant))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.PartialAppCompatMatch(raw.PositiveInvariant))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullHMSMatch(raw.NegativeInvariant))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullDHMMatch(raw.NegativeInvariant))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.PartialAppCompatMatch(raw.NegativeInvariant))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks);
+ overflow = overflow || !match;
+ }
+ }
+
+ if (loc)
+ {
+ if (!match && raw.FullHMSMatch(raw.PositiveLocalized))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullDHMMatch(raw.PositiveLocalized))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.PartialAppCompatMatch(raw.PositiveLocalized))
+ {
+ positive = true;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullHMSMatch(raw.NegativeLocalized))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.FullDHMMatch(raw.NegativeLocalized))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks);
+ overflow = overflow || !match;
+ }
+
+ if (!match && raw.PartialAppCompatMatch(raw.NegativeLocalized))
+ {
+ positive = false;
+ match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks);
+ overflow = overflow || !match;
+ }
+ }
+
+ if (match)
+ {
+ if (!positive)
+ {
+ ticks = -ticks;
+ if (ticks > 0)
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+ }
+
+ result.parsedTimeSpan._ticks = ticks;
+ return true;
+ }
+
+ return overflow ?
+ result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)) : // we found at least one literal pattern match but the numbers just didn't fit
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); // we couldn't find a thing
+ }
+
+ /// <summary>Validate the 2-number "Hours:Minutes" terminal case.</summary>
+ private static bool ProcessTerminal_HM(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
+ {
+ if (raw._sepCount != 3 || (style & TimeSpanStandardStyles.RequireFull) != 0)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+ Debug.Assert(raw._numCount == 2);
+
+ bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0);
+ bool loc = ((style & TimeSpanStandardStyles.Localized) != 0);
+
+ bool positive = false, match = false;
+
+ if (inv)
+ {
+ if (raw.FullHMMatch(raw.PositiveInvariant))
+ {
+ match = true;
+ positive = true;
+ }
+
+ if (!match && raw.FullHMMatch(raw.NegativeInvariant))
+ {
+ match = true;
+ positive = false;
+ }
+ }
+
+ if (loc)
+ {
+ if (!match && raw.FullHMMatch(raw.PositiveLocalized))
+ {
+ match = true;
+ positive = true;
+ }
+
+ if (!match && raw.FullHMMatch(raw.NegativeLocalized))
+ {
+ match = true;
+ positive = false;
+ }
+ }
+
+ if (match)
+ {
+ long ticks = 0;
+ var zero = new TimeSpanToken(0);
+
+ if (!TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, zero, out ticks))
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+
+ if (!positive)
+ {
+ ticks = -ticks;
+ if (ticks > 0)
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+ }
+
+ result.parsedTimeSpan._ticks = ticks;
+ return true;
+ }
+
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ /// <summary>Validate the 1-number "Days" terminal case.</summary>
+ private static bool ProcessTerminal_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
+ {
+ if (raw._sepCount != 2 || (style & TimeSpanStandardStyles.RequireFull) != 0)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+ Debug.Assert(raw._numCount == 1);
+
+ bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0);
+ bool loc = ((style & TimeSpanStandardStyles.Localized) != 0);
+
+ bool positive = false, match = false;
+
+ if (inv)
+ {
+ if (raw.FullDMatch(raw.PositiveInvariant))
+ {
+ match = true;
+ positive = true;
+ }
+
+ if (!match && raw.FullDMatch(raw.NegativeInvariant))
+ {
+ match = true;
+ positive = false;
+ }
+ }
+
+ if (loc)
+ {
+ if (!match && raw.FullDMatch(raw.PositiveLocalized))
+ {
+ match = true;
+ positive = true;
+ }
+
+ if (!match && raw.FullDMatch(raw.NegativeLocalized))
+ {
+ match = true;
+ positive = false;
+ }
+ }
+
+ if (match)
+ {
+ long ticks = 0;
+ var zero = new TimeSpanToken(0);
+
+ if (!TryTimeToTicks(positive, raw._numbers0, zero, zero, zero, zero, out ticks))
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+
+ if (!positive)
+ {
+ ticks = -ticks;
+ if (ticks > 0)
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+ }
+
+ result.parsedTimeSpan._ticks = ticks;
+ return true;
+ }
+
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ /// <summary>Common private ParseExact method called by both ParseExact and TryParseExact.</summary>
+ private static bool TryParseExactTimeSpan(ReadOnlySpan<char> input, string format, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result)
+ {
+ if (format == null)
+ {
+ return result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), argumentName: nameof(format));
+ }
+
+ if (format.Length == 0)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier));
+ }
+
+ if (format.Length == 1)
+ {
+ switch (format[0])
+ {
+ case 'c':
+ case 't':
+ case 'T':
+ return TryParseTimeSpanConstant(input, ref result); // fast path for legacy style TimeSpan formats.
+
+ case 'g':
+ return TryParseTimeSpan(input, TimeSpanStandardStyles.Localized, formatProvider, ref result);
+
+ case 'G':
+ return TryParseTimeSpan(input, TimeSpanStandardStyles.Localized | TimeSpanStandardStyles.RequireFull, formatProvider, ref result);
+
+ default:
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier));
+ }
+ }
+
+ return TryParseByFormat(input, format, styles, ref result);
+ }
+
+ /// <summary>Parse the TimeSpan instance using the specified format. Used by TryParseExactTimeSpan.</summary>
+ private static bool TryParseByFormat(ReadOnlySpan<char> input, string format, TimeSpanStyles styles, ref TimeSpanResult result)
+ {
+ Debug.Assert(format != null, "format != null");
+
+ bool seenDD = false; // already processed days?
+ bool seenHH = false; // already processed hours?
+ bool seenMM = false; // already processed minutes?
+ bool seenSS = false; // already processed seconds?
+ bool seenFF = false; // already processed fraction?
+
+ int dd = 0; // parsed days
+ int hh = 0; // parsed hours
+ int mm = 0; // parsed minutes
+ int ss = 0; // parsed seconds
+ int leadingZeroes = 0; // number of leading zeroes in the parsed fraction
+ int ff = 0; // parsed fraction
+ int i = 0; // format string position
+ int tokenLen = 0; // length of current format token, used to update index 'i'
+
+ var tokenizer = new TimeSpanTokenizer(input, -1);
+
+ while (i < format.Length)
+ {
+ char ch = format[i];
+ int nextFormatChar;
+ switch (ch)
+ {
+ case 'h':
+ tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
+ if (tokenLen > 2 || seenHH || !ParseExactDigits(ref tokenizer, tokenLen, out hh))
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString));
+ }
+ seenHH = true;
+ break;
+
+ case 'm':
+ tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
+ if (tokenLen > 2 || seenMM || !ParseExactDigits(ref tokenizer, tokenLen, out mm))
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString));
+ }
+ seenMM = true;
+ break;
+
+ case 's':
+ tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
+ if (tokenLen > 2 || seenSS || !ParseExactDigits(ref tokenizer, tokenLen, out ss))
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString));
+ }
+ seenSS = true;
+ break;
+
+ case 'f':
+ tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
+ if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF || !ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff))
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString));
+ }
+ seenFF = true;
+ break;
+
+ case 'F':
+ tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
+ if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString));
+ }
+ ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff);
+ seenFF = true;
+ break;
+
+ case 'd':
+ tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
+ int tmp = 0;
+ if (tokenLen > 8 || seenDD || !ParseExactDigits(ref tokenizer, (tokenLen < 2) ? 1 : tokenLen, (tokenLen < 2) ? 8 : tokenLen, out tmp, out dd))
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString));
+ }
+ seenDD = true;
+ break;
+
+ case '\'':
+ case '\"':
+ StringBuilder enquotedString = new StringBuilder();
+ if (!DateTimeParse.TryParseQuoteString(format, i, enquotedString, out tokenLen))
+ {
+ return result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadQuote), ch);
+ }
+ if (!ParseExactLiteral(ref tokenizer, enquotedString))
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString));
+ }
+ break;
+
+ case '%':
+ // Optional format character.
+ // For example, format string "%d" will print day
+ // Most of the cases, "%" can be ignored.
+ nextFormatChar = DateTimeFormat.ParseNextChar(format, i);
+
+ // nextFormatChar will be -1 if we already reach the end of the format string.
+ // Besides, we will not allow "%%" appear in the pattern.
+ if (nextFormatChar >= 0 && nextFormatChar != '%')
+ {
+ tokenLen = 1; // skip the '%' and process the format character
+ break;
+ }
+ else
+ {
+ // This means that '%' is at the end of the format string or
+ // "%%" appears in the format string.
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString));
+ }
+
+ case '\\':
+ // Escaped character. Can be used to insert character into the format string.
+ // For example, "\d" will insert the character 'd' into the string.
+ //
+ nextFormatChar = DateTimeFormat.ParseNextChar(format, i);
+ if (nextFormatChar >= 0 && tokenizer.NextChar == (char)nextFormatChar)
+ {
+ tokenLen = 2;
+ }
+ else
+ {
+ // This means that '\' is at the end of the format string or the literal match failed.
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString));
+ }
+ break;
+
+ default:
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString));
+ }
+
+ i += tokenLen;
+ }
+
+
+ if (!tokenizer.EOL)
+ {
+ // the custom format didn't consume the entire input
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ bool positive = (styles & TimeSpanStyles.AssumeNegative) == 0;
+ if (TryTimeToTicks(positive, new TimeSpanToken(dd),
+ new TimeSpanToken(hh),
+ new TimeSpanToken(mm),
+ new TimeSpanToken(ss),
+ new TimeSpanToken(ff, leadingZeroes),
+ out long ticks))
+ {
+ if (!positive)
+ {
+ ticks = -ticks;
+ }
+
+ result.parsedTimeSpan._ticks = ticks;
+ return true;
+ }
+ else
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+ }
+
+ private static bool ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, out int result)
+ {
+ result = 0;
+ int zeroes = 0;
+ int maxDigitLength = (minDigitLength == 1) ? 2 : minDigitLength;
+ return ParseExactDigits(ref tokenizer, minDigitLength, maxDigitLength, out zeroes, out result);
+ }
+
+ private static bool ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, int maxDigitLength, out int zeroes, out int result)
+ {
+ int tmpResult = 0, tmpZeroes = 0;
+
+ int tokenLength = 0;
+ while (tokenLength < maxDigitLength)
+ {
+ char ch = tokenizer.NextChar;
+ if (ch < '0' || ch > '9')
+ {
+ tokenizer.BackOne();
+ break;
+ }
+
+ tmpResult = tmpResult * 10 + (ch - '0');
+ if (tmpResult == 0) tmpZeroes++;
+ tokenLength++;
+ }
+
+ zeroes = tmpZeroes;
+ result = tmpResult;
+ return tokenLength >= minDigitLength;
+ }
+
+ private static bool ParseExactLiteral(ref TimeSpanTokenizer tokenizer, StringBuilder enquotedString)
+ {
+ for (int i = 0; i < enquotedString.Length; i++)
+ {
+ if (enquotedString[i] != tokenizer.NextChar)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Parses the "c" (constant) format. This code is 100% identical to the non-globalized v1.0-v3.5 TimeSpan.Parse() routine
+ /// and exists for performance/appcompat with legacy callers who cannot move onto the globalized Parse overloads.
+ /// </summary>
+ private static bool TryParseTimeSpanConstant(ReadOnlySpan<char> input, ref TimeSpanResult result) =>
+ new StringParser().TryParse(input, ref result);
+
+ [IsByRefLike]
+ private struct StringParser
+ {
+ private ReadOnlySpan<char> _str;
+ private char _ch;
+ private int _pos;
+ private int _len;
+
+ internal void NextChar()
+ {
+ if (_pos < _len)
+ {
+ _pos++;
+ }
+
+ _ch = _pos < _len ?
+ _str[_pos] :
+ (char)0;
+ }
+
+ internal char NextNonDigit()
+ {
+ int i = _pos;
+ while (i < _len)
+ {
+ char ch = _str[i];
+ if (ch < '0' || ch > '9') return ch;
+ i++;
+ }
+
+ return (char)0;
+ }
+
+ internal bool TryParse(ReadOnlySpan<char> input, ref TimeSpanResult result)
+ {
+ result.parsedTimeSpan._ticks = 0;
+
+ _str = input;
+ _len = input.Length;
+ _pos = -1;
+ NextChar();
+ SkipBlanks();
+
+ bool negative = false;
+ if (_ch == '-')
+ {
+ negative = true;
+ NextChar();
+ }
+
+ long time;
+ if (NextNonDigit() == ':')
+ {
+ if (!ParseTime(out time, ref result))
+ {
+ return false;
+ };
+ }
+ else
+ {
+ int days;
+ if (!ParseInt((int)(0x7FFFFFFFFFFFFFFFL / TimeSpan.TicksPerDay), out days, ref result))
+ {
+ return false;
+ }
+
+ time = days * TimeSpan.TicksPerDay;
+
+ if (_ch == '.')
+ {
+ NextChar();
+ long remainingTime;
+ if (!ParseTime(out remainingTime, ref result))
+ {
+ return false;
+ };
+ time += remainingTime;
+ }
+ }
+
+ if (negative)
+ {
+ time = -time;
+ // Allow -0 as well
+ if (time > 0)
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+ }
+ else
+ {
+ if (time < 0)
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+ }
+
+ SkipBlanks();
+
+ if (_pos < _len)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ result.parsedTimeSpan._ticks = time;
+ return true;
+ }
+
+ internal bool ParseInt(int max, out int i, ref TimeSpanResult result)
+ {
+ i = 0;
+ int p = _pos;
+ while (_ch >= '0' && _ch <= '9')
+ {
+ if ((i & 0xF0000000) != 0)
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+
+ i = i * 10 + _ch - '0';
+ if (i < 0)
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+
+ NextChar();
+ }
+
+ if (p == _pos)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ if (i > max)
+ {
+ return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge));
+ }
+
+ return true;
+ }
+
+ internal bool ParseTime(out long time, ref TimeSpanResult result)
+ {
+ time = 0;
+ int unit;
+
+ if (!ParseInt(23, out unit, ref result))
+ {
+ return false;
+ }
+
+ time = unit * TimeSpan.TicksPerHour;
+ if (_ch != ':')
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ NextChar();
+
+ if (!ParseInt(59, out unit, ref result))
+ {
+ return false;
+ }
+
+ time += unit * TimeSpan.TicksPerMinute;
+
+ if (_ch == ':')
+ {
+ NextChar();
+
+ // allow seconds with the leading zero
+ if (_ch != '.')
+ {
+ if (!ParseInt(59, out unit, ref result))
+ {
+ return false;
+ }
+ time += unit * TimeSpan.TicksPerSecond;
+ }
+
+ if (_ch == '.')
+ {
+ NextChar();
+ int f = (int)TimeSpan.TicksPerSecond;
+ while (f > 1 && _ch >= '0' && _ch <= '9')
+ {
+ f /= 10;
+ time += (_ch - '0') * f;
+ NextChar();
+ }
+ }
+ }
+
+ return true;
+ }
+
+ internal void SkipBlanks()
+ {
+ while (_ch == ' ' || _ch == '\t') NextChar();
+ }
+ }
+
+ /// <summary>Common private ParseExactMultiple method called by both ParseExactMultiple and TryParseExactMultiple.</summary>
+ private static bool TryParseExactMultipleTimeSpan(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result)
+ {
+ if (formats == null)
+ {
+ return result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), argumentName: nameof(formats));
+ }
+
+ if (input.Length == 0)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+
+ if (formats.Length == 0)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier));
+ }
+
+ // Do a loop through the provided formats and see if we can parse succesfully in
+ // one of the formats.
+ for (int i = 0; i < formats.Length; i++)
+ {
+ if (formats[i] == null || formats[i].Length == 0)
+ {
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier));
+ }
+
+ // Create a new non-throwing result each time to ensure the runs are independent.
+ TimeSpanResult innerResult = new TimeSpanResult(throwOnFailure: false);
+
+ if (TryParseExactTimeSpan(input, formats[i], formatProvider, styles, ref innerResult))
+ {
+ result.parsedTimeSpan = innerResult.parsedTimeSpan;
+ return true;
+ }
+ }
+
+ return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan));
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/Guid.cs b/src/mscorlib/shared/System/Guid.cs
index aad188be3d..b8b4dd5f0f 100644
--- a/src/mscorlib/src/System/Guid.cs
+++ b/src/mscorlib/shared/System/Guid.cs
@@ -2,11 +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.
-
-using System;
-using System.Globalization;
-using System.Text;
-using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Diagnostics;
@@ -17,12 +12,12 @@ namespace System
// Represents a Globally Unique Identifier.
[StructLayout(LayoutKind.Sequential)]
[Serializable]
- [System.Runtime.Versioning.NonVersionable] // This only applies to field layout
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct Guid : IFormattable, IComparable
- , IComparable<Guid>, IEquatable<Guid>
+ [Runtime.Versioning.NonVersionable] // This only applies to field layout
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public partial struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid>
{
public static readonly Guid Empty = new Guid();
+
////////////////////////////////////////////////////////////////////////////////
// Member variables
////////////////////////////////////////////////////////////////////////////////
@@ -38,25 +33,25 @@ namespace System
private byte _j; // Do not rename (binary serialization)
private byte _k; // Do not rename (binary serialization)
-
-
////////////////////////////////////////////////////////////////////////////////
// Constructors
////////////////////////////////////////////////////////////////////////////////
// Creates a new guid from an array of bytes.
- //
- public Guid(byte[] b)
+ public Guid(byte[] b) :
+ this(new ReadOnlySpan<byte>(b ?? throw new ArgumentNullException(nameof(b))))
+ {
+ }
+
+ // Creates a new guid from a read-only span.
+ public Guid(ReadOnlySpan<byte> b)
{
- if (b == null)
- throw new ArgumentNullException(nameof(b));
if (b.Length != 16)
throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "16"), nameof(b));
- Contract.EndContractBlock();
- _a = ((int)b[3] << 24) | ((int)b[2] << 16) | ((int)b[1] << 8) | b[0];
- _b = (short)(((int)b[5] << 8) | b[4]);
- _c = (short)(((int)b[7] << 8) | b[6]);
+ _a = b[3] << 24 | b[2] << 16 | b[1] << 8 | b[0];
+ _b = (short)(b[5] << 8 | b[4]);
+ _c = (short)(b[7] << 8 | b[6]);
_d = b[8];
_e = b[9];
_f = b[10];
@@ -83,7 +78,6 @@ namespace System
_k = k;
}
-
// Creates a new GUID initialized to the value represented by the arguments.
//
public Guid(int a, short b, short c, byte[] d)
@@ -166,43 +160,46 @@ namespace System
// This will store the result of the parsing. And it will eventually be used to construct a Guid instance.
private struct GuidResult
{
- internal Guid parsedGuid;
- internal GuidParseThrowStyle throwStyle;
+ internal Guid _parsedGuid;
+ internal GuidParseThrowStyle _throwStyle;
- internal ParseFailureKind m_failure;
- internal string m_failureMessageID;
- internal object m_failureMessageFormatArgument;
- internal string m_failureArgumentName;
- internal Exception m_innerException;
+ private ParseFailureKind _failure;
+ private string _failureMessageID;
+ private object _failureMessageFormatArgument;
+ private string _failureArgumentName;
+ private Exception _innerException;
internal void Init(GuidParseThrowStyle canThrow)
{
- parsedGuid = Guid.Empty;
- throwStyle = canThrow;
+ _throwStyle = canThrow;
}
+
internal void SetFailure(Exception nativeException)
{
- m_failure = ParseFailureKind.NativeException;
- m_innerException = nativeException;
+ _failure = ParseFailureKind.NativeException;
+ _innerException = nativeException;
}
+
internal void SetFailure(ParseFailureKind failure, string failureMessageID)
{
SetFailure(failure, failureMessageID, null, null, null);
}
+
internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument)
{
SetFailure(failure, failureMessageID, failureMessageFormatArgument, null, null);
}
+
internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument,
string failureArgumentName, Exception innerException)
{
Debug.Assert(failure != ParseFailureKind.NativeException, "ParseFailureKind.NativeException should not be used with this overload");
- m_failure = failure;
- m_failureMessageID = failureMessageID;
- m_failureMessageFormatArgument = failureMessageFormatArgument;
- m_failureArgumentName = failureArgumentName;
- m_innerException = innerException;
- if (throwStyle != GuidParseThrowStyle.None)
+ _failure = failure;
+ _failureMessageID = failureMessageID;
+ _failureMessageFormatArgument = failureMessageFormatArgument;
+ _failureArgumentName = failureArgumentName;
+ _innerException = innerException;
+ if (_throwStyle != GuidParseThrowStyle.None)
{
throw GetGuidParseException();
}
@@ -210,25 +207,25 @@ namespace System
internal Exception GetGuidParseException()
{
- switch (m_failure)
+ switch (_failure)
{
case ParseFailureKind.ArgumentNull:
- return new ArgumentNullException(m_failureArgumentName, SR.GetResourceString(m_failureMessageID));
+ return new ArgumentNullException(_failureArgumentName, SR.GetResourceString(_failureMessageID));
case ParseFailureKind.FormatWithInnerException:
- return new FormatException(SR.GetResourceString(m_failureMessageID), m_innerException);
+ return new FormatException(SR.GetResourceString(_failureMessageID), _innerException);
case ParseFailureKind.FormatWithParameter:
- return new FormatException(SR.Format(SR.GetResourceString(m_failureMessageID), m_failureMessageFormatArgument));
+ return new FormatException(SR.Format(SR.GetResourceString(_failureMessageID), _failureMessageFormatArgument));
case ParseFailureKind.Format:
- return new FormatException(SR.GetResourceString(m_failureMessageID));
+ return new FormatException(SR.GetResourceString(_failureMessageID));
case ParseFailureKind.NativeException:
- return m_innerException;
+ return _innerException;
default:
- Debug.Assert(false, "Unknown GuidParseFailure: " + m_failure);
+ Debug.Assert(false, "Unknown GuidParseFailure: " + _failure);
return new FormatException(SR.Format_GuidUnrecognized);
}
}
@@ -242,20 +239,19 @@ namespace System
// d is a hex digit. (That is 8 hex digits, followed by 4, then 4, then 4,
// then 12) such as: "CA761232-ED42-11CE-BACD-00AA0057B223"
//
- public Guid(String g)
+ public Guid(string g)
{
if (g == null)
{
throw new ArgumentNullException(nameof(g));
}
Contract.EndContractBlock();
- this = Guid.Empty;
GuidResult result = new GuidResult();
result.Init(GuidParseThrowStyle.All);
if (TryParseGuid(g, GuidStyles.Any, ref result))
{
- this = result.parsedGuid;
+ this = result._parsedGuid;
}
else
{
@@ -263,8 +259,7 @@ namespace System
}
}
-
- public static Guid Parse(String input)
+ public static Guid Parse(string input)
{
if (input == null)
{
@@ -276,7 +271,7 @@ namespace System
result.Init(GuidParseThrowStyle.AllButOverflow);
if (TryParseGuid(input, GuidStyles.Any, ref result))
{
- return result.parsedGuid;
+ return result._parsedGuid;
}
else
{
@@ -284,23 +279,23 @@ namespace System
}
}
- public static bool TryParse(String input, out Guid result)
+ public static bool TryParse(string input, out Guid result)
{
GuidResult parseResult = new GuidResult();
parseResult.Init(GuidParseThrowStyle.None);
if (TryParseGuid(input, GuidStyles.Any, ref parseResult))
{
- result = parseResult.parsedGuid;
+ result = parseResult._parsedGuid;
return true;
}
else
{
- result = Guid.Empty;
+ result = Empty;
return false;
}
}
- public static Guid ParseExact(String input, String format)
+ public static Guid ParseExact(string input, string format)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
@@ -345,7 +340,7 @@ namespace System
result.Init(GuidParseThrowStyle.AllButOverflow);
if (TryParseGuid(input, style, ref result))
{
- return result.parsedGuid;
+ return result._parsedGuid;
}
else
{
@@ -353,11 +348,11 @@ namespace System
}
}
- public static bool TryParseExact(String input, String format, out Guid result)
+ public static bool TryParseExact(string input, string format, out Guid result)
{
if (format == null || format.Length != 1)
{
- result = Guid.Empty;
+ result = Empty;
return false;
}
@@ -387,7 +382,7 @@ namespace System
else
{
// invalid guid format specification
- result = Guid.Empty;
+ result = Empty;
return false;
}
@@ -395,29 +390,28 @@ namespace System
parseResult.Init(GuidParseThrowStyle.None);
if (TryParseGuid(input, style, ref parseResult))
{
- result = parseResult.parsedGuid;
+ result = parseResult._parsedGuid;
return true;
}
else
{
- result = Guid.Empty;
+ result = Empty;
return false;
}
}
-
- private static bool TryParseGuid(String g, GuidStyles flags, ref GuidResult result)
+ private static bool TryParseGuid(string g, GuidStyles flags, ref GuidResult result)
{
if (g == null)
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidUnrecognized");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized));
return false;
}
- String guidString = g.Trim(); //Remove Whitespace
+ string guidString = g.Trim(); //Remove Whitespace
if (guidString.Length == 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidUnrecognized");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized));
return false;
}
@@ -429,7 +423,7 @@ namespace System
if ((flags & (GuidStyles.AllowDashes | GuidStyles.RequireDashes)) == 0)
{
// dashes are not allowed
- result.SetFailure(ParseFailureKind.Format, "Format_GuidUnrecognized");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized));
return false;
}
}
@@ -438,7 +432,7 @@ namespace System
if ((flags & GuidStyles.RequireDashes) != 0)
{
// dashes are required
- result.SetFailure(ParseFailureKind.Format, "Format_GuidUnrecognized");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized));
return false;
}
}
@@ -451,7 +445,7 @@ namespace System
if ((flags & (GuidStyles.AllowBraces | GuidStyles.RequireBraces)) == 0)
{
// braces are not allowed
- result.SetFailure(ParseFailureKind.Format, "Format_GuidUnrecognized");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized));
return false;
}
}
@@ -460,7 +454,7 @@ namespace System
if ((flags & GuidStyles.RequireBraces) != 0)
{
// braces are required
- result.SetFailure(ParseFailureKind.Format, "Format_GuidUnrecognized");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized));
return false;
}
}
@@ -473,7 +467,7 @@ namespace System
if ((flags & (GuidStyles.AllowParenthesis | GuidStyles.RequireParenthesis)) == 0)
{
// parenthesis are not allowed
- result.SetFailure(ParseFailureKind.Format, "Format_GuidUnrecognized");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized));
return false;
}
}
@@ -482,7 +476,7 @@ namespace System
if ((flags & GuidStyles.RequireParenthesis) != 0)
{
// parenthesis are required
- result.SetFailure(ParseFailureKind.Format, "Format_GuidUnrecognized");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized));
return false;
}
}
@@ -508,19 +502,18 @@ namespace System
}
catch (IndexOutOfRangeException ex)
{
- result.SetFailure(ParseFailureKind.FormatWithInnerException, "Format_GuidUnrecognized", null, null, ex);
+ result.SetFailure(ParseFailureKind.FormatWithInnerException, nameof(SR.Format_GuidUnrecognized), null, null, ex);
return false;
}
catch (ArgumentException ex)
{
- result.SetFailure(ParseFailureKind.FormatWithInnerException, "Format_GuidUnrecognized", null, null, ex);
+ result.SetFailure(ParseFailureKind.FormatWithInnerException, nameof(SR.Format_GuidUnrecognized), null, null, ex);
return false;
}
}
-
// Check if it's of the form {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}}
- private static bool TryParseGuidWithHexPrefix(String guidString, ref GuidResult result)
+ private static bool TryParseGuidWithHexPrefix(string guidString, ref GuidResult result)
{
int numStart = 0;
int numLen = 0;
@@ -529,16 +522,16 @@ namespace System
guidString = EatAllWhitespace(guidString);
// Check for leading '{'
- if (String.IsNullOrEmpty(guidString) || guidString[0] != '{')
+ if (string.IsNullOrEmpty(guidString) || guidString[0] != '{')
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidBrace");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBrace));
return false;
}
// Check for '0x'
if (!IsHexPrefix(guidString, 1))
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidHexPrefix", "{0xdddddddd, etc}");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, etc}");
return false;
}
@@ -547,18 +540,17 @@ namespace System
numLen = guidString.IndexOf(',', numStart) - numStart;
if (numLen <= 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidComma");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma));
return false;
}
-
- if (!StringToInt(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result.parsedGuid._a, ref result))
+ if (!StringToInt(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result))
return false;
// Check for '0x'
if (!IsHexPrefix(guidString, numStart + numLen + 1))
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidHexPrefix", "{0xdddddddd, 0xdddd, etc}");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, etc}");
return false;
}
// +3 to get by ',0x'
@@ -566,17 +558,17 @@ namespace System
numLen = guidString.IndexOf(',', numStart) - numStart;
if (numLen <= 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidComma");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma));
return false;
}
// Read in the number
- if (!StringToShort(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result.parsedGuid._b, ref result))
+ if (!StringToShort(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result))
return false;
// Check for '0x'
if (!IsHexPrefix(guidString, numStart + numLen + 1))
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidHexPrefix", "{0xdddddddd, 0xdddd, 0xdddd, etc}");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, 0xdddd, etc}");
return false;
}
// +3 to get by ',0x'
@@ -584,18 +576,18 @@ namespace System
numLen = guidString.IndexOf(',', numStart) - numStart;
if (numLen <= 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidComma");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma));
return false;
}
// Read in the number
- if (!StringToShort(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result.parsedGuid._c, ref result))
+ if (!StringToShort(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result))
return false;
// Check for '{'
if (guidString.Length <= numStart + numLen + 1 || guidString[numStart + numLen + 1] != '{')
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidBrace");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBrace));
return false;
}
@@ -608,7 +600,7 @@ namespace System
// Check for '0x'
if (!IsHexPrefix(guidString, numStart + numLen + 1))
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidHexPrefix", "{... { ... 0xdd, ...}}");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{... { ... 0xdd, ...}}");
return false;
}
@@ -621,7 +613,7 @@ namespace System
numLen = guidString.IndexOf(',', numStart) - numStart;
if (numLen <= 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidComma");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma));
return false;
}
}
@@ -630,7 +622,7 @@ namespace System
numLen = guidString.IndexOf('}', numStart) - numStart;
if (numLen <= 0)
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidBraceAfterLastNumber");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBraceAfterLastNumber));
return false;
}
}
@@ -646,32 +638,32 @@ namespace System
// check for overflow
if (number > 255)
{
- result.SetFailure(ParseFailureKind.Format, "Overflow_Byte");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Overflow_Byte));
return false;
}
bytes[i] = (byte)number;
}
- result.parsedGuid._d = bytes[0];
- result.parsedGuid._e = bytes[1];
- result.parsedGuid._f = bytes[2];
- result.parsedGuid._g = bytes[3];
- result.parsedGuid._h = bytes[4];
- result.parsedGuid._i = bytes[5];
- result.parsedGuid._j = bytes[6];
- result.parsedGuid._k = bytes[7];
+ result._parsedGuid._d = bytes[0];
+ result._parsedGuid._e = bytes[1];
+ result._parsedGuid._f = bytes[2];
+ result._parsedGuid._g = bytes[3];
+ result._parsedGuid._h = bytes[4];
+ result._parsedGuid._i = bytes[5];
+ result._parsedGuid._j = bytes[6];
+ result._parsedGuid._k = bytes[7];
// Check for last '}'
if (numStart + numLen + 1 >= guidString.Length || guidString[numStart + numLen + 1] != '}')
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidEndBrace");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidEndBrace));
return false;
}
// Check if we have extra characters at the end
if (numStart + numLen + 1 != guidString.Length - 1)
{
- result.SetFailure(ParseFailureKind.Format, "Format_ExtraJunkAtEnd");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_ExtraJunkAtEnd));
return false;
}
@@ -679,7 +671,7 @@ namespace System
}
// Check if it's of the form dddddddddddddddddddddddddddddddd
- private static bool TryParseGuidWithNoStyle(String guidString, ref GuidResult result)
+ private static bool TryParseGuidWithNoStyle(string guidString, ref GuidResult result)
{
int startPos = 0;
int temp;
@@ -688,7 +680,7 @@ namespace System
if (guidString.Length != 32)
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidInvLen");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen));
return false;
}
@@ -701,26 +693,26 @@ namespace System
}
else
{
- char upperCaseCh = Char.ToUpperInvariant(ch);
+ char upperCaseCh = char.ToUpperInvariant(ch);
if (upperCaseCh >= 'A' && upperCaseCh <= 'F')
{
continue;
}
}
- result.SetFailure(ParseFailureKind.Format, "Format_GuidInvalidChar");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvalidChar));
return false;
}
- if (!StringToInt(guidString.Substring(startPos, 8) /*first DWORD*/, -1, ParseNumbers.IsTight, out result.parsedGuid._a, ref result))
+ if (!StringToInt(guidString.Substring(startPos, 8) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result))
return false;
startPos += 8;
- if (!StringToShort(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out result.parsedGuid._b, ref result))
+ if (!StringToShort(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result))
return false;
startPos += 4;
- if (!StringToShort(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out result.parsedGuid._c, ref result))
+ if (!StringToShort(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result))
return false;
startPos += 4;
@@ -735,27 +727,26 @@ namespace System
if (currentPos - startPos != 12)
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidInvLen");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen));
return false;
}
- result.parsedGuid._d = (byte)(temp >> 8);
- result.parsedGuid._e = (byte)(temp);
+ result._parsedGuid._d = (byte)(temp >> 8);
+ result._parsedGuid._e = (byte)(temp);
temp = (int)(templ >> 32);
- result.parsedGuid._f = (byte)(temp >> 8);
- result.parsedGuid._g = (byte)(temp);
+ result._parsedGuid._f = (byte)(temp >> 8);
+ result._parsedGuid._g = (byte)(temp);
temp = (int)(templ);
- result.parsedGuid._h = (byte)(temp >> 24);
- result.parsedGuid._i = (byte)(temp >> 16);
- result.parsedGuid._j = (byte)(temp >> 8);
- result.parsedGuid._k = (byte)(temp);
+ result._parsedGuid._h = (byte)(temp >> 24);
+ result._parsedGuid._i = (byte)(temp >> 16);
+ result._parsedGuid._j = (byte)(temp >> 8);
+ result._parsedGuid._k = (byte)(temp);
return true;
}
-
// Check if it's of the form [{|(]dddddddd-dddd-dddd-dddd-dddddddddddd[}|)]
- private static bool TryParseGuidWithDashes(String guidString, ref GuidResult result)
+ private static bool TryParseGuidWithDashes(string guidString, ref GuidResult result)
{
int startPos = 0;
int temp;
@@ -767,7 +758,7 @@ namespace System
{
if (guidString.Length != 38 || guidString[37] != '}')
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidInvLen");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen));
return false;
}
startPos = 1;
@@ -776,14 +767,14 @@ namespace System
{
if (guidString.Length != 38 || guidString[37] != ')')
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidInvLen");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen));
return false;
}
startPos = 1;
}
else if (guidString.Length != 36)
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidInvLen");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen));
return false;
}
@@ -792,24 +783,24 @@ namespace System
guidString[18 + startPos] != '-' ||
guidString[23 + startPos] != '-')
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidDashes");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidDashes));
return false;
}
currentPos = startPos;
if (!StringToInt(guidString, ref currentPos, 8, ParseNumbers.NoSpace, out temp, ref result))
return false;
- result.parsedGuid._a = temp;
+ result._parsedGuid._a = temp;
++currentPos; //Increment past the '-';
if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result))
return false;
- result.parsedGuid._b = (short)temp;
+ result._parsedGuid._b = (short)temp;
++currentPos; //Increment past the '-';
if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result))
return false;
- result.parsedGuid._c = (short)temp;
+ result._parsedGuid._c = (short)temp;
++currentPos; //Increment past the '-';
if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result))
@@ -822,67 +813,60 @@ namespace System
if (currentPos - startPos != 12)
{
- result.SetFailure(ParseFailureKind.Format, "Format_GuidInvLen");
+ result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen));
return false;
}
- result.parsedGuid._d = (byte)(temp >> 8);
- result.parsedGuid._e = (byte)(temp);
+ result._parsedGuid._d = (byte)(temp >> 8);
+ result._parsedGuid._e = (byte)(temp);
temp = (int)(templ >> 32);
- result.parsedGuid._f = (byte)(temp >> 8);
- result.parsedGuid._g = (byte)(temp);
+ result._parsedGuid._f = (byte)(temp >> 8);
+ result._parsedGuid._g = (byte)(temp);
temp = (int)(templ);
- result.parsedGuid._h = (byte)(temp >> 24);
- result.parsedGuid._i = (byte)(temp >> 16);
- result.parsedGuid._j = (byte)(temp >> 8);
- result.parsedGuid._k = (byte)(temp);
+ result._parsedGuid._h = (byte)(temp >> 24);
+ result._parsedGuid._i = (byte)(temp >> 16);
+ result._parsedGuid._j = (byte)(temp >> 8);
+ result._parsedGuid._k = (byte)(temp);
return true;
}
-
- //
- // StringToShort, StringToInt, and StringToLong are wrappers around COMUtilNative integer parsing routines;
-
- private static unsafe bool StringToShort(String str, int requiredLength, int flags, out short result, ref GuidResult parseResult)
+ private static bool StringToShort(string str, int requiredLength, int flags, out short result, ref GuidResult parseResult)
{
- return StringToShort(str, null, requiredLength, flags, out result, ref parseResult);
+ int parsePos = 0;
+ return StringToShort(str, ref parsePos, requiredLength, flags, out result, ref parseResult);
}
- private static unsafe bool StringToShort(String str, int* parsePos, int requiredLength, int flags, out short result, ref GuidResult parseResult)
+
+ private static bool StringToShort(string str, ref int parsePos, int requiredLength, int flags, out short result, ref GuidResult parseResult)
{
result = 0;
int x;
- bool retValue = StringToInt(str, parsePos, requiredLength, flags, out x, ref parseResult);
+ bool retValue = StringToInt(str, ref parsePos, requiredLength, flags, out x, ref parseResult);
result = (short)x;
return retValue;
}
- private static unsafe bool StringToInt(String str, int requiredLength, int flags, out int result, ref GuidResult parseResult)
+ private static bool StringToInt(string str, int requiredLength, int flags, out int result, ref GuidResult parseResult)
{
- return StringToInt(str, null, requiredLength, flags, out result, ref parseResult);
- }
- private static unsafe bool StringToInt(String str, ref int parsePos, int requiredLength, int flags, out int result, ref GuidResult parseResult)
- {
- fixed (int* ppos = &parsePos)
- {
- return StringToInt(str, ppos, requiredLength, flags, out result, ref parseResult);
- }
+ int parsePos = 0;
+ return StringToInt(str, ref parsePos, requiredLength, flags, out result, ref parseResult);
}
- private static unsafe bool StringToInt(String str, int* parsePos, int requiredLength, int flags, out int result, ref GuidResult parseResult)
+
+ private static bool StringToInt(string str, ref int parsePos, int requiredLength, int flags, out int result, ref GuidResult parseResult)
{
result = 0;
- int currStart = (parsePos == null) ? 0 : (*parsePos);
+ int currStart = parsePos;
try
{
- result = ParseNumbers.StringToInt(str, 16, flags, parsePos);
+ result = ParseNumbers.StringToInt(str, 16, flags, ref parsePos);
}
catch (OverflowException ex)
{
- if (parseResult.throwStyle == GuidParseThrowStyle.All)
+ if (parseResult._throwStyle == GuidParseThrowStyle.All)
{
throw;
}
- else if (parseResult.throwStyle == GuidParseThrowStyle.AllButOverflow)
+ else if (parseResult._throwStyle == GuidParseThrowStyle.AllButOverflow)
{
throw new FormatException(SR.Format_GuidUnrecognized, ex);
}
@@ -894,7 +878,7 @@ namespace System
}
catch (Exception ex)
{
- if (parseResult.throwStyle == GuidParseThrowStyle.None)
+ if (parseResult._throwStyle == GuidParseThrowStyle.None)
{
parseResult.SetFailure(ex);
return false;
@@ -906,35 +890,29 @@ namespace System
}
//If we didn't parse enough characters, there's clearly an error.
- if (requiredLength != -1 && parsePos != null && (*parsePos) - currStart != requiredLength)
+ if (requiredLength != -1 && parsePos - currStart != requiredLength)
{
- parseResult.SetFailure(ParseFailureKind.Format, "Format_GuidInvalidChar");
+ parseResult.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvalidChar));
return false;
}
return true;
}
- private static unsafe bool StringToLong(String str, ref int parsePos, int flags, out long result, ref GuidResult parseResult)
- {
- fixed (int* ppos = &parsePos)
- {
- return StringToLong(str, ppos, flags, out result, ref parseResult);
- }
- }
- private static unsafe bool StringToLong(String str, int* parsePos, int flags, out long result, ref GuidResult parseResult)
+
+ private static unsafe bool StringToLong(string str, ref int parsePos, int flags, out long result, ref GuidResult parseResult)
{
result = 0;
try
{
- result = ParseNumbers.StringToLong(str, 16, flags, parsePos);
+ result = ParseNumbers.StringToLong(str, 16, flags, ref parsePos);
}
catch (OverflowException ex)
{
- if (parseResult.throwStyle == GuidParseThrowStyle.All)
+ if (parseResult._throwStyle == GuidParseThrowStyle.All)
{
throw;
}
- else if (parseResult.throwStyle == GuidParseThrowStyle.AllButOverflow)
+ else if (parseResult._throwStyle == GuidParseThrowStyle.AllButOverflow)
{
throw new FormatException(SR.Format_GuidUnrecognized, ex);
}
@@ -946,7 +924,7 @@ namespace System
}
catch (Exception ex)
{
- if (parseResult.throwStyle == GuidParseThrowStyle.None)
+ if (parseResult._throwStyle == GuidParseThrowStyle.None)
{
parseResult.SetFailure(ex);
return false;
@@ -959,8 +937,7 @@ namespace System
return true;
}
-
- private static String EatAllWhitespace(String str)
+ private static string EatAllWhitespace(string str)
{
int newLength = 0;
char[] chArr = new char[str.Length];
@@ -970,67 +947,78 @@ namespace System
for (int i = 0; i < str.Length; i++)
{
curChar = str[i];
- if (!Char.IsWhiteSpace(curChar))
+ if (!char.IsWhiteSpace(curChar))
{
chArr[newLength++] = curChar;
}
}
// Return a new string based on chArr
- return new String(chArr, 0, newLength);
+ return new string(chArr, 0, newLength);
}
- private static bool IsHexPrefix(String str, int i)
+ private static bool IsHexPrefix(string str, int i)
{
- if (str.Length > i + 1 && str[i] == '0' && (Char.ToLowerInvariant(str[i + 1]) == 'x'))
+ if (str.Length > i + 1 && str[i] == '0' && (char.ToLowerInvariant(str[i + 1]) == 'x'))
return true;
else
return false;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void WriteByteHelper(Span<byte> destination)
+ {
+ destination[0] = (byte)(_a);
+ destination[1] = (byte)(_a >> 8);
+ destination[2] = (byte)(_a >> 16);
+ destination[3] = (byte)(_a >> 24);
+ destination[4] = (byte)(_b);
+ destination[5] = (byte)(_b >> 8);
+ destination[6] = (byte)(_c);
+ destination[7] = (byte)(_c >> 8);
+ destination[8] = _d;
+ destination[9] = _e;
+ destination[10] = _f;
+ destination[11] = _g;
+ destination[12] = _h;
+ destination[13] = _i;
+ destination[14] = _j;
+ destination[15] = _k;
+ }
// Returns an unsigned byte array containing the GUID.
public byte[] ToByteArray()
{
- byte[] g = new byte[16];
-
- g[0] = (byte)(_a);
- g[1] = (byte)(_a >> 8);
- g[2] = (byte)(_a >> 16);
- g[3] = (byte)(_a >> 24);
- g[4] = (byte)(_b);
- g[5] = (byte)(_b >> 8);
- g[6] = (byte)(_c);
- g[7] = (byte)(_c >> 8);
- g[8] = _d;
- g[9] = _e;
- g[10] = _f;
- g[11] = _g;
- g[12] = _h;
- g[13] = _i;
- g[14] = _j;
- g[15] = _k;
-
+ var g = new byte[16];
+ WriteByteHelper(g);
return g;
}
+ // Returns whether bytes are sucessfully written to given span.
+ public bool TryWriteBytes(Span<byte> destination)
+ {
+ if (destination.Length < 16)
+ return false;
+
+ WriteByteHelper(destination);
+ return true;
+ }
// Returns the guid in "registry" format.
- public override String ToString()
+ public override string ToString()
{
return ToString("D", null);
}
- public unsafe override int GetHashCode()
+ public override int GetHashCode()
{
// Simply XOR all the bits of the GUID 32 bits at a time.
- fixed (int* ptr = &_a)
- return ptr[0] ^ ptr[1] ^ ptr[2] ^ ptr[3];
+ return _a ^ Unsafe.Add(ref _a, 1) ^ Unsafe.Add(ref _a, 2) ^ Unsafe.Add(ref _a, 3);
}
// Returns true if and only if the guid represented
// by o is the same as this instance.
- public override bool Equals(Object o)
+ public override bool Equals(object o)
{
Guid g;
// Check that o is a Guid first
@@ -1039,59 +1027,19 @@ namespace System
else g = (Guid)o;
// Now compare each of the elements
- if (g._a != _a)
- return false;
- if (g._b != _b)
- return false;
- if (g._c != _c)
- return false;
- if (g._d != _d)
- return false;
- if (g._e != _e)
- return false;
- if (g._f != _f)
- return false;
- if (g._g != _g)
- return false;
- if (g._h != _h)
- return false;
- if (g._i != _i)
- return false;
- if (g._j != _j)
- return false;
- if (g._k != _k)
- return false;
-
- return true;
+ return g._a == _a &&
+ Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) &&
+ Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) &&
+ Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3);
}
public bool Equals(Guid g)
{
// Now compare each of the elements
- if (g._a != _a)
- return false;
- if (g._b != _b)
- return false;
- if (g._c != _c)
- return false;
- if (g._d != _d)
- return false;
- if (g._e != _e)
- return false;
- if (g._f != _f)
- return false;
- if (g._g != _g)
- return false;
- if (g._h != _h)
- return false;
- if (g._i != _i)
- return false;
- if (g._j != _j)
- return false;
- if (g._k != _k)
- return false;
-
- return true;
+ return g._a == _a &&
+ Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) &&
+ Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) &&
+ Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3);
}
private int GetResult(uint me, uint them)
@@ -1103,7 +1051,7 @@ namespace System
return 1;
}
- public int CompareTo(Object value)
+ public int CompareTo(object value)
{
if (value == null)
{
@@ -1132,42 +1080,42 @@ namespace System
if (g._d != _d)
{
- return GetResult((uint)_d, (uint)g._d);
+ return GetResult(_d, g._d);
}
if (g._e != _e)
{
- return GetResult((uint)_e, (uint)g._e);
+ return GetResult(_e, g._e);
}
if (g._f != _f)
{
- return GetResult((uint)_f, (uint)g._f);
+ return GetResult(_f, g._f);
}
if (g._g != _g)
{
- return GetResult((uint)_g, (uint)g._g);
+ return GetResult(_g, g._g);
}
if (g._h != _h)
{
- return GetResult((uint)_h, (uint)g._h);
+ return GetResult(_h, g._h);
}
if (g._i != _i)
{
- return GetResult((uint)_i, (uint)g._i);
+ return GetResult(_i, g._i);
}
if (g._j != _j)
{
- return GetResult((uint)_j, (uint)g._j);
+ return GetResult(_j, g._j);
}
if (g._k != _k)
{
- return GetResult((uint)_k, (uint)g._k);
+ return GetResult(_k, g._k);
}
return 0;
@@ -1192,42 +1140,42 @@ namespace System
if (value._d != _d)
{
- return GetResult((uint)_d, (uint)value._d);
+ return GetResult(_d, value._d);
}
if (value._e != _e)
{
- return GetResult((uint)_e, (uint)value._e);
+ return GetResult(_e, value._e);
}
if (value._f != _f)
{
- return GetResult((uint)_f, (uint)value._f);
+ return GetResult(_f, value._f);
}
if (value._g != _g)
{
- return GetResult((uint)_g, (uint)value._g);
+ return GetResult(_g, value._g);
}
if (value._h != _h)
{
- return GetResult((uint)_h, (uint)value._h);
+ return GetResult(_h, value._h);
}
if (value._i != _i)
{
- return GetResult((uint)_i, (uint)value._i);
+ return GetResult(_i, value._i);
}
if (value._j != _j)
{
- return GetResult((uint)_j, (uint)value._j);
+ return GetResult(_j, value._j);
}
if (value._k != _k)
{
- return GetResult((uint)_k, (uint)value._k);
+ return GetResult(_k, value._k);
}
return 0;
@@ -1236,52 +1184,22 @@ namespace System
public static bool operator ==(Guid a, Guid b)
{
// Now compare each of the elements
- if (a._a != b._a)
- return false;
- if (a._b != b._b)
- return false;
- if (a._c != b._c)
- return false;
- if (a._d != b._d)
- return false;
- if (a._e != b._e)
- return false;
- if (a._f != b._f)
- return false;
- if (a._g != b._g)
- return false;
- if (a._h != b._h)
- return false;
- if (a._i != b._i)
- return false;
- if (a._j != b._j)
- return false;
- if (a._k != b._k)
- return false;
-
- return true;
+ return a._a == b._a &&
+ Unsafe.Add(ref a._a, 1) == Unsafe.Add(ref b._a, 1) &&
+ Unsafe.Add(ref a._a, 2) == Unsafe.Add(ref b._a, 2) &&
+ Unsafe.Add(ref a._a, 3) == Unsafe.Add(ref b._a, 3);
}
public static bool operator !=(Guid a, Guid b)
{
- return !(a == b);
- }
-
- // This will create a new guid. Since we've now decided that constructors should 0-init,
- // we need a method that allows users to create a guid.
- public static Guid NewGuid()
- {
- // CoCreateGuid should never return Guid.Empty, since it attempts to maintain some
- // uniqueness guarantees. It should also never return a known GUID, but it's unclear
- // how extensively it checks for known values.
- Contract.Ensures(Contract.Result<Guid>() != Guid.Empty);
-
- Guid guid;
- Marshal.ThrowExceptionForHR(Win32Native.CoCreateGuid(out guid), new IntPtr(-1));
- return guid;
+ // Now compare each of the elements
+ return a._a != b._a ||
+ Unsafe.Add(ref a._a, 1) != Unsafe.Add(ref b._a, 1) ||
+ Unsafe.Add(ref a._a, 2) != Unsafe.Add(ref b._a, 2) ||
+ Unsafe.Add(ref a._a, 3) != Unsafe.Add(ref b._a, 3);
}
- public String ToString(String format)
+ public string ToString(string format)
{
return ToString(format, null);
}
@@ -1324,124 +1242,168 @@ namespace System
// IFormattable interface
// We currently ignore provider
- public String ToString(String format, IFormatProvider provider)
+ public string ToString(string format, IFormatProvider provider)
{
if (format == null || format.Length == 0)
format = "D";
- string guidString;
- int offset = 0;
- bool dash = true;
- bool hex = false;
-
+ // all acceptable format strings are of length 1
if (format.Length != 1)
- {
- // all acceptable format strings are of length 1
throw new FormatException(SR.Format_InvalidGuidFormatSpecification);
- }
- char formatCh = format[0];
- if (formatCh == 'D' || formatCh == 'd')
- {
- guidString = string.FastAllocateString(36);
- }
- else if (formatCh == 'N' || formatCh == 'n')
- {
- guidString = string.FastAllocateString(32);
- dash = false;
- }
- else if (formatCh == 'B' || formatCh == 'b')
- {
- guidString = string.FastAllocateString(38);
- unsafe
- {
- fixed (char* guidChars = guidString)
- {
- guidChars[offset++] = '{';
- guidChars[37] = '}';
- }
- }
- }
- else if (formatCh == 'P' || formatCh == 'p')
- {
- guidString = string.FastAllocateString(38);
- unsafe
- {
- fixed (char* guidChars = guidString)
- {
- guidChars[offset++] = '(';
- guidChars[37] = ')';
- }
- }
- }
- else if (formatCh == 'X' || formatCh == 'x')
- {
- guidString = string.FastAllocateString(68);
- unsafe
- {
- fixed (char* guidChars = guidString)
- {
- guidChars[offset++] = '{';
- guidChars[67] = '}';
- }
- }
- dash = false;
- hex = true;
- }
- else
- {
+ int guidSize;
+ switch (format[0])
+ {
+ case 'D':
+ case 'd':
+ guidSize = 36;
+ break;
+ case 'N':
+ case 'n':
+ guidSize = 32;
+ break;
+ case 'B':
+ case 'b':
+ case 'P':
+ case 'p':
+ guidSize = 38;
+ break;
+ case 'X':
+ case 'x':
+ guidSize = 68;
+ break;
+ default:
+ throw new FormatException(SR.Format_InvalidGuidFormatSpecification);
+ }
+
+ string guidString = string.FastAllocateString(guidSize);
+
+ int bytesWritten;
+ bool result = TryFormat(new Span<char>(ref guidString.GetRawStringData(), guidString.Length), out bytesWritten, format);
+ Debug.Assert(result && bytesWritten == guidString.Length, "Formatting guid should have succeeded.");
+
+ return guidString;
+ }
+
+ // Returns whether the guid is successfully formatted as a span.
+ public bool TryFormat(Span<char> destination, out int charsWritten, string format = null)
+ {
+ if (format == null || format.Length == 0)
+ format = "D";
+
+ // all acceptable format strings are of length 1
+ if (format.Length != 1)
throw new FormatException(SR.Format_InvalidGuidFormatSpecification);
+
+ bool dash = true;
+ bool hex = false;
+ int braces = 0;
+
+ int guidSize;
+
+ switch (format[0])
+ {
+ case 'D':
+ case 'd':
+ guidSize = 36;
+ break;
+ case 'N':
+ case 'n':
+ dash = false;
+ guidSize = 32;
+ break;
+ case 'B':
+ case 'b':
+ braces = '{' + ('}' << 16);
+ guidSize = 38;
+ break;
+ case 'P':
+ case 'p':
+ braces = '(' + (')' << 16);
+ guidSize = 38;
+ break;
+ case 'X':
+ case 'x':
+ braces = '{' + ('}' << 16);
+ dash = false;
+ hex = true;
+ guidSize = 68;
+ break;
+ default:
+ throw new FormatException(SR.Format_InvalidGuidFormatSpecification);
+ }
+
+ if (destination.Length < guidSize)
+ {
+ charsWritten = 0;
+ return false;
}
unsafe
{
- fixed (char* guidChars = guidString)
+ fixed (char* guidChars = &destination.DangerousGetPinnableReference())
{
+ char * p = guidChars;
+
+ if (braces != 0)
+ *p++ = (char)braces;
+
if (hex)
{
// {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}}
- guidChars[offset++] = '0';
- guidChars[offset++] = 'x';
- offset += HexsToChars(guidChars + offset, _a >> 24, _a >> 16);
- offset += HexsToChars(guidChars + offset, _a >> 8, _a);
- guidChars[offset++] = ',';
- guidChars[offset++] = '0';
- guidChars[offset++] = 'x';
- offset += HexsToChars(guidChars + offset, _b >> 8, _b);
- guidChars[offset++] = ',';
- guidChars[offset++] = '0';
- guidChars[offset++] = 'x';
- offset += HexsToChars(guidChars + offset, _c >> 8, _c);
- guidChars[offset++] = ',';
- guidChars[offset++] = '{';
- offset += HexsToCharsHexOutput(guidChars + offset, _d, _e);
- guidChars[offset++] = ',';
- offset += HexsToCharsHexOutput(guidChars + offset, _f, _g);
- guidChars[offset++] = ',';
- offset += HexsToCharsHexOutput(guidChars + offset, _h, _i);
- guidChars[offset++] = ',';
- offset += HexsToCharsHexOutput(guidChars + offset, _j, _k);
- guidChars[offset++] = '}';
+ *p++ = '0';
+ *p++ = 'x';
+ p += HexsToChars(p, _a >> 24, _a >> 16);
+ p += HexsToChars(p, _a >> 8, _a);
+ *p++ = ',';
+ *p++ = '0';
+ *p++ = 'x';
+ p += HexsToChars(p, _b >> 8, _b);
+ *p++ = ',';
+ *p++ = '0';
+ *p++ = 'x';
+ p += HexsToChars(p, _c >> 8, _c);
+ *p++ = ',';
+ *p++ = '{';
+ p += HexsToCharsHexOutput(p, _d, _e);
+ *p++ = ',';
+ p += HexsToCharsHexOutput(p, _f, _g);
+ *p++ = ',';
+ p += HexsToCharsHexOutput(p, _h, _i);
+ *p++ = ',';
+ p += HexsToCharsHexOutput(p, _j, _k);
+ *p++ = '}';
}
else
{
// [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)]
- offset += HexsToChars(guidChars + offset, _a >> 24, _a >> 16);
- offset += HexsToChars(guidChars + offset, _a >> 8, _a);
- if (dash) guidChars[offset++] = '-';
- offset += HexsToChars(guidChars + offset, _b >> 8, _b);
- if (dash) guidChars[offset++] = '-';
- offset += HexsToChars(guidChars + offset, _c >> 8, _c);
- if (dash) guidChars[offset++] = '-';
- offset += HexsToChars(guidChars + offset, _d, _e);
- if (dash) guidChars[offset++] = '-';
- offset += HexsToChars(guidChars + offset, _f, _g);
- offset += HexsToChars(guidChars + offset, _h, _i);
- offset += HexsToChars(guidChars + offset, _j, _k);
+ p += HexsToChars(p, _a >> 24, _a >> 16);
+ p += HexsToChars(p, _a >> 8, _a);
+ if (dash)
+ *p++ = '-';
+ p += HexsToChars(p, _b >> 8, _b);
+ if (dash)
+ *p++ = '-';
+ p += HexsToChars(p, _c >> 8, _c);
+ if (dash)
+ *p++ = '-';
+ p += HexsToChars(p, _d, _e);
+ if (dash)
+ *p++ = '-';
+ p += HexsToChars(p, _f, _g);
+ p += HexsToChars(p, _h, _i);
+ p += HexsToChars(p, _j, _k);
}
+
+ if (braces != 0)
+ *p++ = (char)(braces >> 16);
+
+ Debug.Assert(p - guidChars == guidSize);
}
}
- return guidString;
+
+ charsWritten = guidSize;
+ return true;
}
}
}
diff --git a/src/mscorlib/src/System/__HResults.cs b/src/mscorlib/shared/System/HResults.cs
index 0592d814ef..ffc47d85bf 100644
--- a/src/mscorlib/src/System/__HResults.cs
+++ b/src/mscorlib/shared/System/HResults.cs
@@ -15,27 +15,17 @@
// HResults. Also note that some of our HResults have to map to certain
// COM HR's, etc.
-// Another arbitrary decision... Feel free to change this, as long as you
-// renumber the HResults yourself (and update rexcep.h).
// Reflection will use 0x1600 -> 0x161f. IO will use 0x1620 -> 0x163f.
// Security will use 0x1640 -> 0x165f
-// There are __HResults files in the IO, Remoting, Reflection &
-// Security/Util directories as well, so choose your HResults carefully.
using System;
namespace System
{
- internal static class __HResults
+ internal static partial class HResults
{
- internal const int RO_E_CLOSED = unchecked((int)0x80000013);
- internal const int E_BOUNDS = unchecked((int)0x8000000B);
- internal const int E_CHANGED_STATE = unchecked((int)0x8000000C);
- internal const int E_FAIL = unchecked((int)0x80004005);
- internal const int E_POINTER = unchecked((int)0x80004003);
- internal const int E_NOTIMPL = unchecked((int)0x80004001);
- internal const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154);
+ internal const int COR_E_ABANDONEDMUTEX = unchecked((int)0x8013152D);
internal const int COR_E_AMBIGUOUSMATCH = unchecked((int)0x8000211D);
internal const int COR_E_APPDOMAINUNLOADED = unchecked((int)0x80131014);
internal const int COR_E_APPLICATION = unchecked((int)0x80131600);
@@ -43,30 +33,36 @@ namespace System
internal const int COR_E_ARGUMENTOUTOFRANGE = unchecked((int)0x80131502);
internal const int COR_E_ARITHMETIC = unchecked((int)0x80070216);
internal const int COR_E_ARRAYTYPEMISMATCH = unchecked((int)0x80131503);
- internal const int COR_E_BADIMAGEFORMAT = unchecked((int)0x8007000B);
internal const int COR_E_BADEXEFORMAT = unchecked((int)0x800700C1);
- internal const int COR_E_TYPEUNLOADED = unchecked((int)0x80131013);
+ internal const int COR_E_BADIMAGEFORMAT = unchecked((int)0x8007000B);
internal const int COR_E_CANNOTUNLOADAPPDOMAIN = unchecked((int)0x80131015);
internal const int COR_E_COMEMULATE = unchecked((int)0x80131535);
internal const int COR_E_CONTEXTMARSHAL = unchecked((int)0x80131504);
- internal const int COR_E_DATAMISALIGNED = unchecked((int)0x80131541);
- internal const int COR_E_TIMEOUT = unchecked((int)0x80131505);
internal const int COR_E_CUSTOMATTRIBUTEFORMAT = unchecked((int)0x80131605);
+ internal const int COR_E_DATAMISALIGNED = unchecked((int)0x80131541);
+ internal const int COR_E_DIRECTORYNOTFOUND = unchecked((int)0x80070003);
internal const int COR_E_DIVIDEBYZERO = unchecked((int)0x80020012); // DISP_E_DIVBYZERO
+ internal const int COR_E_DLLNOTFOUND = unchecked((int)0x80131524);
internal const int COR_E_DUPLICATEWAITOBJECT = unchecked((int)0x80131529);
+ internal const int COR_E_ENDOFSTREAM = unchecked((int)0x80070026); // OS defined
+ internal const int COR_E_ENTRYPOINTNOTFOUND = unchecked((int)0x80131523);
internal const int COR_E_EXCEPTION = unchecked((int)0x80131500);
internal const int COR_E_EXECUTIONENGINE = unchecked((int)0x80131506);
internal const int COR_E_FIELDACCESS = unchecked((int)0x80131507);
+ internal const int COR_E_FILELOAD = unchecked((int)0x80131621);
+ internal const int COR_E_FILENOTFOUND = unchecked((int)0x80070002);
internal const int COR_E_FORMAT = unchecked((int)0x80131537);
+ internal const int COR_E_HOSTPROTECTION = unchecked((int)0x80131640);
internal const int COR_E_INDEXOUTOFRANGE = unchecked((int)0x80131508);
- internal const int COR_E_INSUFFICIENTMEMORY = unchecked((int)0x8013153D);
internal const int COR_E_INSUFFICIENTEXECUTIONSTACK = unchecked((int)0x80131578);
+ internal const int COR_E_INSUFFICIENTMEMORY = unchecked((int)0x8013153D);
internal const int COR_E_INVALIDCAST = unchecked((int)0x80004002);
internal const int COR_E_INVALIDCOMOBJECT = unchecked((int)0x80131527);
internal const int COR_E_INVALIDFILTERCRITERIA = unchecked((int)0x80131601);
internal const int COR_E_INVALIDOLEVARIANTTYPE = unchecked((int)0x80131531);
internal const int COR_E_INVALIDOPERATION = unchecked((int)0x80131509);
internal const int COR_E_INVALIDPROGRAM = unchecked((int)0x8013153A);
+ internal const int COR_E_IO = unchecked((int)0x80131620);
internal const int COR_E_KEYNOTFOUND = unchecked((int)0x80131577);
internal const int COR_E_MARSHALDIRECTIVE = unchecked((int)0x80131535);
internal const int COR_E_MEMBERACCESS = unchecked((int)0x8013151A);
@@ -78,13 +74,14 @@ namespace System
internal const int COR_E_MISSINGSATELLITEASSEMBLY = unchecked((int)0x80131536);
internal const int COR_E_MULTICASTNOTSUPPORTED = unchecked((int)0x80131514);
internal const int COR_E_NOTFINITENUMBER = unchecked((int)0x80131528);
- internal const int COR_E_PLATFORMNOTSUPPORTED = unchecked((int)0x80131539);
internal const int COR_E_NOTSUPPORTED = unchecked((int)0x80131515);
internal const int COR_E_NULLREFERENCE = unchecked((int)0x80004003);
internal const int COR_E_OBJECTDISPOSED = unchecked((int)0x80131622);
internal const int COR_E_OPERATIONCANCELED = unchecked((int)0x8013153B);
internal const int COR_E_OUTOFMEMORY = unchecked((int)0x8007000E);
internal const int COR_E_OVERFLOW = unchecked((int)0x80131516);
+ internal const int COR_E_PATHTOOLONG = unchecked((int)0x800700CE);
+ internal const int COR_E_PLATFORMNOTSUPPORTED = unchecked((int)0x80131539);
internal const int COR_E_RANK = unchecked((int)0x80131517);
internal const int COR_E_REFLECTIONTYPELOAD = unchecked((int)0x80131602);
internal const int COR_E_RUNTIMEWRAPPED = unchecked((int)0x8013153E);
@@ -92,40 +89,38 @@ namespace System
internal const int COR_E_SAFEARRAYTYPEMISMATCH = unchecked((int)0x80131533);
internal const int COR_E_SAFEHANDLEMISSINGATTRIBUTE = unchecked((int)0x80131623);
internal const int COR_E_SECURITY = unchecked((int)0x8013150A);
- internal const int COR_E_SERIALIZATION = unchecked((int)0x8013150C);
internal const int COR_E_SEMAPHOREFULL = unchecked((int)0x8013152B);
- internal const int COR_E_WAITHANDLECANNOTBEOPENED = unchecked((int)0x8013152C);
- internal const int COR_E_ABANDONEDMUTEX = unchecked((int)0x8013152D);
+ internal const int COR_E_SERIALIZATION = unchecked((int)0x8013150C);
internal const int COR_E_STACKOVERFLOW = unchecked((int)0x800703E9);
internal const int COR_E_SYNCHRONIZATIONLOCK = unchecked((int)0x80131518);
internal const int COR_E_SYSTEM = unchecked((int)0x80131501);
internal const int COR_E_TARGET = unchecked((int)0x80131603);
internal const int COR_E_TARGETINVOCATION = unchecked((int)0x80131604);
- internal const int COR_E_TARGETPARAMCOUNT = unchecked((int)0x8002000e);
+ internal const int COR_E_TARGETPARAMCOUNT = unchecked((int)0x8002000E);
internal const int COR_E_THREADABORTED = unchecked((int)0x80131530);
internal const int COR_E_THREADINTERRUPTED = unchecked((int)0x80131519);
+ internal const int COR_E_THREADSTART = unchecked((int)0x80131525);
internal const int COR_E_THREADSTATE = unchecked((int)0x80131520);
internal const int COR_E_THREADSTOP = unchecked((int)0x80131521);
- internal const int COR_E_THREADSTART = unchecked((int)0x80131525);
+ internal const int COR_E_TIMEOUT = unchecked((int)0x80131505);
internal const int COR_E_TYPEACCESS = unchecked((int)0x80131543);
internal const int COR_E_TYPEINITIALIZATION = unchecked((int)0x80131534);
internal const int COR_E_TYPELOAD = unchecked((int)0x80131522);
- internal const int COR_E_ENTRYPOINTNOTFOUND = unchecked((int)0x80131523);
- internal const int COR_E_DLLNOTFOUND = unchecked((int)0x80131524);
+ internal const int COR_E_TYPEUNLOADED = unchecked((int)0x80131013);
internal const int COR_E_UNAUTHORIZEDACCESS = unchecked((int)0x80070005);
internal const int COR_E_UNSUPPORTEDFORMAT = unchecked((int)0x80131523);
internal const int COR_E_VERIFICATION = unchecked((int)0x8013150D);
- internal const int COR_E_HOSTPROTECTION = unchecked((int)0x80131640);
- internal const int CORSEC_E_MIN_GRANT_FAIL = unchecked((int)0x80131417);
- internal const int CORSEC_E_NO_EXEC_PERM = unchecked((int)0x80131418);
- internal const int CORSEC_E_POLICY_EXCEPTION = unchecked((int)0x80131416);
- internal const int CORSEC_E_XMLSYNTAX = unchecked((int)0x80131418);
- internal const int NTE_FAIL = unchecked((int)0x80090020);
- internal const int CORSEC_E_CRYPTO = unchecked((int)0x80131430);
- internal const int CORSEC_E_CRYPTO_UNEX_OPER = unchecked((int)0x80131431);
- internal const int DISP_E_OVERFLOW = unchecked((int)0x8002000a);
- internal const int FUSION_E_REF_DEF_MISMATCH = unchecked((int)0x80131040);
- internal const int FUSION_E_INVALID_NAME = unchecked((int)0x80131047);
- internal const int TYPE_E_TYPEMISMATCH = unchecked((int)0x80028ca0);
+ internal const int COR_E_WAITHANDLECANNOTBEOPENED = unchecked((int)0x8013152C);
+ internal const int DISP_E_OVERFLOW = unchecked((int)0x8002000A);
+ internal const int E_BOUNDS = unchecked((int)0x8000000B);
+ internal const int E_CHANGED_STATE = unchecked((int)0x8000000C);
+ internal const int E_FAIL = unchecked((int)0x80004005);
+ internal const int E_HANDLE = unchecked((int)0x80070006);
+ internal const int E_INVALIDARG = unchecked((int)0x80070057);
+ internal const int E_NOTIMPL = unchecked((int)0x80004001);
+ internal const int E_POINTER = unchecked((int)0x80004003);
+ internal const int ERROR_MRM_MAP_NOT_FOUND = unchecked((int)0x80073B1F);
+ internal const int RO_E_CLOSED = unchecked((int)0x80000013);
+ internal const int TYPE_E_TYPEMISMATCH = unchecked((int)0x80028CA0);
}
}
diff --git a/src/mscorlib/shared/System/IO/BinaryWriter.cs b/src/mscorlib/shared/System/IO/BinaryWriter.cs
index 90b920854f..675e80922e 100644
--- a/src/mscorlib/shared/System/IO/BinaryWriter.cs
+++ b/src/mscorlib/shared/System/IO/BinaryWriter.cs
@@ -4,6 +4,7 @@
using System.Text;
using System.Diagnostics;
+using System.Buffers;
namespace System.IO
{
@@ -389,6 +390,41 @@ namespace System.IO
}
}
+ public virtual void Write(ReadOnlySpan<byte> value)
+ {
+ if (GetType() == typeof(BinaryWriter))
+ {
+ OutStream.Write(value);
+ }
+ else
+ {
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(value.Length);
+ try
+ {
+ value.CopyTo(buffer);
+ Write(buffer, 0, value.Length);
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(buffer);
+ }
+ }
+ }
+
+ public virtual void Write(ReadOnlySpan<char> value)
+ {
+ byte[] bytes = ArrayPool<byte>.Shared.Rent(_encoding.GetMaxByteCount(value.Length));
+ try
+ {
+ int bytesWritten = _encoding.GetBytes(value, bytes);
+ Write(bytes, 0, bytesWritten);
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(bytes);
+ }
+ }
+
protected void Write7BitEncodedInt(int value)
{
// Write out an int 7 bits at a time. The high bit of the byte,
diff --git a/src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs b/src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs
index de09760fcb..87e610b86e 100644
--- a/src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs
+++ b/src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs
@@ -17,19 +17,19 @@ namespace System.IO
public DirectoryNotFoundException()
: base(SR.Arg_DirectoryNotFoundException)
{
- HResult = __HResults.COR_E_DIRECTORYNOTFOUND;
+ HResult = HResults.COR_E_DIRECTORYNOTFOUND;
}
public DirectoryNotFoundException(string message)
: base(message)
{
- HResult = __HResults.COR_E_DIRECTORYNOTFOUND;
+ HResult = HResults.COR_E_DIRECTORYNOTFOUND;
}
public DirectoryNotFoundException(string message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_DIRECTORYNOTFOUND;
+ HResult = HResults.COR_E_DIRECTORYNOTFOUND;
}
protected DirectoryNotFoundException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/IO/DisableMediaInsertionPrompt.cs b/src/mscorlib/shared/System/IO/DisableMediaInsertionPrompt.cs
new file mode 100644
index 0000000000..aa10e8d883
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/DisableMediaInsertionPrompt.cs
@@ -0,0 +1,36 @@
+// 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.IO
+{
+ /// <summary>
+ /// Simple wrapper to safely disable the normal media insertion prompt for
+ /// removable media (floppies, cds, memory cards, etc.)
+ /// </summary>
+ /// <remarks>
+ /// Note that removable media file systems lazily load. After starting the OS
+ /// they won't be loaded until you have media in the drive- and as such the
+ /// prompt won't happen. You have to have had media in at least once to get
+ /// the file system to load and then have removed it.
+ /// </remarks>
+ internal struct DisableMediaInsertionPrompt : IDisposable
+ {
+ private bool _disableSuccess;
+ private uint _oldMode;
+
+ public static DisableMediaInsertionPrompt Create()
+ {
+ DisableMediaInsertionPrompt prompt = new DisableMediaInsertionPrompt();
+ prompt._disableSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out prompt._oldMode);
+ return prompt;
+ }
+
+ public void Dispose()
+ {
+ uint ignore;
+ if (_disableSuccess)
+ Interop.Kernel32.SetThreadErrorMode(_oldMode, out ignore);
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/IO/DriveNotFoundException.cs b/src/mscorlib/shared/System/IO/DriveNotFoundException.cs
index 7e9b0c9f36..c0f8c55af8 100644
--- a/src/mscorlib/src/System/IO/DriveNotFoundException.cs
+++ b/src/mscorlib/shared/System/IO/DriveNotFoundException.cs
@@ -2,33 +2,29 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-//============================================================
-//
-//
-//
-// Purpose: Exception for accessing a drive that is not available.
-//
-//
-//============================================================
-
-using System;
using System.Runtime.Serialization;
namespace System.IO
{
- //Thrown when trying to access a drive that is not availabe.
+ //Thrown when trying to access a drive that is not available.
internal class DriveNotFoundException : IOException
{
public DriveNotFoundException()
: base(SR.Arg_DriveNotFoundException)
{
- HResult = __HResults.COR_E_DIRECTORYNOTFOUND;
+ HResult = HResults.COR_E_DIRECTORYNOTFOUND;
}
- public DriveNotFoundException(String message)
+ public DriveNotFoundException(string message)
: base(message)
{
- HResult = __HResults.COR_E_DIRECTORYNOTFOUND;
+ HResult = HResults.COR_E_DIRECTORYNOTFOUND;
+ }
+
+ public DriveNotFoundException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = HResults.COR_E_DIRECTORYNOTFOUND;
}
protected DriveNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/IO/EndOfStreamException.cs b/src/mscorlib/shared/System/IO/EndOfStreamException.cs
index 68e1b2c882..ee37818dc5 100644
--- a/src/mscorlib/shared/System/IO/EndOfStreamException.cs
+++ b/src/mscorlib/shared/System/IO/EndOfStreamException.cs
@@ -11,19 +11,19 @@ namespace System.IO
public EndOfStreamException()
: base(SR.Arg_EndOfStreamException)
{
- HResult = __HResults.COR_E_ENDOFSTREAM;
+ HResult = HResults.COR_E_ENDOFSTREAM;
}
public EndOfStreamException(string message)
: base(message)
{
- HResult = __HResults.COR_E_ENDOFSTREAM;
+ HResult = HResults.COR_E_ENDOFSTREAM;
}
public EndOfStreamException(string message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_ENDOFSTREAM;
+ HResult = HResults.COR_E_ENDOFSTREAM;
}
protected EndOfStreamException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/IO/FileLoadException.cs b/src/mscorlib/shared/System/IO/FileLoadException.cs
index 6fdf2d58cf..b126424c22 100644
--- a/src/mscorlib/shared/System/IO/FileLoadException.cs
+++ b/src/mscorlib/shared/System/IO/FileLoadException.cs
@@ -11,31 +11,31 @@ namespace System.IO
public FileLoadException()
: base(SR.IO_FileLoad)
{
- HResult = __HResults.COR_E_FILELOAD;
+ HResult = HResults.COR_E_FILELOAD;
}
public FileLoadException(string message)
: base(message)
{
- HResult = __HResults.COR_E_FILELOAD;
+ HResult = HResults.COR_E_FILELOAD;
}
public FileLoadException(string message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_FILELOAD;
+ HResult = HResults.COR_E_FILELOAD;
}
public FileLoadException(string message, string fileName) : base(message)
{
- HResult = __HResults.COR_E_FILELOAD;
+ HResult = HResults.COR_E_FILELOAD;
FileName = fileName;
}
public FileLoadException(string message, string fileName, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_FILELOAD;
+ HResult = HResults.COR_E_FILELOAD;
FileName = fileName;
}
diff --git a/src/mscorlib/shared/System/IO/FileNotFoundException.cs b/src/mscorlib/shared/System/IO/FileNotFoundException.cs
index 374c976055..dc1ccf577a 100644
--- a/src/mscorlib/shared/System/IO/FileNotFoundException.cs
+++ b/src/mscorlib/shared/System/IO/FileNotFoundException.cs
@@ -12,32 +12,32 @@ namespace System.IO
public FileNotFoundException()
: base(SR.IO_FileNotFound)
{
- HResult = __HResults.COR_E_FILENOTFOUND;
+ HResult = HResults.COR_E_FILENOTFOUND;
}
public FileNotFoundException(string message)
: base(message)
{
- HResult = __HResults.COR_E_FILENOTFOUND;
+ HResult = HResults.COR_E_FILENOTFOUND;
}
public FileNotFoundException(string message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_FILENOTFOUND;
+ HResult = HResults.COR_E_FILENOTFOUND;
}
public FileNotFoundException(string message, string fileName)
: base(message)
{
- HResult = __HResults.COR_E_FILENOTFOUND;
+ HResult = HResults.COR_E_FILENOTFOUND;
FileName = fileName;
}
public FileNotFoundException(string message, string fileName, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_FILENOTFOUND;
+ HResult = HResults.COR_E_FILENOTFOUND;
FileName = fileName;
}
@@ -55,7 +55,7 @@ namespace System.IO
if (_message == null)
{
if ((FileName == null) &&
- (HResult == System.__HResults.COR_E_EXCEPTION))
+ (HResult == System.HResults.COR_E_EXCEPTION))
_message = SR.IO_FileNotFound;
else if (FileName != null)
diff --git a/src/mscorlib/shared/System/IO/FileStream.Unix.cs b/src/mscorlib/shared/System/IO/FileStream.Unix.cs
index ad68a001cf..8499595361 100644
--- a/src/mscorlib/shared/System/IO/FileStream.Unix.cs
+++ b/src/mscorlib/shared/System/IO/FileStream.Unix.cs
@@ -286,13 +286,20 @@ namespace System.IO
}
}
+ private void FlushWriteBufferForWriteByte()
+ {
+ _asyncState?.Wait();
+ try { FlushWriteBuffer(); }
+ finally { _asyncState?.Release(); }
+ }
+
/// <summary>Writes any data in the write buffer to the underlying stream and resets the buffer.</summary>
private void FlushWriteBuffer()
{
AssertBufferInvariants();
if (_writePos > 0)
{
- WriteNative(GetBuffer(), 0, _writePos);
+ WriteNative(new ReadOnlySpan<byte>(GetBuffer(), 0, _writePos));
_writePos = 0;
}
}
@@ -375,44 +382,7 @@ namespace System.IO
}
/// <summary>Reads a block of bytes from the stream and writes the data in a given buffer.</summary>
- /// <param name="array">
- /// When this method returns, contains the specified byte array with the values between offset and
- /// (offset + count - 1) replaced by the bytes read from the current source.
- /// </param>
- /// <param name="offset">The byte offset in array at which the read bytes will be placed.</param>
- /// <param name="count">The maximum number of bytes to read. </param>
- /// <returns>
- /// The total number of bytes read into the buffer. This might be less than the number of bytes requested
- /// if that number of bytes are not currently available, or zero if the end of the stream is reached.
- /// </returns>
- public override int Read(byte[] array, int offset, int count)
- {
- ValidateReadWriteArgs(array, offset, count);
-
- if (_useAsyncIO)
- {
- _asyncState.Wait();
- try { return ReadCore(array, offset, count); }
- finally { _asyncState.Release(); }
- }
- else
- {
- return ReadCore(array, offset, count);
- }
- }
-
- /// <summary>Reads a block of bytes from the stream and writes the data in a given buffer.</summary>
- /// <param name="array">
- /// When this method returns, contains the specified byte array with the values between offset and
- /// (offset + count - 1) replaced by the bytes read from the current source.
- /// </param>
- /// <param name="offset">The byte offset in array at which the read bytes will be placed.</param>
- /// <param name="count">The maximum number of bytes to read. </param>
- /// <returns>
- /// The total number of bytes read into the buffer. This might be less than the number of bytes requested
- /// if that number of bytes are not currently available, or zero if the end of the stream is reached.
- /// </returns>
- private int ReadCore(byte[] array, int offset, int count)
+ private int ReadSpan(Span<byte> destination)
{
PrepareForReading();
@@ -429,16 +399,16 @@ namespace System.IO
// If we're not able to seek, then we're not able to rewind the stream (i.e. flushing
// a read buffer), in which case we don't want to use a read buffer. Similarly, if
// the user has asked for more data than we can buffer, we also want to skip the buffer.
- if (!CanSeek || (count >= _bufferLength))
+ if (!CanSeek || (destination.Length >= _bufferLength))
{
// Read directly into the user's buffer
_readPos = _readLength = 0;
- return ReadNative(array, offset, count);
+ return ReadNative(destination);
}
else
{
// Read into our buffer.
- _readLength = numBytesAvailable = ReadNative(GetBuffer(), 0, _bufferLength);
+ _readLength = numBytesAvailable = ReadNative(GetBuffer());
_readPos = 0;
if (numBytesAvailable == 0)
{
@@ -454,8 +424,8 @@ namespace System.IO
// Now that we know there's data in the buffer, read from it into the user's buffer.
Debug.Assert(numBytesAvailable > 0, "Data must be in the buffer to be here");
- int bytesRead = Math.Min(numBytesAvailable, count);
- Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, bytesRead);
+ int bytesRead = Math.Min(numBytesAvailable, destination.Length);
+ new Span<byte>(GetBuffer(), _readPos, bytesRead).CopyTo(destination);
_readPos += bytesRead;
// We may not have had enough data in the buffer to completely satisfy the user's request.
@@ -466,38 +436,33 @@ namespace System.IO
// behavior, we do the same thing here on Unix. Note that we may still get less the requested
// amount, as the OS may give us back fewer than we request, either due to reaching the end of
// file, or due to its own whims.
- if (!readFromOS && bytesRead < count)
+ if (!readFromOS && bytesRead < destination.Length)
{
- Debug.Assert(_readPos == _readLength, "bytesToRead should only be < count if numBytesAvailable < count");
+ Debug.Assert(_readPos == _readLength, "bytesToRead should only be < destination.Length if numBytesAvailable < destination.Length");
_readPos = _readLength = 0; // no data left in the read buffer
- bytesRead += ReadNative(array, offset + bytesRead, count - bytesRead);
+ bytesRead += ReadNative(destination.Slice(bytesRead));
}
return bytesRead;
}
- /// <summary>Unbuffered, reads a block of bytes from the stream and writes the data in a given buffer.</summary>
- /// <param name="array">
- /// When this method returns, contains the specified byte array with the values between offset and
- /// (offset + count - 1) replaced by the bytes read from the current source.
- /// </param>
- /// <param name="offset">The byte offset in array at which the read bytes will be placed.</param>
- /// <param name="count">The maximum number of bytes to read. </param>
+ /// <summary>Unbuffered, reads a block of bytes from the file handle into the given buffer.</summary>
+ /// <param name="buffer">The buffer into which data from the file is read.</param>
/// <returns>
/// The total number of bytes read into the buffer. This might be less than the number of bytes requested
/// if that number of bytes are not currently available, or zero if the end of the stream is reached.
/// </returns>
- private unsafe int ReadNative(byte[] array, int offset, int count)
+ private unsafe int ReadNative(Span<byte> buffer)
{
FlushWriteBuffer(); // we're about to read; dump the write buffer
VerifyOSHandlePosition();
int bytesRead;
- fixed (byte* bufPtr = array)
+ fixed (byte* bufPtr = &buffer.DangerousGetPinnableReference())
{
- bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr + offset, count));
- Debug.Assert(bytesRead <= count);
+ bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr, buffer.Length));
+ Debug.Assert(bytesRead <= buffer.Length);
}
_filePosition += bytesRead;
return bytesRead;
@@ -507,135 +472,97 @@ namespace System.IO
/// Asynchronously reads a sequence of bytes from the current stream and advances
/// the position within the stream by the number of bytes read.
/// </summary>
- /// <param name="buffer">The buffer to write the data into.</param>
- /// <param name="offset">The byte offset in buffer at which to begin writing data from the stream.</param>
- /// <param name="count">The maximum number of bytes to read.</param>
+ /// <param name="destination">The buffer to write the data into.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+ /// <param name="synchronousResult">If the operation completes synchronously, the number of bytes read.</param>
/// <returns>A task that represents the asynchronous read operation.</returns>
- private Task<int> ReadAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ private Task<int> ReadAsyncInternal(Memory<byte> destination, CancellationToken cancellationToken, out int synchronousResult)
{
- if (_useAsyncIO)
+ Debug.Assert(_useAsyncIO);
+
+ if (!CanRead) // match Windows behavior; this gets thrown synchronously
{
- if (!CanRead) // match Windows behavior; this gets thrown synchronously
- {
- throw Error.GetReadNotSupported();
- }
+ throw Error.GetReadNotSupported();
+ }
- // Serialize operations using the semaphore.
- Task waitTask = _asyncState.WaitAsync();
+ // Serialize operations using the semaphore.
+ Task waitTask = _asyncState.WaitAsync();
- // If we got ownership immediately, and if there's enough data in our buffer
- // to satisfy the full request of the caller, hand back the buffered data.
- // While it would be a legal implementation of the Read contract, we don't
- // hand back here less than the amount requested so as to match the behavior
- // in ReadCore that will make a native call to try to fulfill the remainder
- // of the request.
- if (waitTask.Status == TaskStatus.RanToCompletion)
+ // If we got ownership immediately, and if there's enough data in our buffer
+ // to satisfy the full request of the caller, hand back the buffered data.
+ // While it would be a legal implementation of the Read contract, we don't
+ // hand back here less than the amount requested so as to match the behavior
+ // in ReadCore that will make a native call to try to fulfill the remainder
+ // of the request.
+ if (waitTask.Status == TaskStatus.RanToCompletion)
+ {
+ int numBytesAvailable = _readLength - _readPos;
+ if (numBytesAvailable >= destination.Length)
{
- int numBytesAvailable = _readLength - _readPos;
- if (numBytesAvailable >= count)
+ try
{
- try
- {
- PrepareForReading();
-
- Buffer.BlockCopy(GetBuffer(), _readPos, buffer, offset, count);
- _readPos += count;
-
- return _asyncState._lastSuccessfulReadTask != null && _asyncState._lastSuccessfulReadTask.Result == count ?
- _asyncState._lastSuccessfulReadTask :
- (_asyncState._lastSuccessfulReadTask = Task.FromResult(count));
- }
- catch (Exception exc)
- {
- return Task.FromException<int>(exc);
- }
- finally
- {
- _asyncState.Release();
- }
- }
- }
+ PrepareForReading();
- // Otherwise, issue the whole request asynchronously.
- _asyncState.Update(buffer, offset, count);
- return waitTask.ContinueWith((t, s) =>
- {
- // The options available on Unix for writing asynchronously to an arbitrary file
- // handle typically amount to just using another thread to do the synchronous write,
- // which is exactly what this implementation does. This does mean there are subtle
- // differences in certain FileStream behaviors between Windows and Unix when multiple
- // asynchronous operations are issued against the stream to execute concurrently; on
- // Unix the operations will be serialized due to the usage of a semaphore, but the
- // position /length information won't be updated until after the write has completed,
- // whereas on Windows it may happen before the write has completed.
-
- Debug.Assert(t.Status == TaskStatus.RanToCompletion);
- var thisRef = (FileStream)s;
- try
+ new Span<byte>(GetBuffer(), _readPos, destination.Length).CopyTo(destination.Span);
+ _readPos += destination.Length;
+
+ synchronousResult = destination.Length;
+ return null;
+ }
+ catch (Exception exc)
{
- byte[] b = thisRef._asyncState._buffer;
- thisRef._asyncState._buffer = null; // remove reference to user's buffer
- return thisRef.ReadCore(b, thisRef._asyncState._offset, thisRef._asyncState._count);
+ synchronousResult = 0;
+ return Task.FromException<int>(exc);
}
- finally { thisRef._asyncState.Release(); }
- }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
- }
- else
- {
- return base.ReadAsync(buffer, offset, count, cancellationToken);
+ finally
+ {
+ _asyncState.Release();
+ }
+ }
}
- }
- /// <summary>
- /// Reads a byte from the stream and advances the position within the stream
- /// by one byte, or returns -1 if at the end of the stream.
- /// </summary>
- /// <returns>The unsigned byte cast to an Int32, or -1 if at the end of the stream.</returns>
- public override int ReadByte()
- {
- if (_useAsyncIO)
- {
- _asyncState.Wait();
- try { return ReadByteCore(); }
- finally { _asyncState.Release(); }
- }
- else
+ // Otherwise, issue the whole request asynchronously.
+ synchronousResult = 0;
+ _asyncState.Memory = destination;
+ return waitTask.ContinueWith((t, s) =>
{
- return ReadByteCore();
- }
+ // The options available on Unix for writing asynchronously to an arbitrary file
+ // handle typically amount to just using another thread to do the synchronous write,
+ // which is exactly what this implementation does. This does mean there are subtle
+ // differences in certain FileStream behaviors between Windows and Unix when multiple
+ // asynchronous operations are issued against the stream to execute concurrently; on
+ // Unix the operations will be serialized due to the usage of a semaphore, but the
+ // position /length information won't be updated until after the write has completed,
+ // whereas on Windows it may happen before the write has completed.
+
+ Debug.Assert(t.Status == TaskStatus.RanToCompletion);
+ var thisRef = (FileStream)s;
+ try
+ {
+ Memory<byte> memory = thisRef._asyncState.Memory;
+ thisRef._asyncState.Memory = default(Memory<byte>);
+ return thisRef.ReadSpan(memory.Span);
+ }
+ finally { thisRef._asyncState.Release(); }
+ }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
}
- /// <summary>Writes a block of bytes to the file stream.</summary>
- /// <param name="array">The buffer containing data to write to the stream.</param>
- /// <param name="offset">The zero-based byte offset in array from which to begin copying bytes to the stream.</param>
- /// <param name="count">The maximum number of bytes to write.</param>
- public override void Write(byte[] array, int offset, int count)
+ /// <summary>Reads from the file handle into the buffer, overwriting anything in it.</summary>
+ private int FillReadBufferForReadByte()
{
- ValidateReadWriteArgs(array, offset, count);
-
- if (_useAsyncIO)
- {
- _asyncState.Wait();
- try { WriteCore(array, offset, count); }
- finally { _asyncState.Release(); }
- }
- else
- {
- WriteCore(array, offset, count);
- }
+ _asyncState?.Wait();
+ try { return ReadNative(_buffer); }
+ finally { _asyncState?.Release(); }
}
/// <summary>Writes a block of bytes to the file stream.</summary>
- /// <param name="array">The buffer containing data to write to the stream.</param>
- /// <param name="offset">The zero-based byte offset in array from which to begin copying bytes to the stream.</param>
- /// <param name="count">The maximum number of bytes to write.</param>
- private void WriteCore(byte[] array, int offset, int count)
+ /// <param name="source">The buffer containing data to write to the stream.</param>
+ private void WriteSpan(ReadOnlySpan<byte> source)
{
PrepareForWriting();
// If no data is being written, nothing more to do.
- if (count == 0)
+ if (source.Length == 0)
{
return;
}
@@ -647,21 +574,17 @@ namespace System.IO
// If there's space remaining in the buffer, then copy as much as
// we can from the user's buffer into ours.
int spaceRemaining = _bufferLength - _writePos;
- if (spaceRemaining > 0)
+ if (spaceRemaining >= source.Length)
{
- int bytesToCopy = Math.Min(spaceRemaining, count);
- Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, bytesToCopy);
- _writePos += bytesToCopy;
-
- // If we've successfully copied all of the user's data, we're done.
- if (count == bytesToCopy)
- {
- return;
- }
-
- // Otherwise, keep track of how much more data needs to be handled.
- offset += bytesToCopy;
- count -= bytesToCopy;
+ source.CopyTo(new Span<byte>(GetBuffer()).Slice(_writePos));
+ _writePos += source.Length;
+ return;
+ }
+ else if (spaceRemaining > 0)
+ {
+ source.Slice(0, spaceRemaining).CopyTo(new Span<byte>(GetBuffer()).Slice(_writePos));
+ _writePos += spaceRemaining;
+ source = source.Slice(spaceRemaining);
}
// At this point, the buffer is full, so flush it out.
@@ -672,35 +595,33 @@ namespace System.IO
// the user's looking to write more data than we can store in the buffer),
// skip the buffer. Otherwise, put the remaining data into the buffer.
Debug.Assert(_writePos == 0);
- if (count >= _bufferLength)
+ if (source.Length >= _bufferLength)
{
- WriteNative(array, offset, count);
+ WriteNative(source);
}
else
{
- Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, count);
- _writePos = count;
+ source.CopyTo(new Span<byte>(GetBuffer()));
+ _writePos = source.Length;
}
}
/// <summary>Unbuffered, writes a block of bytes to the file stream.</summary>
- /// <param name="array">The buffer containing data to write to the stream.</param>
- /// <param name="offset">The zero-based byte offset in array from which to begin copying bytes to the stream.</param>
- /// <param name="count">The maximum number of bytes to write.</param>
- private unsafe void WriteNative(byte[] array, int offset, int count)
+ /// <param name="source">The buffer containing data to write to the stream.</param>
+ private unsafe void WriteNative(ReadOnlySpan<byte> source)
{
VerifyOSHandlePosition();
- fixed (byte* bufPtr = array)
+ fixed (byte* bufPtr = &source.DangerousGetPinnableReference())
{
+ int offset = 0;
+ int count = source.Length;
while (count > 0)
{
int bytesWritten = CheckFileCall(Interop.Sys.Write(_fileHandle, bufPtr + offset, count));
- Debug.Assert(bytesWritten <= count);
-
_filePosition += bytesWritten;
- count -= bytesWritten;
offset += bytesWritten;
+ count -= bytesWritten;
}
}
}
@@ -710,103 +631,77 @@ namespace System.IO
/// the current position within this stream by the number of bytes written, and
/// monitors cancellation requests.
/// </summary>
- /// <param name="buffer">The buffer to write data from.</param>
- /// <param name="offset">The zero-based byte offset in buffer from which to begin copying bytes to the stream.</param>
- /// <param name="count">The maximum number of bytes to write.</param>
+ /// <param name="source">The buffer to write data from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
- private Task WriteAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ private Task WriteAsyncInternal(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
{
+ Debug.Assert(_useAsyncIO);
+
if (cancellationToken.IsCancellationRequested)
return Task.FromCanceled(cancellationToken);
if (_fileHandle.IsClosed)
throw Error.GetFileNotOpen();
- if (_useAsyncIO)
+ if (!CanWrite) // match Windows behavior; this gets thrown synchronously
{
- if (!CanWrite) // match Windows behavior; this gets thrown synchronously
- {
- throw Error.GetWriteNotSupported();
- }
+ throw Error.GetWriteNotSupported();
+ }
- // Serialize operations using the semaphore.
- Task waitTask = _asyncState.WaitAsync();
+ // Serialize operations using the semaphore.
+ Task waitTask = _asyncState.WaitAsync();
- // If we got ownership immediately, and if there's enough space in our buffer
- // to buffer the entire write request, then do so and we're done.
- if (waitTask.Status == TaskStatus.RanToCompletion)
+ // If we got ownership immediately, and if there's enough space in our buffer
+ // to buffer the entire write request, then do so and we're done.
+ if (waitTask.Status == TaskStatus.RanToCompletion)
+ {
+ int spaceRemaining = _bufferLength - _writePos;
+ if (spaceRemaining >= source.Length)
{
- int spaceRemaining = _bufferLength - _writePos;
- if (spaceRemaining >= count)
+ try
{
- try
- {
- PrepareForWriting();
-
- Buffer.BlockCopy(buffer, offset, GetBuffer(), _writePos, count);
- _writePos += count;
-
- return Task.CompletedTask;
- }
- catch (Exception exc)
- {
- return Task.FromException(exc);
- }
- finally
- {
- _asyncState.Release();
- }
- }
- }
+ PrepareForWriting();
- // Otherwise, issue the whole request asynchronously.
- _asyncState.Update(buffer, offset, count);
- return waitTask.ContinueWith((t, s) =>
- {
- // The options available on Unix for writing asynchronously to an arbitrary file
- // handle typically amount to just using another thread to do the synchronous write,
- // which is exactly what this implementation does. This does mean there are subtle
- // differences in certain FileStream behaviors between Windows and Unix when multiple
- // asynchronous operations are issued against the stream to execute concurrently; on
- // Unix the operations will be serialized due to the usage of a semaphore, but the
- // position /length information won't be updated until after the write has completed,
- // whereas on Windows it may happen before the write has completed.
-
- Debug.Assert(t.Status == TaskStatus.RanToCompletion);
- var thisRef = (FileStream)s;
- try
+ source.Span.CopyTo(new Span<byte>(GetBuffer(), _writePos, source.Length));
+ _writePos += source.Length;
+
+ return Task.CompletedTask;
+ }
+ catch (Exception exc)
{
- byte[] b = thisRef._asyncState._buffer;
- thisRef._asyncState._buffer = null; // remove reference to user's buffer
- thisRef.WriteCore(b, thisRef._asyncState._offset, thisRef._asyncState._count);
+ return Task.FromException(exc);
}
- finally { thisRef._asyncState.Release(); }
- }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
- }
- else
- {
- return base.WriteAsync(buffer, offset, count, cancellationToken);
+ finally
+ {
+ _asyncState.Release();
+ }
+ }
}
- }
- /// <summary>
- /// Writes a byte to the current position in the stream and advances the position
- /// within the stream by one byte.
- /// </summary>
- /// <param name="value">The byte to write to the stream.</param>
- public override void WriteByte(byte value) // avoids an array allocation in the base implementation
- {
- if (_useAsyncIO)
+ // Otherwise, issue the whole request asynchronously.
+ _asyncState.ReadOnlyMemory = source;
+ return waitTask.ContinueWith((t, s) =>
{
- _asyncState.Wait();
- try { WriteByteCore(value); }
- finally { _asyncState.Release(); }
- }
- else
- {
- WriteByteCore(value);
- }
+ // The options available on Unix for writing asynchronously to an arbitrary file
+ // handle typically amount to just using another thread to do the synchronous write,
+ // which is exactly what this implementation does. This does mean there are subtle
+ // differences in certain FileStream behaviors between Windows and Unix when multiple
+ // asynchronous operations are issued against the stream to execute concurrently; on
+ // Unix the operations will be serialized due to the usage of a semaphore, but the
+ // position/length information won't be updated until after the write has completed,
+ // whereas on Windows it may happen before the write has completed.
+
+ Debug.Assert(t.Status == TaskStatus.RanToCompletion);
+ var thisRef = (FileStream)s;
+ try
+ {
+ ReadOnlyMemory<byte> readOnlyMemory = thisRef._asyncState.ReadOnlyMemory;
+ thisRef._asyncState.ReadOnlyMemory = default(ReadOnlyMemory<byte>);
+ thisRef.WriteSpan(readOnlyMemory.Span);
+ }
+ finally { thisRef._asyncState.Release(); }
+ }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
}
/// <summary>Sets the current position of this stream to the given value.</summary>
@@ -907,25 +802,11 @@ namespace System.IO
/// <summary>State used when the stream is in async mode.</summary>
private sealed class AsyncState : SemaphoreSlim
{
- /// <summary>The caller's buffer currently being used by the active async operation.</summary>
- internal byte[] _buffer;
- /// <summary>The caller's offset currently being used by the active async operation.</summary>
- internal int _offset;
- /// <summary>The caller's count currently being used by the active async operation.</summary>
- internal int _count;
- /// <summary>The last task successfully, synchronously returned task from ReadAsync.</summary>
- internal Task<int> _lastSuccessfulReadTask;
+ internal ReadOnlyMemory<byte> ReadOnlyMemory;
+ internal Memory<byte> Memory;
/// <summary>Initialize the AsyncState.</summary>
internal AsyncState() : base(initialCount: 1, maxCount: 1) { }
-
- /// <summary>Sets the active buffer, offset, and count.</summary>
- internal void Update(byte[] buffer, int offset, int count)
- {
- _buffer = buffer;
- _offset = offset;
- _count = count;
- }
}
}
}
diff --git a/src/mscorlib/shared/System/IO/FileStream.Win32.cs b/src/mscorlib/shared/System/IO/FileStream.Win32.cs
index 61cd007895..4b75ad6dad 100644
--- a/src/mscorlib/shared/System/IO/FileStream.Win32.cs
+++ b/src/mscorlib/shared/System/IO/FileStream.Win32.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using Microsoft.Win32.SafeHandles;
-using System.Runtime.InteropServices;
namespace System.IO
{
@@ -11,6 +10,11 @@ namespace System.IO
{
private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options)
{
+ return CreateFileOpenHandle(mode, share, options);
+ }
+
+ private unsafe SafeFileHandle CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options)
+ {
Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share);
int fAccess =
@@ -30,44 +34,13 @@ namespace System.IO
// For mitigating local elevation of privilege attack through named pipes
// make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
// named pipe server can't impersonate a high privileged client security context
+ // (note that this is the effective default on CreateFile2)
flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS);
- // Don't pop up a dialog for reading from an empty floppy drive
- uint oldMode;
- bool success = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode);
- try
- {
- SafeFileHandle fileHandle = Interop.Kernel32.CreateFile(_path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero);
- fileHandle.IsAsync = _useAsyncIO;
-
- if (fileHandle.IsInvalid)
- {
- // Return a meaningful exception with the full path.
-
- // NT5 oddity - when trying to open "C:\" as a Win32FileStream,
- // we usually get ERROR_PATH_NOT_FOUND from the OS. We should
- // probably be consistent w/ every other directory.
- int errorCode = Marshal.GetLastWin32Error();
-
- if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path))
- errorCode = Interop.Errors.ERROR_ACCESS_DENIED;
-
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
- }
-
- int fileType = Interop.Kernel32.GetFileType(fileHandle);
- if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK)
- {
- fileHandle.Dispose();
- throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles);
- }
-
- return fileHandle;
- }
- finally
+ using (DisableMediaInsertionPrompt.Create())
{
- if (success)
- Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode);
+ return ValidateFileHandle(
+ Interop.Kernel32.CreateFile(_path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero));
}
}
}
diff --git a/src/mscorlib/shared/System/IO/FileStream.WinRT.cs b/src/mscorlib/shared/System/IO/FileStream.WinRT.cs
index b9a9f8a783..304088eea7 100644
--- a/src/mscorlib/shared/System/IO/FileStream.WinRT.cs
+++ b/src/mscorlib/shared/System/IO/FileStream.WinRT.cs
@@ -13,7 +13,7 @@ namespace System.IO
{
Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share);
- int fAccess =
+ int access =
((_access & FileAccess.Read) == FileAccess.Read ? GENERIC_READ : 0) |
((_access & FileAccess.Write) == FileAccess.Write ? GENERIC_WRITE : 0);
@@ -30,31 +30,15 @@ namespace System.IO
parameters.dwFileFlags = (uint)options;
parameters.lpSecurityAttributes = &secAttrs;
- SafeFileHandle fileHandle = Interop.Kernel32.CreateFile2(
- lpFileName: _path,
- dwDesiredAccess: fAccess,
- dwShareMode: share,
- dwCreationDisposition: mode,
- pCreateExParams: &parameters);
-
- fileHandle.IsAsync = _useAsyncIO;
-
- if (fileHandle.IsInvalid)
+ using (DisableMediaInsertionPrompt.Create())
{
- // Return a meaningful exception with the full path.
-
- // NT5 oddity - when trying to open "C:\" as a Win32FileStream,
- // we usually get ERROR_PATH_NOT_FOUND from the OS. We should
- // probably be consistent w/ every other directory.
- int errorCode = Marshal.GetLastWin32Error();
-
- if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path))
- errorCode = Interop.Errors.ERROR_ACCESS_DENIED;
-
- throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
+ return ValidateFileHandle(Interop.Kernel32.CreateFile2(
+ lpFileName: _path,
+ dwDesiredAccess: access,
+ dwShareMode: share,
+ dwCreationDisposition: mode,
+ pCreateExParams: ref parameters));
}
-
- return fileHandle;
}
}
}
diff --git a/src/mscorlib/shared/System/IO/FileStream.Windows.cs b/src/mscorlib/shared/System/IO/FileStream.Windows.cs
index cdf1cb092e..eec11b4b13 100644
--- a/src/mscorlib/shared/System/IO/FileStream.Windows.cs
+++ b/src/mscorlib/shared/System/IO/FileStream.Windows.cs
@@ -48,7 +48,6 @@ namespace System.IO
private static unsafe IOCompletionCallback s_ioCallback = FileStreamCompletionSource.IOCallback;
- private Task<int> _lastSynchronouslyCompletedTask = null; // cached task for read ops that complete synchronously
private Task _activeBufferOperation = null; // tracks in-progress async ops using the buffer
private PreAllocatedOverlapped _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations
private FileStreamCompletionSource _currentOverlappedOwner; // async op currently using the preallocated overlapped
@@ -313,7 +312,7 @@ namespace System.IO
// If the buffer is already flushed, don't spin up the OS write
if (_writePos == 0) return Task.CompletedTask;
- Task flushTask = WriteInternalCoreAsync(GetBuffer(), 0, _writePos, cancellationToken);
+ Task flushTask = WriteAsyncInternalCore(new ReadOnlyMemory<byte>(GetBuffer(), 0, _writePos), cancellationToken);
_writePos = 0;
// Update the active buffer operation
@@ -324,6 +323,8 @@ namespace System.IO
return flushTask;
}
+ private void FlushWriteBufferForWriteByte() => FlushWriteBuffer();
+
// Writes are buffered. Anytime the buffer fills up
// (_writePos + delta > _bufferSize) or the buffer switches to reading
// and there is left over data (_writePos > 0), this function must be called.
@@ -355,7 +356,7 @@ namespace System.IO
}
else
{
- WriteCore(GetBuffer(), 0, _writePos);
+ WriteCore(new ReadOnlySpan<byte>(GetBuffer(), 0, _writePos));
}
_writePos = 0;
@@ -411,14 +412,9 @@ namespace System.IO
// accessing its fields by ref. This avoids a compiler warning.
private FileStreamCompletionSource CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource newSource, FileStreamCompletionSource existingSource) => Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource);
- public override int Read(byte[] array, int offset, int count)
- {
- ValidateReadWriteArgs(array, offset, count);
- return ReadCore(array, offset, count);
- }
-
- private int ReadCore(byte[] array, int offset, int count)
+ private int ReadSpan(Span<byte> destination)
{
+ Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode");
Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength),
"We're either reading or writing, but not both.");
@@ -430,23 +426,23 @@ namespace System.IO
{
if (!CanRead) throw Error.GetReadNotSupported();
if (_writePos > 0) FlushWriteBuffer();
- if (!CanSeek || (count >= _bufferLength))
+ if (!CanSeek || (destination.Length >= _bufferLength))
{
- n = ReadNative(array, offset, count);
+ n = ReadNative(destination);
// Throw away read buffer.
_readPos = 0;
_readLength = 0;
return n;
}
- n = ReadNative(GetBuffer(), 0, _bufferLength);
+ n = ReadNative(GetBuffer());
if (n == 0) return 0;
isBlocked = n < _bufferLength;
_readPos = 0;
_readLength = n;
}
// Now copy min of count or numBytesAvailable (i.e. near EOF) to array.
- if (n > count) n = count;
- Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n);
+ if (n > destination.Length) n = destination.Length;
+ new ReadOnlySpan<byte>(GetBuffer(), _readPos, n).CopyTo(destination);
_readPos += n;
// We may have read less than the number of bytes the user asked
@@ -466,10 +462,10 @@ namespace System.IO
// read some more from the underlying stream. However, if we got
// fewer bytes from the underlying stream than we asked for (i.e. we're
// probably blocked), don't ask for more bytes.
- if (n < count && !isBlocked)
+ if (n < destination.Length && !isBlocked)
{
Debug.Assert(_readPos == _readLength, "Read buffer should be empty!");
- int moreBytesRead = ReadNative(array, offset + n, count - n);
+ int moreBytesRead = ReadNative(destination.Slice(n));
n += moreBytesRead;
// We've just made our buffer inconsistent with our position
// pointer. We must throw away the read buffer.
@@ -482,28 +478,28 @@ namespace System.IO
}
[Conditional("DEBUG")]
- private void AssertCanRead(byte[] buffer, int offset, int count)
+ private void AssertCanRead()
{
Debug.Assert(!_fileHandle.IsClosed, "!_fileHandle.IsClosed");
Debug.Assert(CanRead, "CanRead");
- Debug.Assert(buffer != null, "buffer != null");
- Debug.Assert(_writePos == 0, "_writePos == 0");
- Debug.Assert(offset >= 0, "offset is negative");
- Debug.Assert(count >= 0, "count is negative");
}
- private unsafe int ReadNative(byte[] buffer, int offset, int count)
- {
- AssertCanRead(buffer, offset, count);
+ /// <summary>Reads from the file handle into the buffer, overwriting anything in it.</summary>
+ private int FillReadBufferForReadByte() =>
+ _useAsyncIO ?
+ ReadNativeAsync(new Memory<byte>(_buffer), 0, CancellationToken.None).GetAwaiter().GetResult() :
+ ReadNative(_buffer);
- if (_useAsyncIO)
- return ReadNativeAsync(buffer, offset, count, 0, CancellationToken.None).GetAwaiter().GetResult();
+ private unsafe int ReadNative(Span<byte> buffer)
+ {
+ Debug.Assert(!_useAsyncIO, $"{nameof(ReadNative)} doesn't work on asynchronous file streams.");
+ AssertCanRead();
// Make sure we are reading from the right spot
VerifyOSHandlePosition();
int errorCode = 0;
- int r = ReadFileNative(_fileHandle, buffer, offset, count, null, out errorCode);
+ int r = ReadFileNative(_fileHandle, buffer, null, out errorCode);
if (r == -1)
{
@@ -579,7 +575,6 @@ namespace System.IO
{
if (_readPos > 0)
{
- //Console.WriteLine("Seek: seeked for 0, adjusting buffer back by: "+_readPos+" _readLen: "+_readLen);
Buffer.BlockCopy(GetBuffer(), _readPos, GetBuffer(), 0, _readLength - _readPos);
_readLength -= _readPos;
_readPos = 0;
@@ -592,7 +587,6 @@ namespace System.IO
else if (oldPos - _readPos < pos && pos < oldPos + _readLength - _readPos)
{
int diff = (int)(pos - oldPos);
- //Console.WriteLine("Seek: diff was "+diff+", readpos was "+_readPos+" adjusting buffer - shrinking by "+ (_readPos + diff));
Buffer.BlockCopy(GetBuffer(), _readPos + diff, GetBuffer(), 0, _readLength - (_readPos + diff));
_readLength -= (_readPos + diff);
_readPos = 0;
@@ -646,9 +640,9 @@ namespace System.IO
_preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer);
}
- public override void Write(byte[] array, int offset, int count)
+ private void WriteSpan(ReadOnlySpan<byte> source)
{
- ValidateReadWriteArgs(array, offset, count);
+ Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode");
if (_writePos == 0)
{
@@ -672,65 +666,56 @@ namespace System.IO
int numBytes = _bufferLength - _writePos; // space left in buffer
if (numBytes > 0)
{
- if (numBytes > count)
- numBytes = count;
- Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, numBytes);
- _writePos += numBytes;
- if (count == numBytes) return;
- offset += numBytes;
- count -= numBytes;
+ if (numBytes >= source.Length)
+ {
+ source.CopyTo(new Span<byte>(GetBuffer()).Slice(_writePos));
+ _writePos += source.Length;
+ return;
+ }
+ else
+ {
+ source.Slice(0, numBytes).CopyTo(new Span<byte>(GetBuffer()).Slice(_writePos));
+ _writePos += numBytes;
+ source = source.Slice(numBytes);
+ }
}
// Reset our buffer. We essentially want to call FlushWrite
// without calling Flush on the underlying Stream.
- if (_useAsyncIO)
- {
- WriteInternalCoreAsync(GetBuffer(), 0, _writePos, CancellationToken.None).GetAwaiter().GetResult();
- }
- else
- {
- WriteCore(GetBuffer(), 0, _writePos);
- }
+ WriteCore(new ReadOnlySpan<byte>(GetBuffer(), 0, _writePos));
_writePos = 0;
}
+
// If the buffer would slow writes down, avoid buffer completely.
- if (count >= _bufferLength)
+ if (source.Length >= _bufferLength)
{
Debug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted.");
- WriteCore(array, offset, count);
+ WriteCore(source);
return;
}
- else if (count == 0)
+ else if (source.Length == 0)
{
return; // Don't allocate a buffer then call memcpy for 0 bytes.
}
// Copy remaining bytes into buffer, to write at a later date.
- Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, count);
- _writePos = count;
+ source.CopyTo(new Span<byte>(GetBuffer()).Slice(_writePos));
+ _writePos = source.Length;
return;
}
- private unsafe void WriteCore(byte[] buffer, int offset, int count)
+ private unsafe void WriteCore(ReadOnlySpan<byte> source)
{
+ Debug.Assert(!_useAsyncIO);
Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");
Debug.Assert(CanWrite, "_parent.CanWrite");
-
- Debug.Assert(buffer != null, "buffer != null");
Debug.Assert(_readPos == _readLength, "_readPos == _readLen");
- Debug.Assert(offset >= 0, "offset is negative");
- Debug.Assert(count >= 0, "count is negative");
- if (_useAsyncIO)
- {
- WriteInternalCoreAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
- return;
- }
// Make sure we are writing to the position that we think we are
VerifyOSHandlePosition();
int errorCode = 0;
- int r = WriteFileNative(_fileHandle, buffer, offset, count, null, out errorCode);
+ int r = WriteFileNative(_fileHandle, source, null, out errorCode);
if (r == -1)
{
@@ -742,9 +727,8 @@ namespace System.IO
else
{
// ERROR_INVALID_PARAMETER may be returned for writes
- // where the position is too large (i.e. writing at Int64.MaxValue
- // on Win9x) OR for synchronous writes to a handle opened
- // asynchronously.
+ // where the position is too large or for synchronous writes
+ // to a handle opened asynchronously.
if (errorCode == ERROR_INVALID_PARAMETER)
throw new IOException(SR.IO_FileTooLongOrHandleNotSync);
throw Win32Marshal.GetExceptionForWin32Error(errorCode);
@@ -755,15 +739,9 @@ namespace System.IO
return;
}
- private Task<int> ReadAsyncInternal(byte[] array, int offset, int numBytes, CancellationToken cancellationToken)
+ private Task<int> ReadAsyncInternal(Memory<byte> destination, CancellationToken cancellationToken, out int synchronousResult)
{
- // If async IO is not supported on this platform or
- // if this Win32FileStream was not opened with FileOptions.Asynchronous.
- if (!_useAsyncIO)
- {
- return base.ReadAsync(array, offset, numBytes, cancellationToken);
- }
-
+ Debug.Assert(_useAsyncIO);
if (!CanRead) throw Error.GetReadNotSupported();
Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both.");
@@ -785,18 +763,17 @@ namespace System.IO
// pipes. But don't completely ignore buffered data either.
if (_readPos < _readLength)
{
- int n = _readLength - _readPos;
- if (n > numBytes) n = numBytes;
- Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n);
+ int n = Math.Min(_readLength - _readPos, destination.Length);
+ new Span<byte>(GetBuffer(), _readPos, n).CopyTo(destination.Span);
_readPos += n;
-
- // Return a completed task
- return TaskFromResultOrCache(n);
+ synchronousResult = n;
+ return null;
}
else
{
Debug.Assert(_writePos == 0, "Win32FileStream must not have buffered write data here! Pipes should be unidirectional.");
- return ReadNativeAsync(array, offset, numBytes, 0, cancellationToken);
+ synchronousResult = 0;
+ return ReadNativeAsync(destination, 0, cancellationToken);
}
}
@@ -817,17 +794,16 @@ namespace System.IO
// problem, and any async read less than 64K gets turned into a
// synchronous read by NT anyways... --
- if (numBytes < _bufferLength)
+ if (destination.Length < _bufferLength)
{
- Task<int> readTask = ReadNativeAsync(GetBuffer(), 0, _bufferLength, 0, cancellationToken);
+ Task<int> readTask = ReadNativeAsync(new Memory<byte>(GetBuffer()), 0, cancellationToken);
_readLength = readTask.GetAwaiter().GetResult();
- int n = _readLength;
- if (n > numBytes) n = numBytes;
- Buffer.BlockCopy(GetBuffer(), 0, array, offset, n);
+ int n = Math.Min(_readLength, destination.Length);
+ new Span<byte>(GetBuffer(), 0, n).CopyTo(destination.Span);
_readPos = n;
- // Return a completed task (recycling the one above if possible)
- return (_readLength == n ? readTask : TaskFromResultOrCache(n));
+ synchronousResult = n;
+ return null;
}
else
{
@@ -835,20 +811,21 @@ namespace System.IO
// with our read buffer. Throw away the read buffer's contents.
_readPos = 0;
_readLength = 0;
- return ReadNativeAsync(array, offset, numBytes, 0, cancellationToken);
+ synchronousResult = 0;
+ return ReadNativeAsync(destination, 0, cancellationToken);
}
}
else
{
- int n = _readLength - _readPos;
- if (n > numBytes) n = numBytes;
- Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n);
+ int n = Math.Min(_readLength - _readPos, destination.Length);
+ new Span<byte>(GetBuffer(), _readPos, n).CopyTo(destination.Span);
_readPos += n;
- if (n >= numBytes)
+ if (n == destination.Length)
{
// Return a completed task
- return TaskFromResultOrCache(n);
+ synchronousResult = n;
+ return null;
}
else
{
@@ -859,19 +836,21 @@ namespace System.IO
// Throw away read buffer.
_readPos = 0;
_readLength = 0;
- return ReadNativeAsync(array, offset + n, numBytes - n, n, cancellationToken);
+ synchronousResult = 0;
+ return ReadNativeAsync(destination.Slice(n), n, cancellationToken);
}
}
}
- unsafe private Task<int> ReadNativeAsync(byte[] bytes, int offset, int numBytes, int numBufferedBytesRead, CancellationToken cancellationToken)
+ unsafe private Task<int> ReadNativeAsync(Memory<byte> destination, int numBufferedBytesRead, CancellationToken cancellationToken)
{
- AssertCanRead(bytes, offset, numBytes);
+ AssertCanRead();
Debug.Assert(_useAsyncIO, "ReadNativeAsync doesn't work on synchronous file streams!");
// Create and store async stream class library specific data in the async result
-
- FileStreamCompletionSource completionSource = new FileStreamCompletionSource(this, numBufferedBytesRead, bytes, cancellationToken);
+ FileStreamCompletionSource completionSource = destination.TryGetArray(out ArraySegment<byte> memoryArray) ?
+ new FileStreamCompletionSource(this, numBufferedBytesRead, memoryArray.Array) :
+ new MemoryFileStreamCompletionSource(this, numBufferedBytesRead, destination);
NativeOverlapped* intOverlapped = completionSource.Overlapped;
// Calculate position in the file we should be at after the read is done
@@ -882,12 +861,16 @@ namespace System.IO
// Make sure we are reading from the position that we think we are
VerifyOSHandlePosition();
- if (_filePosition + numBytes > len)
+ if (_filePosition + destination.Length > len)
{
if (_filePosition <= len)
- numBytes = (int)(len - _filePosition);
+ {
+ destination = destination.Slice(0, (int)(len - _filePosition));
+ }
else
- numBytes = 0;
+ {
+ destination = default(Memory<byte>);
+ }
}
// Now set the position to read from in the NativeOverlapped struct
@@ -904,12 +887,13 @@ namespace System.IO
// the file pointer when writing to a UNC path!
// So changed the code below to seek to an absolute
// location, not a relative one. ReadFile seems consistent though.
- SeekCore(_fileHandle, numBytes, SeekOrigin.Current);
+ SeekCore(_fileHandle, destination.Length, SeekOrigin.Current);
}
// queue an async ReadFile operation and pass in a packed overlapped
int errorCode = 0;
- int r = ReadFileNative(_fileHandle, bytes, offset, numBytes, intOverlapped, out errorCode);
+ int r = ReadFileNative(_fileHandle, destination.Span, intOverlapped, out errorCode);
+
// ReadFile, the OS version, will return 0 on failure. But
// my ReadFileNative wrapper returns -1. My wrapper will return
// the following:
@@ -920,7 +904,7 @@ namespace System.IO
// read back from this call when using overlapped structures! You must
// not pass in a non-null lpNumBytesRead to ReadFile when using
// overlapped structures! This is by design NT behavior.
- if (r == -1 && numBytes != -1)
+ if (r == -1)
{
// For pipes, when they hit EOF, they will come here.
if (errorCode == ERROR_BROKEN_PIPE)
@@ -951,10 +935,10 @@ namespace System.IO
throw Win32Marshal.GetExceptionForWin32Error(errorCode);
}
}
- else
+ else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
{
// Only once the IO is pending do we register for cancellation
- completionSource.RegisterForCancellation();
+ completionSource.RegisterForCancellation(cancellationToken);
}
}
else
@@ -966,33 +950,19 @@ namespace System.IO
// synchronously or asynchronously. We absolutely must not
// set asyncResult._numBytes here, since will never have correct
// results.
- //Console.WriteLine("ReadFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on a separate thread");
}
return completionSource.Task;
}
- // Reads a byte from the file stream. Returns the byte cast to an int
- // or -1 if reading from the end of the stream.
- public override int ReadByte()
+ private Task WriteAsyncInternal(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
{
- return ReadByteCore();
- }
-
- private Task WriteAsyncInternal(byte[] array, int offset, int numBytes, CancellationToken cancellationToken)
- {
- // If async IO is not supported on this platform or
- // if this Win32FileStream was not opened with FileOptions.Asynchronous.
- if (!_useAsyncIO)
- {
- return base.WriteAsync(array, offset, numBytes, cancellationToken);
- }
-
- if (!CanWrite) throw Error.GetWriteNotSupported();
-
+ Debug.Assert(_useAsyncIO);
Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both.");
Debug.Assert(!_isPipe || (_readPos == 0 && _readLength == 0), "Win32FileStream must not have buffered data here! Pipes should be unidirectional.");
+ if (!CanWrite) throw Error.GetWriteNotSupported();
+
bool writeDataStoredInBuffer = false;
if (!_isPipe) // avoid async buffering with pipes, as doing so can lead to deadlocks (see comments in ReadInternalAsyncCore)
{
@@ -1016,10 +986,10 @@ namespace System.IO
// - There's no active flush operation, such that we don't have to worry about the existing buffer being in use.
// - And the data we're trying to write fits in the buffer, meaning it wasn't already filled by previous writes.
// In that case, just store it in the buffer.
- if (numBytes < _bufferLength && !HasActiveBufferOperation && numBytes <= remainingBuffer)
+ if (source.Length < _bufferLength && !HasActiveBufferOperation && source.Length <= remainingBuffer)
{
- Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, numBytes);
- _writePos += numBytes;
+ source.Span.CopyTo(new Span<byte>(GetBuffer(), _writePos, source.Length));
+ _writePos += source.Length;
writeDataStoredInBuffer = true;
// There is one special-but-common case, common because devs often use
@@ -1028,7 +998,7 @@ namespace System.IO
// then we're done and can return a completed task now. But if we filled the buffer
// completely, we want to do the asynchronous flush/write as part of this operation
// rather than waiting until the next write that fills the buffer.
- if (numBytes != remainingBuffer)
+ if (source.Length != remainingBuffer)
return Task.CompletedTask;
Debug.Assert(_writePos == _bufferLength);
@@ -1084,40 +1054,37 @@ namespace System.IO
// Finally, issue the write asynchronously, and return a Task that logically
// represents the write operation, including any flushing done.
- Task writeTask = WriteInternalCoreAsync(array, offset, numBytes, cancellationToken);
+ Task writeTask = WriteAsyncInternalCore(source, cancellationToken);
return
(flushTask == null || flushTask.Status == TaskStatus.RanToCompletion) ? writeTask :
(writeTask.Status == TaskStatus.RanToCompletion) ? flushTask :
Task.WhenAll(flushTask, writeTask);
}
- private unsafe Task WriteInternalCoreAsync(byte[] bytes, int offset, int numBytes, CancellationToken cancellationToken)
+ private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
{
Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");
Debug.Assert(CanWrite, "_parent.CanWrite");
- Debug.Assert(bytes != null, "bytes != null");
Debug.Assert(_readPos == _readLength, "_readPos == _readLen");
Debug.Assert(_useAsyncIO, "WriteInternalCoreAsync doesn't work on synchronous file streams!");
- Debug.Assert(offset >= 0, "offset is negative");
- Debug.Assert(numBytes >= 0, "numBytes is negative");
// Create and store async stream class library specific data in the async result
- FileStreamCompletionSource completionSource = new FileStreamCompletionSource(this, 0, bytes, cancellationToken);
+ FileStreamCompletionSource completionSource = source.DangerousTryGetArray(out ArraySegment<byte> array) ?
+ new FileStreamCompletionSource(this, 0, array.Array) :
+ new MemoryFileStreamCompletionSource(this, 0, source);
NativeOverlapped* intOverlapped = completionSource.Overlapped;
if (CanSeek)
{
// Make sure we set the length of the file appropriately.
long len = Length;
- //Console.WriteLine("WriteInternalCoreAsync - Calculating end pos. pos: "+pos+" len: "+len+" numBytes: "+numBytes);
// Make sure we are writing to the position that we think we are
VerifyOSHandlePosition();
- if (_filePosition + numBytes > len)
+ if (_filePosition + source.Length > len)
{
- //Console.WriteLine("WriteInternalCoreAsync - Setting length to: "+(pos + numBytes));
- SetLengthCore(_filePosition + numBytes);
+ SetLengthCore(_filePosition + source.Length);
}
// Now set the position to read from in the NativeOverlapped struct
@@ -1128,14 +1095,12 @@ namespace System.IO
// When using overlapped IO, the OS is not supposed to
// touch the file pointer location at all. We will adjust it
// ourselves. This isn't threadsafe.
- SeekCore(_fileHandle, numBytes, SeekOrigin.Current);
+ SeekCore(_fileHandle, source.Length, SeekOrigin.Current);
}
- //Console.WriteLine("WriteInternalCoreAsync finishing. pos: "+pos+" numBytes: "+numBytes+" _pos: "+_pos+" Position: "+Position);
-
int errorCode = 0;
// queue an async WriteFile operation and pass in a packed overlapped
- int r = WriteFileNative(_fileHandle, bytes, offset, numBytes, intOverlapped, out errorCode);
+ int r = WriteFileNative(_fileHandle, source.Span, intOverlapped, out errorCode);
// WriteFile, the OS version, will return 0 on failure. But
// my WriteFileNative wrapper returns -1. My wrapper will return
@@ -1147,10 +1112,8 @@ namespace System.IO
// written back from this call when using overlapped IO! You must
// not pass in a non-null lpNumBytesWritten to WriteFile when using
// overlapped structures! This is ByDesign NT behavior.
- if (r == -1 && numBytes != -1)
+ if (r == -1)
{
- //Console.WriteLine("WriteFile returned 0; Write will complete asynchronously (if errorCode==3e5) errorCode: 0x{0:x}", errorCode);
-
// For pipes, when they are closed on the other side, they will come here.
if (errorCode == ERROR_NO_DATA)
{
@@ -1177,10 +1140,10 @@ namespace System.IO
throw Win32Marshal.GetExceptionForWin32Error(errorCode);
}
}
- else // ERROR_IO_PENDING
+ else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
{
// Only once the IO is pending do we register for cancellation
- completionSource.RegisterForCancellation();
+ completionSource.RegisterForCancellation(cancellationToken);
}
}
else
@@ -1192,17 +1155,11 @@ namespace System.IO
// synchronously or asynchronously. We absolutely must not
// set asyncResult._numBytes here, since will never have correct
// results.
- //Console.WriteLine("WriteFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on another thread.");
}
return completionSource.Task;
}
- public override void WriteByte(byte value)
- {
- WriteByteCore(value);
- }
-
// Windows API definitions, from winbase.h and others
private const int FILE_ATTRIBUTE_NORMAL = 0x00000080;
@@ -1223,35 +1180,19 @@ namespace System.IO
private const int ERROR_IO_PENDING = 997;
// __ConsoleStream also uses this code.
- private unsafe int ReadFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int errorCode)
+ private unsafe int ReadFileNative(SafeFileHandle handle, Span<byte> bytes, NativeOverlapped* overlapped, out int errorCode)
{
Debug.Assert(handle != null, "handle != null");
- Debug.Assert(offset >= 0, "offset >= 0");
- Debug.Assert(count >= 0, "count >= 0");
- Debug.Assert(bytes != null, "bytes != null");
- // Don't corrupt memory when multiple threads are erroneously writing
- // to this stream simultaneously.
- if (bytes.Length - offset < count)
- throw new IndexOutOfRangeException(SR.IndexOutOfRange_IORaceCondition);
-
Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to ReadFileNative.");
- // You can't use the fixed statement on an array of length 0.
- if (bytes.Length == 0)
- {
- errorCode = 0;
- return 0;
- }
-
- int r = 0;
+ int r;
int numBytesRead = 0;
- fixed (byte* p = &bytes[0])
+ fixed (byte* p = &bytes.DangerousGetPinnableReference())
{
- if (_useAsyncIO)
- r = Interop.Kernel32.ReadFile(handle, p + offset, count, IntPtr.Zero, overlapped);
- else
- r = Interop.Kernel32.ReadFile(handle, p + offset, count, out numBytesRead, IntPtr.Zero);
+ r = _useAsyncIO ?
+ Interop.Kernel32.ReadFile(handle, p, bytes.Length, IntPtr.Zero, overlapped) :
+ Interop.Kernel32.ReadFile(handle, p, bytes.Length, out numBytesRead, IntPtr.Zero);
}
if (r == 0)
@@ -1266,37 +1207,19 @@ namespace System.IO
}
}
- private unsafe int WriteFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int errorCode)
+ private unsafe int WriteFileNative(SafeFileHandle handle, ReadOnlySpan<byte> buffer, NativeOverlapped* overlapped, out int errorCode)
{
Debug.Assert(handle != null, "handle != null");
- Debug.Assert(offset >= 0, "offset >= 0");
- Debug.Assert(count >= 0, "count >= 0");
- Debug.Assert(bytes != null, "bytes != null");
- // Don't corrupt memory when multiple threads are erroneously writing
- // to this stream simultaneously. (the OS is reading from
- // the array we pass to WriteFile, but if we read beyond the end and
- // that memory isn't allocated, we could get an AV.)
- if (bytes.Length - offset < count)
- throw new IndexOutOfRangeException(SR.IndexOutOfRange_IORaceCondition);
-
Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to WriteFileNative.");
- // You can't use the fixed statement on an array of length 0.
- if (bytes.Length == 0)
- {
- errorCode = 0;
- return 0;
- }
-
int numBytesWritten = 0;
- int r = 0;
+ int r;
- fixed (byte* p = &bytes[0])
+ fixed (byte* p = &buffer.DangerousGetPinnableReference())
{
- if (_useAsyncIO)
- r = Interop.Kernel32.WriteFile(handle, p + offset, count, IntPtr.Zero, overlapped);
- else
- r = Interop.Kernel32.WriteFile(handle, p + offset, count, out numBytesWritten, IntPtr.Zero);
+ r = _useAsyncIO ?
+ Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped) :
+ Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, IntPtr.Zero);
}
if (r == 0)
@@ -1472,7 +1395,7 @@ namespace System.IO
}
// Kick off the read.
- synchronousSuccess = ReadFileNative(_fileHandle, copyBuffer, 0, copyBuffer.Length, readAwaitable._nativeOverlapped, out errorCode) >= 0;
+ synchronousSuccess = ReadFileNative(_fileHandle, copyBuffer, readAwaitable._nativeOverlapped, out errorCode) >= 0;
}
// If the operation did not synchronously succeed, it either failed or initiated the asynchronous operation.
@@ -1696,20 +1619,6 @@ namespace System.IO
}
}
- private Task<int> TaskFromResultOrCache(int result)
- {
- Task<int> completedTask = _lastSynchronouslyCompletedTask;
- Debug.Assert(completedTask == null || completedTask.Status == TaskStatus.RanToCompletion, "Cached task should have completed successfully");
-
- if ((completedTask == null) || (completedTask.Result != result))
- {
- completedTask = Task.FromResult(result);
- _lastSynchronouslyCompletedTask = completedTask;
- }
-
- return completedTask;
- }
-
private void LockInternal(long position, long length)
{
int positionLow = unchecked((int)(position));
@@ -1735,5 +1644,25 @@ namespace System.IO
throw Win32Marshal.GetExceptionForLastWin32Error();
}
}
+ private SafeFileHandle ValidateFileHandle(SafeFileHandle fileHandle)
+ {
+ if (fileHandle.IsInvalid)
+ {
+ // Return a meaningful exception with the full path.
+
+ // NT5 oddity - when trying to open "C:\" as a Win32FileStream,
+ // we usually get ERROR_PATH_NOT_FOUND from the OS. We should
+ // probably be consistent w/ every other directory.
+ int errorCode = Marshal.GetLastWin32Error();
+
+ if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path))
+ errorCode = Interop.Errors.ERROR_ACCESS_DENIED;
+
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
+ }
+
+ fileHandle.IsAsync = _useAsyncIO;
+ return fileHandle;
+ }
}
}
diff --git a/src/mscorlib/shared/System/IO/FileStream.cs b/src/mscorlib/shared/System/IO/FileStream.cs
index 39f7b60d12..65c63bcc53 100644
--- a/src/mscorlib/shared/System/IO/FileStream.cs
+++ b/src/mscorlib/shared/System/IO/FileStream.cs
@@ -51,6 +51,9 @@ namespace System.IO
/// </summary>
private readonly bool _useAsyncIO;
+ /// <summary>cached task for read ops that complete synchronously</summary>
+ private Task<int> _lastSynchronouslyCompletedTask = null;
+
/// <summary>
/// Currently cached position in the stream. This should always mirror the underlying file's actual position,
/// and should only ever be out of sync if another stream with access to this same file manipulates it, at which
@@ -292,6 +295,36 @@ namespace System.IO
return FlushAsyncInternal(cancellationToken);
}
+ public override int Read(byte[] array, int offset, int count)
+ {
+ ValidateReadWriteArgs(array, offset, count);
+ return _useAsyncIO ?
+ ReadAsyncTask(array, offset, count, CancellationToken.None).GetAwaiter().GetResult() :
+ ReadSpan(new Span<byte>(array, offset, count));
+ }
+
+ public override int Read(Span<byte> destination)
+ {
+ if (GetType() == typeof(FileStream) && !_useAsyncIO)
+ {
+ if (_fileHandle.IsClosed)
+ {
+ throw Error.GetFileNotOpen();
+ }
+ return ReadSpan(destination);
+ }
+ else
+ {
+ // This type is derived from FileStream and/or the stream is in async mode. If this is a
+ // derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span<byte>)
+ // overload being introduced. In that case, this Read(Span<byte>) overload should use the behavior
+ // of Read(byte[],int,int) overload. Or if the stream is in async mode, we can't call the
+ // synchronous ReadSpan, so we similarly call the base Read, which will turn delegate to
+ // Read(byte[],int,int), which will do the right thing if we're in async mode.
+ return base.Read(destination);
+ }
+ }
+
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer == null)
@@ -304,10 +337,12 @@ namespace System.IO
throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
// If we have been inherited into a subclass, the following implementation could be incorrect
- // since it does not call through to Read() or ReadAsync() which a subclass might have overridden.
+ // since it does not call through to Read() which a subclass might have overridden.
// To be safe we will only use this implementation in cases where we know it is safe to do so,
// and delegate to our base class (which will call into Read/ReadAsync) when we are not sure.
- if (GetType() != typeof(FileStream))
+ // Similarly, if we weren't opened for asynchronous I/O, call to the base implementation so that
+ // Read is invoked asynchronously.
+ if (GetType() != typeof(FileStream) || !_useAsyncIO)
return base.ReadAsync(buffer, offset, count, cancellationToken);
if (cancellationToken.IsCancellationRequested)
@@ -316,7 +351,86 @@ namespace System.IO
if (IsClosed)
throw Error.GetFileNotOpen();
- return ReadAsyncInternal(buffer, offset, count, cancellationToken);
+ return ReadAsyncTask(buffer, offset, count, cancellationToken);
+ }
+
+ public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (!_useAsyncIO || GetType() != typeof(FileStream))
+ {
+ // If we're not using async I/O, delegate to the base, which will queue a call to Read.
+ // Or if this isn't a concrete FileStream, a derived type may have overridden ReadAsync(byte[],...),
+ // which was introduced first, so delegate to the base which will delegate to that.
+ return base.ReadAsync(destination, cancellationToken);
+ }
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
+ }
+
+ if (IsClosed)
+ {
+ throw Error.GetFileNotOpen();
+ }
+
+ Task<int> t = ReadAsyncInternal(destination, cancellationToken, out int synchronousResult);
+ return t != null ?
+ new ValueTask<int>(t) :
+ new ValueTask<int>(synchronousResult);
+ }
+
+ private Task<int> ReadAsyncTask(byte[] array, int offset, int count, CancellationToken cancellationToken)
+ {
+ Task<int> t = ReadAsyncInternal(new Memory<byte>(array, offset, count), cancellationToken, out int synchronousResult);
+
+ if (t == null)
+ {
+ t = _lastSynchronouslyCompletedTask;
+ Debug.Assert(t == null || t.IsCompletedSuccessfully, "Cached task should have completed successfully");
+
+ if (t == null || t.Result != synchronousResult)
+ {
+ _lastSynchronouslyCompletedTask = t = Task.FromResult(synchronousResult);
+ }
+ }
+
+ return t;
+ }
+
+ public override void Write(byte[] array, int offset, int count)
+ {
+ ValidateReadWriteArgs(array, offset, count);
+ if (_useAsyncIO)
+ {
+ WriteAsyncInternal(new ReadOnlyMemory<byte>(array, offset, count), CancellationToken.None).GetAwaiter().GetResult();
+ }
+ else
+ {
+ WriteSpan(new ReadOnlySpan<byte>(array, offset, count));
+ }
+ }
+
+ public override void Write(ReadOnlySpan<byte> destination)
+ {
+ if (GetType() == typeof(FileStream) && !_useAsyncIO)
+ {
+ if (_fileHandle.IsClosed)
+ {
+ throw Error.GetFileNotOpen();
+ }
+ WriteSpan(destination);
+ }
+ else
+ {
+ // This type is derived from FileStream and/or the stream is in async mode. If this is a
+ // derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan<byte>)
+ // overload being introduced. In that case, this Write(ReadOnlySpan<byte>) overload should use the behavior
+ // of Write(byte[],int,int) overload. Or if the stream is in async mode, we can't call the
+ // synchronous WriteSpan, so we similarly call the base Write, which will turn delegate to
+ // Write(byte[],int,int), which will do the right thing if we're in async mode.
+ base.Write(destination);
+ }
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
@@ -334,7 +448,7 @@ namespace System.IO
// since it does not call through to Write() or WriteAsync() which a subclass might have overridden.
// To be safe we will only use this implementation in cases where we know it is safe to do so,
// and delegate to our base class (which will call into Write/WriteAsync) when we are not sure.
- if (GetType() != typeof(FileStream))
+ if (!_useAsyncIO || GetType() != typeof(FileStream))
return base.WriteAsync(buffer, offset, count, cancellationToken);
if (cancellationToken.IsCancellationRequested)
@@ -343,7 +457,30 @@ namespace System.IO
if (IsClosed)
throw Error.GetFileNotOpen();
- return WriteAsyncInternal(buffer, offset, count, cancellationToken);
+ return WriteAsyncInternal(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken);
+ }
+
+ public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (!_useAsyncIO || GetType() != typeof(FileStream))
+ {
+ // If we're not using async I/O, delegate to the base, which will queue a call to Write.
+ // Or if this isn't a concrete FileStream, a derived type may have overridden WriteAsync(byte[],...),
+ // which was introduced first, so delegate to the base which will delegate to that.
+ return base.WriteAsync(source, cancellationToken);
+ }
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled<int>(cancellationToken);
+ }
+
+ if (IsClosed)
+ {
+ throw Error.GetFileNotOpen();
+ }
+
+ return WriteAsyncInternal(source, cancellationToken);
}
/// <summary>
@@ -594,7 +731,11 @@ namespace System.IO
_readPos = _readLength = 0;
}
- private int ReadByteCore()
+ /// <summary>
+ /// Reads a byte from the file stream. Returns the byte cast to an int
+ /// or -1 if reading from the end of the stream.
+ /// </summary>
+ public override int ReadByte()
{
PrepareForReading();
@@ -602,9 +743,7 @@ namespace System.IO
if (_readPos == _readLength)
{
FlushWriteBuffer();
- Debug.Assert(_bufferLength > 0, "_bufferSize > 0");
-
- _readLength = ReadNative(buffer, 0, _bufferLength);
+ _readLength = FillReadBufferForReadByte();
_readPos = 0;
if (_readLength == 0)
{
@@ -615,13 +754,18 @@ namespace System.IO
return buffer[_readPos++];
}
- private void WriteByteCore(byte value)
+ /// <summary>
+ /// Writes a byte to the current position in the stream and advances the position
+ /// within the stream by one byte.
+ /// </summary>
+ /// <param name="value">The byte to write to the stream.</param>
+ public override void WriteByte(byte value)
{
PrepareForWriting();
// Flush the write buffer if it's full
if (_writePos == _bufferLength)
- FlushWriteBuffer();
+ FlushWriteBufferForWriteByte();
// We now have space in the buffer. Store the byte.
GetBuffer()[_writePos++] = value;
@@ -672,7 +816,7 @@ namespace System.IO
if (!IsAsync)
return base.BeginRead(array, offset, numBytes, callback, state);
else
- return TaskToApm.Begin(ReadAsyncInternal(array, offset, numBytes, CancellationToken.None), callback, state);
+ return TaskToApm.Begin(ReadAsyncTask(array, offset, numBytes, CancellationToken.None), callback, state);
}
public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback callback, object state)
@@ -692,7 +836,7 @@ namespace System.IO
if (!IsAsync)
return base.BeginWrite(array, offset, numBytes, callback, state);
else
- return TaskToApm.Begin(WriteAsyncInternal(array, offset, numBytes, CancellationToken.None), callback, state);
+ return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory<byte>(array, offset, numBytes), CancellationToken.None), callback, state);
}
public override int EndRead(IAsyncResult asyncResult)
diff --git a/src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs b/src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs
index 7dca13335e..e3871bcd6c 100644
--- a/src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs
+++ b/src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs
@@ -2,11 +2,11 @@
// 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.Security;
+using System.Buffers;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
-using System.Runtime.InteropServices;
-using System.Diagnostics;
namespace System.IO
{
@@ -15,7 +15,7 @@ namespace System.IO
// This is an internal object extending TaskCompletionSource with fields
// for all of the relevant data necessary to complete the IO operation.
// This is used by IOCallback and all of the async methods.
- unsafe private sealed class FileStreamCompletionSource : TaskCompletionSource<int>
+ private unsafe class FileStreamCompletionSource : TaskCompletionSource<int>
{
private const long NoResult = 0;
private const long ResultSuccess = (long)1 << 32;
@@ -28,7 +28,6 @@ namespace System.IO
private readonly FileStream _stream;
private readonly int _numBufferedBytes;
- private readonly CancellationToken _cancellationToken;
private CancellationTokenRegistration _cancellationRegistration;
#if DEBUG
private bool _cancellationHasBeenRegistered;
@@ -37,20 +36,19 @@ namespace System.IO
private long _result; // Using long since this needs to be used in Interlocked APIs
// Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations)
- internal FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[] bytes, CancellationToken cancellationToken)
+ internal FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[] bytes)
: base(TaskCreationOptions.RunContinuationsAsynchronously)
{
_numBufferedBytes = numBufferedBytes;
_stream = stream;
_result = NoResult;
- _cancellationToken = cancellationToken;
- // Create the native overlapped. We try to use the preallocated overlapped if possible:
- // it's possible if the byte buffer is the same one that's associated with the preallocated overlapped
- // and if no one else is currently using the preallocated overlapped. This is the fast-path for cases
- // where the user-provided buffer is smaller than the FileStream's buffer (such that the FileStream's
+ // Create the native overlapped. We try to use the preallocated overlapped if possible: it's possible if the byte
+ // buffer is null (there's nothing to pin) or the same one that's associated with the preallocated overlapped (and
+ // thus is already pinned) and if no one else is currently using the preallocated overlapped. This is the fast-path
+ // for cases where the user-provided buffer is smaller than the FileStream's buffer (such that the FileStream's
// buffer is used) and where operations on the FileStream are not being performed concurrently.
- _overlapped = ReferenceEquals(bytes, _stream._buffer) && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ?
+ _overlapped = (bytes == null || ReferenceEquals(bytes, _stream._buffer)) && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ?
_stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(_stream._preallocatedOverlapped) :
_stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(s_ioCallback, this, bytes);
Debug.Assert(_overlapped != null, "AllocateNativeOverlapped returned null");
@@ -67,15 +65,16 @@ namespace System.IO
TrySetResult(numBytes + _numBufferedBytes);
}
- public void RegisterForCancellation()
+ public void RegisterForCancellation(CancellationToken cancellationToken)
{
#if DEBUG
+ Debug.Assert(cancellationToken.CanBeCanceled);
Debug.Assert(!_cancellationHasBeenRegistered, "Cannot register for cancellation twice");
_cancellationHasBeenRegistered = true;
#endif
- // Quick check to make sure that the cancellation token supports cancellation, and that the IO hasn't completed
- if ((_cancellationToken.CanBeCanceled) && (_overlapped != null))
+ // Quick check to make sure the IO hasn't completed
+ if (_overlapped != null)
{
var cancelCallback = s_cancelCallback;
if (cancelCallback == null) s_cancelCallback = cancelCallback = Cancel;
@@ -84,7 +83,7 @@ namespace System.IO
long packedResult = Interlocked.CompareExchange(ref _result, RegisteringCancellation, NoResult);
if (packedResult == NoResult)
{
- _cancellationRegistration = _cancellationToken.Register(cancelCallback, this);
+ _cancellationRegistration = cancellationToken.Register(cancelCallback, this);
// Switch the result, just in case IO completed while we were setting the registration
packedResult = Interlocked.Exchange(ref _result, NoResult);
@@ -104,7 +103,7 @@ namespace System.IO
}
}
- internal void ReleaseNativeResource()
+ internal virtual void ReleaseNativeResource()
{
// Ensure that cancellation has been completed and cleaned up.
_cancellationRegistration.Dispose();
@@ -172,6 +171,7 @@ namespace System.IO
private void CompleteCallback(ulong packedResult)
{
// Free up the native resource and cancellation registration
+ CancellationToken cancellationToken = _cancellationRegistration.Token; // access before disposing registration
ReleaseNativeResource();
// Unpack the result and send it to the user
@@ -181,7 +181,7 @@ namespace System.IO
int errorCode = unchecked((int)(packedResult & uint.MaxValue));
if (errorCode == Interop.Errors.ERROR_OPERATION_ABORTED)
{
- TrySetCanceled(_cancellationToken.IsCancellationRequested ? _cancellationToken : new CancellationToken(true));
+ TrySetCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true));
}
else
{
@@ -218,5 +218,28 @@ namespace System.IO
}
}
}
+
+ /// <summary>
+ /// Extends <see cref="FileStreamCompletionSource"/> with to support disposing of a
+ /// <see cref="MemoryHandle"/> when the operation has completed. This should only be used
+ /// when memory doesn't wrap a byte[].
+ /// </summary>
+ private sealed class MemoryFileStreamCompletionSource : FileStreamCompletionSource
+ {
+ private MemoryHandle _handle; // mutable struct; do not make this readonly
+
+ internal MemoryFileStreamCompletionSource(FileStream stream, int numBufferedBytes, ReadOnlyMemory<byte> memory) :
+ base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes
+ {
+ Debug.Assert(!memory.DangerousTryGetArray(out ArraySegment<byte> array), "The base should be used directly if we can get the array.");
+ _handle = memory.Retain(pin: true);
+ }
+
+ internal override void ReleaseNativeResource()
+ {
+ _handle.Dispose();
+ base.ReleaseNativeResource();
+ }
+ }
}
}
diff --git a/src/mscorlib/shared/System/IO/IOException.cs b/src/mscorlib/shared/System/IO/IOException.cs
new file mode 100644
index 0000000000..1fbd352613
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/IOException.cs
@@ -0,0 +1,41 @@
+// 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.Serialization;
+
+namespace System.IO
+{
+ public class IOException : SystemException
+ {
+ public IOException()
+ : base(SR.Arg_IOException)
+ {
+ HResult = HResults.COR_E_IO;
+ }
+
+ public IOException(String message)
+ : base(message)
+ {
+ HResult = HResults.COR_E_IO;
+ }
+
+ public IOException(String message, int hresult)
+ : base(message)
+ {
+ HResult = hresult;
+ }
+
+ public IOException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = HResults.COR_E_IO;
+ }
+
+ protected IOException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ throw new PlatformNotSupportedException();
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/Path.cs b/src/mscorlib/shared/System/IO/Path.cs
index 135bb42881..2676f0d2fe 100644
--- a/src/mscorlib/shared/System/IO/Path.cs
+++ b/src/mscorlib/shared/System/IO/Path.cs
@@ -166,6 +166,30 @@ namespace System.IO
return new string(pRandomFileName, 0, RandomFileNameLength);
}
+ /// <summary>
+ /// Returns true if the path is fixed to a specific drive or UNC path. This method does no
+ /// validation of the path (URIs will be returned as relative as a result).
+ /// Returns false if the path specified is relative to the current drive or working directory.
+ /// </summary>
+ /// <remarks>
+ /// Handles paths that use the alternate directory separator. It is a frequent mistake to
+ /// assume that rooted paths <see cref="Path.IsPathRooted(string)"/> are not relative. This isn't the case.
+ /// "C:a" is drive relative- meaning that it will be resolved against the current directory
+ /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory
+ /// will not be used to modify the path).
+ /// </remarks>
+ /// <exception cref="ArgumentNullException">
+ /// Thrown if <paramref name="path"/> is null.
+ /// </exception>
+ public static bool IsPathFullyQualified(string path)
+ {
+ if (path == null)
+ {
+ throw new ArgumentNullException(nameof(path));
+ }
+ return !PathInternal.IsPartiallyQualified(path);
+ }
+
// Tests if a path includes a file extension. The result is
// true if the characters that follow the last directory
// separator ('\\' or '/') or volume separator (':') in the path include
diff --git a/src/mscorlib/shared/System/IO/PathInternal.Windows.cs b/src/mscorlib/shared/System/IO/PathInternal.Windows.cs
index 01069e757c..2ab3928608 100644
--- a/src/mscorlib/shared/System/IO/PathInternal.Windows.cs
+++ b/src/mscorlib/shared/System/IO/PathInternal.Windows.cs
@@ -437,11 +437,6 @@ namespace System.IO
return IsDirectorySeparator(ch) || VolumeSeparatorChar == ch;
}
- internal static string TrimEndingDirectorySeparator(string path) =>
- EndsInDirectorySeparator(path) ?
- path.Substring(0, path.Length - 1) :
- path;
-
/// <summary>
/// Returns true if the path is effectively empty for the current OS.
/// For unix, this is empty or null. For Windows, this is empty, null, or
diff --git a/src/mscorlib/shared/System/IO/PathTooLongException.cs b/src/mscorlib/shared/System/IO/PathTooLongException.cs
index 64c8e6c7e6..15f282ebf6 100644
--- a/src/mscorlib/shared/System/IO/PathTooLongException.cs
+++ b/src/mscorlib/shared/System/IO/PathTooLongException.cs
@@ -13,19 +13,19 @@ namespace System.IO
public PathTooLongException()
: base(SR.IO_PathTooLong)
{
- HResult = __HResults.COR_E_PATHTOOLONG;
+ HResult = HResults.COR_E_PATHTOOLONG;
}
public PathTooLongException(string message)
: base(message)
{
- HResult = __HResults.COR_E_PATHTOOLONG;
+ HResult = HResults.COR_E_PATHTOOLONG;
}
public PathTooLongException(string message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_PATHTOOLONG;
+ HResult = HResults.COR_E_PATHTOOLONG;
}
protected PathTooLongException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/IO/PinnedBufferMemoryStream.cs b/src/mscorlib/shared/System/IO/PinnedBufferMemoryStream.cs
index c8e720b7ac..e8f74dd05c 100644
--- a/src/mscorlib/shared/System/IO/PinnedBufferMemoryStream.cs
+++ b/src/mscorlib/shared/System/IO/PinnedBufferMemoryStream.cs
@@ -46,6 +46,10 @@ namespace System.IO
Initialize(ptr, len, len, FileAccess.Read);
}
+ public override int Read(Span<byte> destination) => ReadCore(destination);
+
+ public override void Write(ReadOnlySpan<byte> source) => WriteCore(source);
+
~PinnedBufferMemoryStream()
{
Dispose(false);
diff --git a/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs b/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs
index b78f50fe7b..b899951ba7 100644
--- a/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs
+++ b/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs
@@ -361,8 +361,27 @@ namespace System.IO
throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
if (buffer.Length - offset < count)
throw new ArgumentException(SR.Argument_InvalidOffLen);
- Contract.EndContractBlock(); // Keep this in sync with contract validation in ReadAsync
+ return ReadCore(new Span<byte>(buffer, offset, count));
+ }
+
+ public override int Read(Span<byte> destination)
+ {
+ if (GetType() == typeof(UnmanagedMemoryStream))
+ {
+ return ReadCore(destination);
+ }
+ else
+ {
+ // UnmanagedMemoryStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior
+ // to this Read(Span<byte>) overload being introduced. In that case, this Read(Span<byte>) overload
+ // should use the behavior of Read(byte[],int,int) overload.
+ return base.Read(destination);
+ }
+ }
+
+ internal int ReadCore(Span<byte> destination)
+ {
if (!_isOpen) throw Error.GetStreamIsClosed();
if (!CanRead) throw Error.GetReadNotSupported();
@@ -370,20 +389,22 @@ namespace System.IO
// changes our position after we decide we can read some bytes.
long pos = Interlocked.Read(ref _position);
long len = Interlocked.Read(ref _length);
- long n = len - pos;
- if (n > count)
- n = count;
+ long n = Math.Min(len - pos, destination.Length);
if (n <= 0)
+ {
return 0;
+ }
int nInt = (int)n; // Safe because n <= count, which is an Int32
if (nInt < 0)
+ {
return 0; // _position could be beyond EOF
+ }
Debug.Assert(pos + nInt >= 0, "_position + n >= 0"); // len is less than 2^63 -1.
unsafe
{
- fixed (byte* pBuffer = buffer)
+ fixed (byte* pBuffer = &destination.DangerousGetPinnableReference())
{
if (_buffer != null)
{
@@ -393,7 +414,7 @@ namespace System.IO
try
{
_buffer.AcquirePointer(ref pointer);
- Buffer.Memcpy(pBuffer + offset, pointer + pos + _offset, nInt);
+ Buffer.Memcpy(pBuffer, pointer + pos + _offset, nInt);
}
finally
{
@@ -405,7 +426,7 @@ namespace System.IO
}
else
{
- Buffer.Memcpy(pBuffer + offset, _mem + pos, nInt);
+ Buffer.Memcpy(pBuffer, _mem + pos, nInt);
}
}
}
@@ -450,6 +471,28 @@ namespace System.IO
}
/// <summary>
+ /// Reads bytes from stream and puts them into the buffer
+ /// </summary>
+ /// <param name="destination">Buffer to read the bytes to.</param>
+ /// <param name="cancellationToken">Token that can be used to cancel this operation.</param>
+ public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
+ }
+
+ try
+ {
+ return new ValueTask<int>(Read(destination.Span));
+ }
+ catch (Exception ex)
+ {
+ return new ValueTask<int>(Task.FromException<int>(ex));
+ }
+ }
+
+ /// <summary>
/// Returns the byte at the stream current Position and advances the Position.
/// </summary>
/// <returns></returns>
@@ -583,17 +626,38 @@ namespace System.IO
throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
if (buffer.Length - offset < count)
throw new ArgumentException(SR.Argument_InvalidOffLen);
- Contract.EndContractBlock(); // Keep contract validation in sync with WriteAsync(..)
+ WriteCore(new Span<byte>(buffer, offset, count));
+ }
+
+ public override void Write(ReadOnlySpan<byte> source)
+ {
+ if (GetType() == typeof(UnmanagedMemoryStream))
+ {
+ WriteCore(source);
+ }
+ else
+ {
+ // UnmanagedMemoryStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior
+ // to this Write(Span<byte>) overload being introduced. In that case, this Write(Span<byte>) overload
+ // should use the behavior of Write(byte[],int,int) overload.
+ base.Write(source);
+ }
+ }
+
+ internal unsafe void WriteCore(ReadOnlySpan<byte> source)
+ {
if (!_isOpen) throw Error.GetStreamIsClosed();
if (!CanWrite) throw Error.GetWriteNotSupported();
long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
long len = Interlocked.Read(ref _length);
- long n = pos + count;
+ long n = pos + source.Length;
// Check for overflow
if (n < 0)
+ {
throw new IOException(SR.IO_StreamTooLong);
+ }
if (n > _capacity)
{
@@ -606,10 +670,7 @@ namespace System.IO
// zero any memory in the middle.
if (pos > len)
{
- unsafe
- {
- Buffer.ZeroMemory(_mem + len, pos - len);
- }
+ Buffer.ZeroMemory(_mem + len, pos - len);
}
// set length after zeroing memory to avoid race condition of accessing unzeroed memory
@@ -619,39 +680,37 @@ namespace System.IO
}
}
- unsafe
+ fixed (byte* pBuffer = &source.DangerousGetPinnableReference())
{
- fixed (byte* pBuffer = buffer)
+ if (_buffer != null)
{
- if (_buffer != null)
+ long bytesLeft = _capacity - pos;
+ if (bytesLeft < source.Length)
{
- long bytesLeft = _capacity - pos;
- if (bytesLeft < count)
- {
- throw new ArgumentException(SR.Arg_BufferTooSmall);
- }
+ throw new ArgumentException(SR.Arg_BufferTooSmall);
+ }
- byte* pointer = null;
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- _buffer.AcquirePointer(ref pointer);
- Buffer.Memcpy(pointer + pos + _offset, pBuffer + offset, count);
- }
- finally
- {
- if (pointer != null)
- {
- _buffer.ReleasePointer();
- }
- }
+ byte* pointer = null;
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try
+ {
+ _buffer.AcquirePointer(ref pointer);
+ Buffer.Memcpy(pointer + pos + _offset, pBuffer, source.Length);
}
- else
+ finally
{
- Buffer.Memcpy(_mem + pos, pBuffer + offset, count);
+ if (pointer != null)
+ {
+ _buffer.ReleasePointer();
+ }
}
}
+ else
+ {
+ Buffer.Memcpy(_mem + pos, pBuffer, source.Length);
+ }
}
+
Interlocked.Exchange(ref _position, n);
return;
}
@@ -692,6 +751,29 @@ namespace System.IO
}
/// <summary>
+ /// Writes buffer into the stream. The operation completes synchronously.
+ /// </summary>
+ /// <param name="buffer">Buffer that will be written.</param>
+ /// <param name="cancellationToken">Token that can be used to cancel the operation.</param>
+ public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+
+ try
+ {
+ Write(source.Span);
+ return Task.CompletedTask;
+ }
+ catch (Exception ex)
+ {
+ return Task.FromException(ex);
+ }
+ }
+
+ /// <summary>
/// Writes a byte to the stream and advances the current Position.
/// </summary>
/// <param name="value"></param>
diff --git a/src/mscorlib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs b/src/mscorlib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs
index d547e771d7..2699912ea1 100644
--- a/src/mscorlib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs
+++ b/src/mscorlib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs
@@ -114,6 +114,11 @@ namespace System.IO
return _unmanagedStream.Read(buffer, offset, count);
}
+ public override int Read(Span<byte> destination)
+ {
+ return _unmanagedStream.Read(destination);
+ }
+
public override int ReadByte()
{
return _unmanagedStream.ReadByte();
@@ -136,6 +141,11 @@ namespace System.IO
_unmanagedStream.Write(buffer, offset, count);
}
+ public override void Write(ReadOnlySpan<byte> source)
+ {
+ _unmanagedStream.Write(source);
+ }
+
public override void WriteByte(byte value)
{
_unmanagedStream.WriteByte(value);
@@ -200,11 +210,21 @@ namespace System.IO
return _unmanagedStream.ReadAsync(buffer, offset, count, cancellationToken);
}
+ public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return _unmanagedStream.ReadAsync(destination, cancellationToken);
+ }
+
public override Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
{
return _unmanagedStream.WriteAsync(buffer, offset, count, cancellationToken);
}
+
+ public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return _unmanagedStream.WriteAsync(source, cancellationToken);
+ }
} // class UnmanagedMemoryStreamWrapper
} // namespace
diff --git a/src/mscorlib/shared/System/IO/Win32Marshal.cs b/src/mscorlib/shared/System/IO/Win32Marshal.cs
index a24409ed03..14a064a700 100644
--- a/src/mscorlib/shared/System/IO/Win32Marshal.cs
+++ b/src/mscorlib/shared/System/IO/Win32Marshal.cs
@@ -63,10 +63,16 @@ namespace System.IO
return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path), MakeHRFromErrorCode(errorCode));
case Interop.Errors.ERROR_FILENAME_EXCED_RANGE:
- return new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, path));
+ if (path.Length == 0)
+ return new PathTooLongException(SR.IO_PathTooLong);
+ else
+ return new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, path));
+
+ case Interop.Errors.ERROR_INVALID_DRIVE:
+ throw new DriveNotFoundException(SR.Format(SR.IO_DriveNotFound_Drive, path));
case Interop.Errors.ERROR_INVALID_PARAMETER:
- return new IOException(GetMessage(errorCode), MakeHRFromErrorCode(errorCode));
+ return new IOException(Interop.Kernel32.GetMessage(errorCode), MakeHRFromErrorCode(errorCode));
case Interop.Errors.ERROR_SHARING_VIOLATION:
if (path.Length == 0)
@@ -84,7 +90,7 @@ namespace System.IO
return new OperationCanceledException();
default:
- return new IOException(GetMessage(errorCode), MakeHRFromErrorCode(errorCode));
+ return new IOException(Interop.Kernel32.GetMessage(errorCode), MakeHRFromErrorCode(errorCode));
}
}
@@ -97,13 +103,5 @@ namespace System.IO
return unchecked(((int)0x80070000) | errorCode);
}
-
- /// <summary>
- /// Returns a string message for the specified Win32 error code.
- /// </summary>
- internal static string GetMessage(int errorCode)
- {
- return Interop.Kernel32.GetMessage(errorCode);
- }
}
}
diff --git a/src/mscorlib/shared/System/IndexOutOfRangeException.cs b/src/mscorlib/shared/System/IndexOutOfRangeException.cs
index d5b24b35d5..bec63d73d6 100644
--- a/src/mscorlib/shared/System/IndexOutOfRangeException.cs
+++ b/src/mscorlib/shared/System/IndexOutOfRangeException.cs
@@ -20,19 +20,19 @@ namespace System
public IndexOutOfRangeException()
: base(SR.Arg_IndexOutOfRangeException)
{
- HResult = __HResults.COR_E_INDEXOUTOFRANGE;
+ HResult = HResults.COR_E_INDEXOUTOFRANGE;
}
public IndexOutOfRangeException(String message)
: base(message)
{
- HResult = __HResults.COR_E_INDEXOUTOFRANGE;
+ HResult = HResults.COR_E_INDEXOUTOFRANGE;
}
public IndexOutOfRangeException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_INDEXOUTOFRANGE;
+ HResult = HResults.COR_E_INDEXOUTOFRANGE;
}
}
}
diff --git a/src/mscorlib/shared/System/InsufficientExecutionStackException.cs b/src/mscorlib/shared/System/InsufficientExecutionStackException.cs
index 41df3ae970..a14cb67150 100644
--- a/src/mscorlib/shared/System/InsufficientExecutionStackException.cs
+++ b/src/mscorlib/shared/System/InsufficientExecutionStackException.cs
@@ -11,19 +11,19 @@ namespace System
public InsufficientExecutionStackException()
: base(SR.Arg_InsufficientExecutionStackException)
{
- HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK;
+ HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK;
}
public InsufficientExecutionStackException(String message)
: base(message)
{
- HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK;
+ HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK;
}
public InsufficientExecutionStackException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK;
+ HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK;
}
}
}
diff --git a/src/mscorlib/src/System/Int16.cs b/src/mscorlib/shared/System/Int16.cs
index 82e5dd5a38..6aaf4280d3 100644
--- a/src/mscorlib/src/System/Int16.cs
+++ b/src/mscorlib/shared/System/Int16.cs
@@ -12,19 +12,18 @@
**
===========================================================*/
-
-using System;
+using System.Diagnostics.Contracts;
using System.Globalization;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Diagnostics.Contracts;
+using System.Runtime.Versioning;
namespace System
{
[Serializable]
- [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct Int16 : IComparable, IFormattable, IConvertible
- , IComparable<Int16>, IEquatable<Int16>
+ [StructLayout(LayoutKind.Sequential)]
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public struct Int16 : IComparable, IConvertible, IFormattable, IComparable<Int16>, IEquatable<Int16>
{
private short m_value; // Do not rename (binary serialization)
@@ -66,7 +65,7 @@ namespace System
return m_value == ((Int16)obj).m_value;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public bool Equals(Int16 obj)
{
return m_value == obj;
@@ -117,27 +116,37 @@ namespace System
public static short Parse(String s)
{
- return Parse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
public static short Parse(String s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
- return Parse(s, style, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo);
}
public static short Parse(String s, IFormatProvider provider)
{
- return Parse(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
public static short Parse(String s, NumberStyles style, IFormatProvider provider)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ public static short Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Parse(s, style, NumberFormatInfo.GetInstance(provider));
}
- private static short Parse(String s, NumberStyles style, NumberFormatInfo info)
+ private static short Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
{
int i = 0;
try
@@ -166,16 +175,35 @@ namespace System
public static bool TryParse(String s, out Int16 result)
{
- return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return TryParse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Int16 result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ public static bool TryParse(ReadOnlySpan<char> s, out Int16 result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result);
}
- private static bool TryParse(String s, NumberStyles style, NumberFormatInfo info, out Int16 result)
+ private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out Int16 result)
{
result = 0;
int i;
diff --git a/src/mscorlib/src/System/Int32.cs b/src/mscorlib/shared/System/Int32.cs
index 503552117d..f5e832a274 100644
--- a/src/mscorlib/src/System/Int32.cs
+++ b/src/mscorlib/shared/System/Int32.cs
@@ -12,19 +12,18 @@
**
===========================================================*/
-using System;
+using System.Diagnostics.Contracts;
using System.Globalization;
-using System.Runtime;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Diagnostics.Contracts;
+using System.Runtime.Versioning;
namespace System
{
[Serializable]
- [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct Int32 : IComparable, IFormattable, IConvertible
- , IComparable<Int32>, IEquatable<Int32>
+ [StructLayout(LayoutKind.Sequential)]
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public struct Int32 : IComparable, IConvertible, IFormattable, IComparable<Int32>, IEquatable<Int32>
{
private int m_value; // Do not rename (binary serialization)
@@ -33,8 +32,11 @@ namespace System
// Compares this object to another object, returning an integer that
// indicates the relationship.
- // Returns a value less than zero if this object
- // null is considered to be less than any instance.
+ // Returns :
+ // 0 if the values are equal
+ // Negative number if _value is less than value
+ // Positive number if _value is more than value
+ // null is considered to be less than any instance, hence returns positive number
// If object is not of type Int32, this method throws an ArgumentException.
//
public int CompareTo(Object value)
@@ -45,8 +47,8 @@ namespace System
}
if (value is Int32)
{
- // Need to use compare because subtraction will wrap
- // to positive for very large neg numbers, etc.
+ // NOTE: Cannot use return (_value - value) as this causes a wrap
+ // around in cases where _value - value > MaxValue.
int i = (int)value;
if (m_value < i) return -1;
if (m_value > i) return 1;
@@ -57,8 +59,8 @@ namespace System
public int CompareTo(int value)
{
- // Need to use compare because subtraction will wrap
- // to positive for very large neg numbers, etc.
+ // NOTE: Cannot use return (_value - value) as this causes a wrap
+ // around in cases where _value - value > MaxValue.
if (m_value < value) return -1;
if (m_value > value) return 1;
return 0;
@@ -73,7 +75,7 @@ namespace System
return m_value == ((Int32)obj).m_value;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public bool Equals(Int32 obj)
{
return m_value == obj;
@@ -116,14 +118,16 @@ namespace System
[Pure]
public static int Parse(String s)
{
- return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
[Pure]
public static int Parse(String s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
- return Number.ParseInt32(s, style, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo);
}
// Parses an integer from a String in the given style. If
@@ -133,7 +137,8 @@ namespace System
[Pure]
public static int Parse(String s, IFormatProvider provider)
{
- return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
// Parses an integer from a String in the given style. If
@@ -144,6 +149,13 @@ namespace System
public static int Parse(String s, NumberStyles style, IFormatProvider provider)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ public static int Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.ParseInt32(s, style, NumberFormatInfo.GetInstance(provider));
}
@@ -153,7 +165,13 @@ namespace System
[Pure]
public static bool TryParse(String s, out Int32 result)
{
- return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return Number.TryParseInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
// Parses an integer from a String in the given style. Returns false rather
@@ -163,6 +181,19 @@ namespace System
public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Int32 result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return Number.TryParseInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ public static bool TryParse(ReadOnlySpan<char> s, out int result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result);
}
diff --git a/src/mscorlib/src/System/Int64.cs b/src/mscorlib/shared/System/Int64.cs
index 015387233c..785fa39fa5 100644
--- a/src/mscorlib/src/System/Int64.cs
+++ b/src/mscorlib/shared/System/Int64.cs
@@ -12,18 +12,18 @@
**
===========================================================*/
-using System;
+using System.Diagnostics.Contracts;
using System.Globalization;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Diagnostics.Contracts;
+using System.Runtime.Versioning;
namespace System
{
[Serializable]
- [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct Int64 : IComparable, IFormattable, IConvertible
- , IComparable<Int64>, IEquatable<Int64>
+ [StructLayout(LayoutKind.Sequential)]
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public struct Int64 : IComparable, IConvertible, IFormattable, IComparable<Int64>, IEquatable<Int64>
{
private long m_value; // Do not rename (binary serialization)
@@ -72,7 +72,7 @@ namespace System
return m_value == ((Int64)obj).m_value;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public bool Equals(Int64 obj)
{
return m_value == obj;
@@ -110,18 +110,21 @@ namespace System
public static long Parse(String s)
{
- return Number.ParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
public static long Parse(String s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
- return Number.ParseInt64(s, style, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo);
}
public static long Parse(String s, IFormatProvider provider)
{
- return Number.ParseInt64(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
@@ -132,17 +135,43 @@ namespace System
public static long Parse(String s, NumberStyles style, IFormatProvider provider)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ public static long Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.ParseInt64(s, style, NumberFormatInfo.GetInstance(provider));
}
public static Boolean TryParse(String s, out Int64 result)
{
- return Number.TryParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return Number.TryParseInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Int64 result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return Number.TryParseInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ public static bool TryParse(ReadOnlySpan<char> s, out long result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result);
}
diff --git a/src/mscorlib/shared/System/InvalidCastException.cs b/src/mscorlib/shared/System/InvalidCastException.cs
index cf359ac0b5..00b393c60e 100644
--- a/src/mscorlib/shared/System/InvalidCastException.cs
+++ b/src/mscorlib/shared/System/InvalidCastException.cs
@@ -17,19 +17,19 @@ namespace System
public InvalidCastException()
: base(SR.Arg_InvalidCastException)
{
- HResult = __HResults.COR_E_INVALIDCAST;
+ HResult = HResults.COR_E_INVALIDCAST;
}
public InvalidCastException(String message)
: base(message)
{
- HResult = __HResults.COR_E_INVALIDCAST;
+ HResult = HResults.COR_E_INVALIDCAST;
}
public InvalidCastException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_INVALIDCAST;
+ HResult = HResults.COR_E_INVALIDCAST;
}
public InvalidCastException(String message, int errorCode)
diff --git a/src/mscorlib/shared/System/InvalidOperationException.cs b/src/mscorlib/shared/System/InvalidOperationException.cs
index ad743e05ff..74a4ffd74a 100644
--- a/src/mscorlib/shared/System/InvalidOperationException.cs
+++ b/src/mscorlib/shared/System/InvalidOperationException.cs
@@ -21,19 +21,19 @@ namespace System
public InvalidOperationException()
: base(SR.Arg_InvalidOperationException)
{
- HResult = __HResults.COR_E_INVALIDOPERATION;
+ HResult = HResults.COR_E_INVALIDOPERATION;
}
public InvalidOperationException(String message)
: base(message)
{
- HResult = __HResults.COR_E_INVALIDOPERATION;
+ HResult = HResults.COR_E_INVALIDOPERATION;
}
public InvalidOperationException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_INVALIDOPERATION;
+ HResult = HResults.COR_E_INVALIDOPERATION;
}
protected InvalidOperationException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/InvalidProgramException.cs b/src/mscorlib/shared/System/InvalidProgramException.cs
index 47e7325836..e3521574ea 100644
--- a/src/mscorlib/shared/System/InvalidProgramException.cs
+++ b/src/mscorlib/shared/System/InvalidProgramException.cs
@@ -20,19 +20,19 @@ namespace System
public InvalidProgramException()
: base(SR.InvalidProgram_Default)
{
- HResult = __HResults.COR_E_INVALIDPROGRAM;
+ HResult = HResults.COR_E_INVALIDPROGRAM;
}
public InvalidProgramException(String message)
: base(message)
{
- HResult = __HResults.COR_E_INVALIDPROGRAM;
+ HResult = HResults.COR_E_INVALIDPROGRAM;
}
public InvalidProgramException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_INVALIDPROGRAM;
+ HResult = HResults.COR_E_INVALIDPROGRAM;
}
}
}
diff --git a/src/mscorlib/shared/System/Lazy.cs b/src/mscorlib/shared/System/Lazy.cs
index 5d68714c7e..2ef3cd2e5f 100644
--- a/src/mscorlib/shared/System/Lazy.cs
+++ b/src/mscorlib/shared/System/Lazy.cs
@@ -261,7 +261,7 @@ namespace System
/// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/>
/// class that uses <typeparamref name="T"/>'s default constructor and a specified thread-safety mode.
/// </summary>
- /// <param name="mode">The lazy thread-safety mode mode</param>
+ /// <param name="mode">The lazy thread-safety mode</param>
/// <exception cref="System.ArgumentOutOfRangeException"><paramref name="mode"/> mode contains an invalid valuee</exception>
public Lazy(LazyThreadSafetyMode mode) :
this(null, mode, useDefaultConstructor:true)
diff --git a/src/mscorlib/shared/System/MemberAccessException.cs b/src/mscorlib/shared/System/MemberAccessException.cs
index abca952f19..bb26d9e3c4 100644
--- a/src/mscorlib/shared/System/MemberAccessException.cs
+++ b/src/mscorlib/shared/System/MemberAccessException.cs
@@ -23,7 +23,7 @@ namespace System
public MemberAccessException()
: base(SR.Arg_AccessException)
{
- HResult = __HResults.COR_E_MEMBERACCESS;
+ HResult = HResults.COR_E_MEMBERACCESS;
}
// Creates a new MemberAccessException with its message string set to
@@ -33,13 +33,13 @@ namespace System
public MemberAccessException(String message)
: base(message)
{
- HResult = __HResults.COR_E_MEMBERACCESS;
+ HResult = HResults.COR_E_MEMBERACCESS;
}
public MemberAccessException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_MEMBERACCESS;
+ HResult = HResults.COR_E_MEMBERACCESS;
}
protected MemberAccessException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/Memory.cs b/src/mscorlib/shared/System/Memory.cs
new file mode 100644
index 0000000000..270f88e4bc
--- /dev/null
+++ b/src/mscorlib/shared/System/Memory.cs
@@ -0,0 +1,284 @@
+// 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 EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
+using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
+using System.Diagnostics;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace System
+{
+ public struct Memory<T>
+ {
+ // The highest order bit of _index is used to discern whether _arrayOrOwnedMemory is an array or an owned memory
+ // if (_index >> 31) == 1, object _arrayOrOwnedMemory is an OwnedMemory<T>
+ // else, object _arrayOrOwnedMemory is a T[]
+ private readonly object _arrayOrOwnedMemory;
+ private readonly int _index;
+ private readonly int _length;
+
+ private const int RemoveOwnedFlagBitMask = 0x7FFFFFFF;
+
+ /// <summary>
+ /// Creates a new memory over the entirety of the target array.
+ /// </summary>
+ /// <param name="array">The target array.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
+ /// reference (Nothing in Visual Basic).</exception>
+ /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Memory(T[] array)
+ {
+ if (array == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException();
+
+ _arrayOrOwnedMemory = array;
+ _index = 0;
+ _length = array.Length;
+ }
+
+ /// <summary>
+ /// Creates a new memory over the portion of the target array beginning
+ /// at 'start' index and ending at 'end' index (exclusive).
+ /// </summary>
+ /// <param name="array">The target array.</param>
+ /// <param name="start">The index at which to begin the memory.</param>
+ /// <param name="length">The number of items in the memory.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
+ /// reference (Nothing in Visual Basic).</exception>
+ /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Memory(T[] array, int start, int length)
+ {
+ if (array == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException();
+ if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ _arrayOrOwnedMemory = array;
+ _index = start;
+ _length = length;
+ }
+
+ // Constructor for internal use only.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal Memory(OwnedMemory<T> owner, int index, int length)
+ {
+ if (owner == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ownedMemory);
+ if (index < 0 || length < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ _arrayOrOwnedMemory = owner;
+ _index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask
+ _length = length;
+ }
+
+ /// <summary>
+ /// Defines an implicit conversion of an array to a <see cref="Memory{T}"/>
+ /// </summary>
+ public static implicit operator Memory<T>(T[] array) => new Memory<T>(array);
+
+ /// <summary>
+ /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Memory{T}"/>
+ /// </summary>
+ public static implicit operator Memory<T>(ArraySegment<T> arraySegment) => new Memory<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
+
+ /// <summary>
+ /// Defines an implicit conversion of a <see cref="Memory{T}"/> to a <see cref="ReadOnlyMemory{T}"/>
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator ReadOnlyMemory<T>(Memory<T> memory)
+ {
+ if (memory._index < 0)
+ return new ReadOnlyMemory<T>((OwnedMemory<T>)memory._arrayOrOwnedMemory, memory._index & RemoveOwnedFlagBitMask, memory._length);
+ return new ReadOnlyMemory<T>((T[])memory._arrayOrOwnedMemory, memory._index, memory._length);
+ }
+
+ /// <summary>
+ /// Returns an empty <see cref="Memory{T}"/>
+ /// </summary>
+ public static Memory<T> Empty { get; } = Array.Empty<T>();
+
+ /// <summary>
+ /// The number of items in the memory.
+ /// </summary>
+ public int Length => _length;
+
+ /// <summary>
+ /// Returns true if Length is 0.
+ /// </summary>
+ public bool IsEmpty => _length == 0;
+
+ /// <summary>
+ /// Forms a slice out of the given memory, beginning at 'start'.
+ /// </summary>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Memory<T> Slice(int start)
+ {
+ if ((uint)start > (uint)_length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ if (_index < 0)
+ return new Memory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, _length - start);
+ return new Memory<T>((T[])_arrayOrOwnedMemory, _index + start, _length - start);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given memory, beginning at 'start', of given length
+ /// </summary>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <param name="length">The desired length for the slice (exclusive).</param>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Memory<T> Slice(int start, int length)
+ {
+ if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ if (_index < 0)
+ return new Memory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, length);
+ return new Memory<T>((T[])_arrayOrOwnedMemory, _index + start, length);
+ }
+
+ /// <summary>
+ /// Returns a span from the memory.
+ /// </summary>
+ public Span<T> Span
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ if (_index < 0)
+ return ((OwnedMemory<T>)_arrayOrOwnedMemory).AsSpan().Slice(_index & RemoveOwnedFlagBitMask, _length);
+ return new Span<T>((T[])_arrayOrOwnedMemory, _index, _length);
+ }
+ }
+
+ public unsafe MemoryHandle Retain(bool pin = false)
+ {
+ MemoryHandle memoryHandle;
+ if (pin)
+ {
+ if (_index < 0)
+ {
+ memoryHandle = ((OwnedMemory<T>)_arrayOrOwnedMemory).Pin();
+ }
+ else
+ {
+ var array = (T[])_arrayOrOwnedMemory;
+ var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
+ void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
+ memoryHandle = new MemoryHandle(null, pointer, handle);
+ }
+ }
+ else
+ {
+ if (_index < 0)
+ {
+ ((OwnedMemory<T>)_arrayOrOwnedMemory).Retain();
+ memoryHandle = new MemoryHandle((OwnedMemory<T>)_arrayOrOwnedMemory);
+ }
+ else
+ {
+ memoryHandle = new MemoryHandle(null);
+ }
+ }
+ return memoryHandle;
+ }
+
+ /// <summary>
+ /// Get an array segment from the underlying memory.
+ /// If unable to get the array segment, return false with a default array segment.
+ /// </summary>
+ public bool TryGetArray(out ArraySegment<T> arraySegment)
+ {
+ if (_index < 0)
+ {
+ if (((OwnedMemory<T>)_arrayOrOwnedMemory).TryGetArray(out var segment))
+ {
+ arraySegment = new ArraySegment<T>(segment.Array, segment.Offset + (_index & RemoveOwnedFlagBitMask), _length);
+ return true;
+ }
+ }
+ else
+ {
+ arraySegment = new ArraySegment<T>((T[])_arrayOrOwnedMemory, _index, _length);
+ return true;
+ }
+
+ arraySegment = default(ArraySegment<T>);
+ return false;
+ }
+
+ /// <summary>
+ /// Copies the contents from the memory into a new array. This heap
+ /// allocates, so should generally be avoided, however it is sometimes
+ /// necessary to bridge the gap with APIs written in terms of arrays.
+ /// </summary>
+ public T[] ToArray() => Span.ToArray();
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override bool Equals(object obj)
+ {
+ if (obj is ReadOnlyMemory<T>)
+ {
+ return ((ReadOnlyMemory<T>)obj).Equals(this);
+ }
+ else if (obj is Memory<T> memory)
+ {
+ return Equals(memory);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Returns true if the memory points to the same array and has the same length. Note that
+ /// this does *not* check to see if the *contents* are equal.
+ /// </summary>
+ public bool Equals(Memory<T> other)
+ {
+ return
+ _arrayOrOwnedMemory == other._arrayOrOwnedMemory &&
+ _index == other._index &&
+ _length == other._length;
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override int GetHashCode()
+ {
+ return CombineHashCodes(_arrayOrOwnedMemory.GetHashCode(), (_index & RemoveOwnedFlagBitMask).GetHashCode(), _length.GetHashCode());
+ }
+
+ private static int CombineHashCodes(int left, int right)
+ {
+ return ((left << 5) + left) ^ right;
+ }
+
+ private static int CombineHashCodes(int h1, int h2, int h3)
+ {
+ return CombineHashCodes(CombineHashCodes(h1, h2), h3);
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/MethodAccessException.cs b/src/mscorlib/shared/System/MethodAccessException.cs
index 2c9c998c15..12691386c5 100644
--- a/src/mscorlib/shared/System/MethodAccessException.cs
+++ b/src/mscorlib/shared/System/MethodAccessException.cs
@@ -18,19 +18,19 @@ namespace System
public MethodAccessException()
: base(SR.Arg_MethodAccessException)
{
- HResult = __HResults.COR_E_METHODACCESS;
+ HResult = HResults.COR_E_METHODACCESS;
}
public MethodAccessException(String message)
: base(message)
{
- HResult = __HResults.COR_E_METHODACCESS;
+ HResult = HResults.COR_E_METHODACCESS;
}
public MethodAccessException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_METHODACCESS;
+ HResult = HResults.COR_E_METHODACCESS;
}
protected MethodAccessException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/MissingMethodException.cs b/src/mscorlib/shared/System/MissingMethodException.cs
index 967f434302..4f5e8b6562 100644
--- a/src/mscorlib/shared/System/MissingMethodException.cs
+++ b/src/mscorlib/shared/System/MissingMethodException.cs
@@ -20,19 +20,19 @@ namespace System
public MissingMethodException()
: base(SR.Arg_MissingMethodException)
{
- HResult = __HResults.COR_E_MISSINGMETHOD;
+ HResult = HResults.COR_E_MISSINGMETHOD;
}
public MissingMethodException(string message)
: base(message)
{
- HResult = __HResults.COR_E_MISSINGMETHOD;
+ HResult = HResults.COR_E_MISSINGMETHOD;
}
public MissingMethodException(string message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_MISSINGMETHOD;
+ HResult = HResults.COR_E_MISSINGMETHOD;
}
public MissingMethodException(string className, string methodName)
diff --git a/src/mscorlib/shared/System/MulticastNotSupportedException.cs b/src/mscorlib/shared/System/MulticastNotSupportedException.cs
index 493671e227..56a3ec9c00 100644
--- a/src/mscorlib/shared/System/MulticastNotSupportedException.cs
+++ b/src/mscorlib/shared/System/MulticastNotSupportedException.cs
@@ -16,19 +16,19 @@ namespace System
public MulticastNotSupportedException()
: base(SR.Arg_MulticastNotSupportedException)
{
- HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED;
+ HResult = HResults.COR_E_MULTICASTNOTSUPPORTED;
}
public MulticastNotSupportedException(String message)
: base(message)
{
- HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED;
+ HResult = HResults.COR_E_MULTICASTNOTSUPPORTED;
}
public MulticastNotSupportedException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED;
+ HResult = HResults.COR_E_MULTICASTNOTSUPPORTED;
}
}
}
diff --git a/src/mscorlib/shared/System/NonSerializedAttribute.cs b/src/mscorlib/shared/System/NonSerializedAttribute.cs
new file mode 100644
index 0000000000..cabd5a2aa2
--- /dev/null
+++ b/src/mscorlib/shared/System/NonSerializedAttribute.cs
@@ -0,0 +1,14 @@
+// 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
+{
+ [AttributeUsage(AttributeTargets.Field, Inherited = false)]
+ public sealed class NonSerializedAttribute : Attribute
+ {
+ public NonSerializedAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/NotFiniteNumberException.cs b/src/mscorlib/shared/System/NotFiniteNumberException.cs
index 99882b9eb2..ca47a21e5c 100644
--- a/src/mscorlib/shared/System/NotFiniteNumberException.cs
+++ b/src/mscorlib/shared/System/NotFiniteNumberException.cs
@@ -14,41 +14,41 @@ namespace System
: base(SR.Arg_NotFiniteNumberException)
{
_offendingNumber = 0;
- HResult = __HResults.COR_E_NOTFINITENUMBER;
+ HResult = HResults.COR_E_NOTFINITENUMBER;
}
public NotFiniteNumberException(double offendingNumber)
: base()
{
_offendingNumber = offendingNumber;
- HResult = __HResults.COR_E_NOTFINITENUMBER;
+ HResult = HResults.COR_E_NOTFINITENUMBER;
}
public NotFiniteNumberException(String message)
: base(message)
{
_offendingNumber = 0;
- HResult = __HResults.COR_E_NOTFINITENUMBER;
+ HResult = HResults.COR_E_NOTFINITENUMBER;
}
public NotFiniteNumberException(String message, double offendingNumber)
: base(message)
{
_offendingNumber = offendingNumber;
- HResult = __HResults.COR_E_NOTFINITENUMBER;
+ HResult = HResults.COR_E_NOTFINITENUMBER;
}
public NotFiniteNumberException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_NOTFINITENUMBER;
+ HResult = HResults.COR_E_NOTFINITENUMBER;
}
public NotFiniteNumberException(String message, double offendingNumber, Exception innerException)
: base(message, innerException)
{
_offendingNumber = offendingNumber;
- HResult = __HResults.COR_E_NOTFINITENUMBER;
+ HResult = HResults.COR_E_NOTFINITENUMBER;
}
protected NotFiniteNumberException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/NotImplementedException.cs b/src/mscorlib/shared/System/NotImplementedException.cs
index ae62527fac..98976c19db 100644
--- a/src/mscorlib/shared/System/NotImplementedException.cs
+++ b/src/mscorlib/shared/System/NotImplementedException.cs
@@ -21,17 +21,17 @@ namespace System
public NotImplementedException()
: base(SR.Arg_NotImplementedException)
{
- HResult = __HResults.E_NOTIMPL;
+ HResult = HResults.E_NOTIMPL;
}
public NotImplementedException(String message)
: base(message)
{
- HResult = __HResults.E_NOTIMPL;
+ HResult = HResults.E_NOTIMPL;
}
public NotImplementedException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.E_NOTIMPL;
+ HResult = HResults.E_NOTIMPL;
}
protected NotImplementedException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/NotSupportedException.cs b/src/mscorlib/shared/System/NotSupportedException.cs
index 8532e5ad2f..dc43261fbd 100644
--- a/src/mscorlib/shared/System/NotSupportedException.cs
+++ b/src/mscorlib/shared/System/NotSupportedException.cs
@@ -20,19 +20,19 @@ namespace System
public NotSupportedException()
: base(SR.Arg_NotSupportedException)
{
- HResult = __HResults.COR_E_NOTSUPPORTED;
+ HResult = HResults.COR_E_NOTSUPPORTED;
}
public NotSupportedException(String message)
: base(message)
{
- HResult = __HResults.COR_E_NOTSUPPORTED;
+ HResult = HResults.COR_E_NOTSUPPORTED;
}
public NotSupportedException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_NOTSUPPORTED;
+ HResult = HResults.COR_E_NOTSUPPORTED;
}
protected NotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/NullReferenceException.cs b/src/mscorlib/shared/System/NullReferenceException.cs
index f689345654..eb6d709dbf 100644
--- a/src/mscorlib/shared/System/NullReferenceException.cs
+++ b/src/mscorlib/shared/System/NullReferenceException.cs
@@ -20,19 +20,19 @@ namespace System
public NullReferenceException()
: base(SR.Arg_NullReferenceException)
{
- HResult = __HResults.COR_E_NULLREFERENCE;
+ HResult = HResults.COR_E_NULLREFERENCE;
}
public NullReferenceException(String message)
: base(message)
{
- HResult = __HResults.COR_E_NULLREFERENCE;
+ HResult = HResults.COR_E_NULLREFERENCE;
}
public NullReferenceException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_NULLREFERENCE;
+ HResult = HResults.COR_E_NULLREFERENCE;
}
protected NullReferenceException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/ObjectDisposedException.cs b/src/mscorlib/shared/System/ObjectDisposedException.cs
index 6e8e6b2d74..3d7ba5df15 100644
--- a/src/mscorlib/shared/System/ObjectDisposedException.cs
+++ b/src/mscorlib/shared/System/ObjectDisposedException.cs
@@ -28,14 +28,14 @@ namespace System
public ObjectDisposedException(String objectName, String message) : base(message)
{
- HResult = __HResults.COR_E_OBJECTDISPOSED;
+ HResult = HResults.COR_E_OBJECTDISPOSED;
_objectName = objectName;
}
public ObjectDisposedException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_OBJECTDISPOSED;
+ HResult = HResults.COR_E_OBJECTDISPOSED;
}
protected ObjectDisposedException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/OperationCanceledException.cs b/src/mscorlib/shared/System/OperationCanceledException.cs
index 2c7654854f..44a0427c58 100644
--- a/src/mscorlib/shared/System/OperationCanceledException.cs
+++ b/src/mscorlib/shared/System/OperationCanceledException.cs
@@ -31,19 +31,19 @@ namespace System
public OperationCanceledException()
: base(SR.OperationCanceled)
{
- HResult = __HResults.COR_E_OPERATIONCANCELED;
+ HResult = HResults.COR_E_OPERATIONCANCELED;
}
public OperationCanceledException(String message)
: base(message)
{
- HResult = __HResults.COR_E_OPERATIONCANCELED;
+ HResult = HResults.COR_E_OPERATIONCANCELED;
}
public OperationCanceledException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_OPERATIONCANCELED;
+ HResult = HResults.COR_E_OPERATIONCANCELED;
}
diff --git a/src/mscorlib/shared/System/OverflowException.cs b/src/mscorlib/shared/System/OverflowException.cs
index 4052e41a18..bf6676ca2c 100644
--- a/src/mscorlib/shared/System/OverflowException.cs
+++ b/src/mscorlib/shared/System/OverflowException.cs
@@ -20,19 +20,19 @@ namespace System
public OverflowException()
: base(SR.Arg_OverflowException)
{
- HResult = __HResults.COR_E_OVERFLOW;
+ HResult = HResults.COR_E_OVERFLOW;
}
public OverflowException(String message)
: base(message)
{
- HResult = __HResults.COR_E_OVERFLOW;
+ HResult = HResults.COR_E_OVERFLOW;
}
public OverflowException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_OVERFLOW;
+ HResult = HResults.COR_E_OVERFLOW;
}
protected OverflowException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/PlatformNotSupportedException.cs b/src/mscorlib/shared/System/PlatformNotSupportedException.cs
index f679ac9454..4403c1da06 100644
--- a/src/mscorlib/shared/System/PlatformNotSupportedException.cs
+++ b/src/mscorlib/shared/System/PlatformNotSupportedException.cs
@@ -20,19 +20,19 @@ namespace System
public PlatformNotSupportedException()
: base(SR.Arg_PlatformNotSupported)
{
- HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED;
+ HResult = HResults.COR_E_PLATFORMNOTSUPPORTED;
}
public PlatformNotSupportedException(String message)
: base(message)
{
- HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED;
+ HResult = HResults.COR_E_PLATFORMNOTSUPPORTED;
}
public PlatformNotSupportedException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED;
+ HResult = HResults.COR_E_PLATFORMNOTSUPPORTED;
}
protected PlatformNotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/Random.cs b/src/mscorlib/shared/System/Random.cs
index 4affed8a1a..20f035e2e8 100644
--- a/src/mscorlib/shared/System/Random.cs
+++ b/src/mscorlib/shared/System/Random.cs
@@ -2,20 +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: A random number generator.
-**
-**
-===========================================================*/
-
-using System;
-using System.Runtime;
-using System.Runtime.CompilerServices;
-using System.Globalization;
-using System.Diagnostics.Contracts;
-
namespace System
{
public class Random
@@ -23,11 +9,10 @@ namespace System
//
// Private Constants
//
- private const int MBIG = Int32.MaxValue;
+ private const int MBIG = int.MaxValue;
private const int MSEED = 161803398;
private const int MZ = 0;
-
//
// Member Variables
//
@@ -64,7 +49,7 @@ namespace System
int mj, mk;
//Initialize our Seed array.
- int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed);
+ int subtraction = (Seed == int.MinValue) ? int.MaxValue : Math.Abs(Seed);
mj = MSEED - subtraction;
_seedArray[55] = mj;
mk = 1;
@@ -195,8 +180,8 @@ namespace System
result = -result;
}
double d = result;
- d += (Int32.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1)
- d /= 2 * (uint)Int32.MaxValue - 1;
+ d += (int.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1)
+ d /= 2 * (uint)int.MaxValue - 1;
return d;
}
@@ -213,10 +198,9 @@ namespace System
{
throw new ArgumentOutOfRangeException(nameof(minValue), SR.Format(SR.Argument_MinMaxValue, nameof(minValue), nameof(maxValue)));
}
- Contract.EndContractBlock();
long range = (long)maxValue - minValue;
- if (range <= (long)Int32.MaxValue)
+ if (range <= int.MaxValue)
{
return ((int)(Sample() * range) + minValue);
}
@@ -238,7 +222,6 @@ namespace System
{
throw new ArgumentOutOfRangeException(nameof(maxValue), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(maxValue)));
}
- Contract.EndContractBlock();
return (int)(Sample() * maxValue);
}
@@ -263,10 +246,17 @@ namespace System
public virtual void NextBytes(byte[] buffer)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
- Contract.EndContractBlock();
for (int i = 0; i < buffer.Length; i++)
{
- buffer[i] = (byte)(InternalSample() % (Byte.MaxValue + 1));
+ buffer[i] = (byte)InternalSample();
+ }
+ }
+
+ public virtual void NextBytes(Span<byte> buffer)
+ {
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ buffer[i] = (byte)Next();
}
}
}
diff --git a/src/mscorlib/shared/System/RankException.cs b/src/mscorlib/shared/System/RankException.cs
index 15759ea75d..f2c5d06548 100644
--- a/src/mscorlib/shared/System/RankException.cs
+++ b/src/mscorlib/shared/System/RankException.cs
@@ -21,19 +21,19 @@ namespace System
public RankException()
: base(SR.Arg_RankException)
{
- HResult = __HResults.COR_E_RANK;
+ HResult = HResults.COR_E_RANK;
}
public RankException(String message)
: base(message)
{
- HResult = __HResults.COR_E_RANK;
+ HResult = HResults.COR_E_RANK;
}
public RankException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_RANK;
+ HResult = HResults.COR_E_RANK;
}
protected RankException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/ReadOnlyMemory.cs b/src/mscorlib/shared/System/ReadOnlyMemory.cs
new file mode 100644
index 0000000000..8153d02fa3
--- /dev/null
+++ b/src/mscorlib/shared/System/ReadOnlyMemory.cs
@@ -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.
+
+using System.Buffers;
+using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
+using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
+using System.Diagnostics;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace System
+{
+ public struct ReadOnlyMemory<T>
+ {
+ // The highest order bit of _index is used to discern whether _arrayOrOwnedMemory is an array or an owned memory
+ // if (_index >> 31) == 1, object _arrayOrOwnedMemory is an OwnedMemory<T>
+ // else, object _arrayOrOwnedMemory is a T[]
+ private readonly object _arrayOrOwnedMemory;
+ private readonly int _index;
+ private readonly int _length;
+
+ private const int RemoveOwnedFlagBitMask = 0x7FFFFFFF;
+
+ /// <summary>
+ /// Creates a new memory over the entirety of the target array.
+ /// </summary>
+ /// <param name="array">The target array.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
+ /// reference (Nothing in Visual Basic).</exception>
+ /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlyMemory(T[] array)
+ {
+ if (array == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+
+ _arrayOrOwnedMemory = array;
+ _index = 0;
+ _length = array.Length;
+ }
+
+ /// <summary>
+ /// Creates a new memory over the portion of the target array beginning
+ /// at 'start' index and ending at 'end' index (exclusive).
+ /// </summary>
+ /// <param name="array">The target array.</param>
+ /// <param name="start">The index at which to begin the memory.</param>
+ /// <param name="length">The number of items in the memory.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
+ /// reference (Nothing in Visual Basic).</exception>
+ /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlyMemory(T[] array, int start, int length)
+ {
+ if (array == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ _arrayOrOwnedMemory = array;
+ _index = start;
+ _length = length;
+ }
+
+ // Constructor for internal use only.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ReadOnlyMemory(OwnedMemory<T> owner, int index, int length)
+ {
+ if (owner == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ownedMemory);
+ if (index < 0 || length < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ _arrayOrOwnedMemory = owner;
+ _index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask
+ _length = length;
+ }
+
+ /// <summary>
+ /// Defines an implicit conversion of an array to a <see cref="Memory{T}"/>
+ /// </summary>
+ public static implicit operator ReadOnlyMemory<T>(T[] array) => new ReadOnlyMemory<T>(array);
+
+ /// <summary>
+ /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Memory{T}"/>
+ /// </summary>
+ public static implicit operator ReadOnlyMemory<T>(ArraySegment<T> arraySegment) => new ReadOnlyMemory<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
+
+ /// <summary>
+ /// Returns an empty <see cref="Memory{T}"/>
+ /// </summary>
+ public static ReadOnlyMemory<T> Empty { get; } = Array.Empty<T>();
+
+ /// <summary>
+ /// The number of items in the memory.
+ /// </summary>
+ public int Length => _length;
+
+ /// <summary>
+ /// Returns true if Length is 0.
+ /// </summary>
+ public bool IsEmpty => _length == 0;
+
+ /// <summary>
+ /// Forms a slice out of the given memory, beginning at 'start'.
+ /// </summary>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlyMemory<T> Slice(int start)
+ {
+ if ((uint)start > (uint)_length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ if (_index < 0)
+ return new ReadOnlyMemory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, _length - start);
+ return new ReadOnlyMemory<T>((T[])_arrayOrOwnedMemory, _index + start, _length - start);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given memory, beginning at 'start', of given length
+ /// </summary>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <param name="length">The desired length for the slice (exclusive).</param>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlyMemory<T> Slice(int start, int length)
+ {
+ if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ if (_index < 0)
+ return new ReadOnlyMemory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, length);
+ return new ReadOnlyMemory<T>((T[])_arrayOrOwnedMemory, _index + start, length);
+ }
+
+ /// <summary>
+ /// Returns a span from the memory.
+ /// </summary>
+ public ReadOnlySpan<T> Span
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ if (_index < 0)
+ return ((OwnedMemory<T>)_arrayOrOwnedMemory).AsSpan().Slice(_index & RemoveOwnedFlagBitMask, _length);
+ return new ReadOnlySpan<T>((T[])_arrayOrOwnedMemory, _index, _length);
+ }
+ }
+
+ public unsafe MemoryHandle Retain(bool pin = false)
+ {
+ MemoryHandle memoryHandle;
+ if (pin)
+ {
+ if (_index < 0)
+ {
+ memoryHandle = ((OwnedMemory<T>)_arrayOrOwnedMemory).Pin();
+ }
+ else
+ {
+ var array = (T[])_arrayOrOwnedMemory;
+ var handle = GCHandle.Alloc(array, GCHandleType.Pinned);
+ void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index);
+ memoryHandle = new MemoryHandle(null, pointer, handle);
+ }
+ }
+ else
+ {
+ if (_index < 0)
+ {
+ ((OwnedMemory<T>)_arrayOrOwnedMemory).Retain();
+ memoryHandle = new MemoryHandle((OwnedMemory<T>)_arrayOrOwnedMemory);
+ }
+ else
+ {
+ memoryHandle = new MemoryHandle(null);
+ }
+ }
+ return memoryHandle;
+ }
+
+ /// <summary>
+ /// Get an array segment from the underlying memory.
+ /// If unable to get the array segment, return false with a default array segment.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public bool DangerousTryGetArray(out ArraySegment<T> arraySegment)
+ {
+ if (_index < 0)
+ {
+ if (((OwnedMemory<T>)_arrayOrOwnedMemory).TryGetArray(out var segment))
+ {
+ arraySegment = new ArraySegment<T>(segment.Array, segment.Offset + (_index & RemoveOwnedFlagBitMask), _length);
+ return true;
+ }
+ }
+ else
+ {
+ arraySegment = new ArraySegment<T>((T[])_arrayOrOwnedMemory, _index, _length);
+ return true;
+ }
+
+ arraySegment = default(ArraySegment<T>);
+ return false;
+ }
+
+ /// <summary>
+ /// Copies the contents from the memory into a new array. This heap
+ /// allocates, so should generally be avoided, however it is sometimes
+ /// necessary to bridge the gap with APIs written in terms of arrays.
+ /// </summary>
+ public T[] ToArray() => Span.ToArray();
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override bool Equals(object obj)
+ {
+ if (obj is ReadOnlyMemory<T> readOnlyMemory)
+ {
+ return Equals(readOnlyMemory);
+ }
+ else if (obj is Memory<T> memory)
+ {
+ return Equals(memory);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Returns true if the memory points to the same array and has the same length. Note that
+ /// this does *not* check to see if the *contents* are equal.
+ /// </summary>
+ public bool Equals(ReadOnlyMemory<T> other)
+ {
+ return
+ _arrayOrOwnedMemory == other._arrayOrOwnedMemory &&
+ _index == other._index &&
+ _length == other._length;
+ }
+
+ [EditorBrowsable( EditorBrowsableState.Never)]
+ public override int GetHashCode()
+ {
+ return CombineHashCodes(_arrayOrOwnedMemory.GetHashCode(), (_index & RemoveOwnedFlagBitMask).GetHashCode(), _length.GetHashCode());
+ }
+
+ private static int CombineHashCodes(int left, int right)
+ {
+ return ((left << 5) + left) ^ right;
+ }
+
+ private static int CombineHashCodes(int h1, int h2, int h3)
+ {
+ return CombineHashCodes(CombineHashCodes(h1, h2), h3);
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/ReadOnlySpan.cs b/src/mscorlib/shared/System/ReadOnlySpan.cs
index ae49f59269..bcf1697e88 100644
--- a/src/mscorlib/shared/System/ReadOnlySpan.cs
+++ b/src/mscorlib/shared/System/ReadOnlySpan.cs
@@ -2,10 +2,10 @@
// 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 EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
-using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
+using System.Runtime.Versioning;
#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
@@ -15,7 +15,9 @@ namespace System
/// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed
/// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
/// </summary>
+ [IsReadOnly]
[IsByRefLike]
+ [NonVersionable]
public struct ReadOnlySpan<T>
{
/// <summary>A byref or a native ptr.</summary>
@@ -44,29 +46,6 @@ namespace System
/// <summary>
/// Creates a new read-only span over the portion of the target array beginning
- /// at 'start' index and covering the remainder of the array.
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <param name="start">The index at which to begin the read-only span.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> is not in the range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan(T[] array, int start)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- if ((uint)start > (uint)array.Length)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
- _length = array.Length - start;
- }
-
- /// <summary>
- /// Creates a new read-only span over the portion of the target array beginning
/// at 'start' index and ending at 'end' index (exclusive).
/// </summary>
/// <param name="array">The target array.</param>
@@ -126,6 +105,7 @@ namespace System
/// <param name="objectData">A reference to data within that object.</param>
/// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public static ReadOnlySpan<T> DangerousCreate(object obj, ref T objectData, int length) => new ReadOnlySpan<T>(ref objectData, length);
// Constructor for internal use only.
@@ -143,6 +123,7 @@ namespace System
/// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public ref T DangerousGetPinnableReference()
{
return ref _pointer.Value;
@@ -151,12 +132,26 @@ namespace System
/// <summary>
/// The number of items in the read-only span.
/// </summary>
- public int Length => _length;
+ public int Length
+ {
+ [NonVersionable]
+ get
+ {
+ return _length;
+ }
+ }
/// <summary>
/// Returns true if Length is 0.
/// </summary>
- public bool IsEmpty => _length == 0;
+ public bool IsEmpty
+ {
+ [NonVersionable]
+ get
+ {
+ return _length == 0;
+ }
+ }
/// <summary>
/// Returns the specified element of the read-only span.
@@ -179,6 +174,7 @@ namespace System
[Intrinsic]
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [NonVersionable]
get
{
if ((uint)index >= (uint)_length)
diff --git a/src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs b/src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs
index a0075bbc0b..c4aeca704d 100644
--- a/src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs
+++ b/src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs
@@ -11,19 +11,19 @@ namespace System.Reflection
public AmbiguousMatchException()
: base(SR.RFLCT_Ambiguous)
{
- HResult = __HResults.COR_E_AMBIGUOUSMATCH;
+ HResult = HResults.COR_E_AMBIGUOUSMATCH;
}
public AmbiguousMatchException(string message)
: base(message)
{
- HResult = __HResults.COR_E_AMBIGUOUSMATCH;
+ HResult = HResults.COR_E_AMBIGUOUSMATCH;
}
public AmbiguousMatchException(string message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_AMBIGUOUSMATCH;
+ HResult = HResults.COR_E_AMBIGUOUSMATCH;
}
}
}
diff --git a/src/mscorlib/shared/System/Reflection/BindingFlags.cs b/src/mscorlib/shared/System/Reflection/BindingFlags.cs
index 26c875d0f9..7ba83e20da 100644
--- a/src/mscorlib/shared/System/Reflection/BindingFlags.cs
+++ b/src/mscorlib/shared/System/Reflection/BindingFlags.cs
@@ -46,5 +46,6 @@ namespace System.Reflection
// These are a couple of misc attributes used
IgnoreReturn = 0x01000000, // This is used in COM Interop
+ DoNotWrapExceptions = 0x02000000, // Disables wrapping exceptions in TargetInvocationException
}
}
diff --git a/src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs b/src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs
index 13766ae8d0..ae67158a53 100644
--- a/src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs
+++ b/src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs
@@ -21,7 +21,7 @@ namespace System.Reflection
public CustomAttributeFormatException(string message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_CUSTOMATTRIBUTEFORMAT;
+ HResult = HResults.COR_E_CUSTOMATTRIBUTEFORMAT;
}
protected CustomAttributeFormatException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/Reflection/IReflect.cs b/src/mscorlib/shared/System/Reflection/IReflect.cs
index 51141ae47c..a7e84b6168 100644
--- a/src/mscorlib/shared/System/Reflection/IReflect.cs
+++ b/src/mscorlib/shared/System/Reflection/IReflect.cs
@@ -47,7 +47,7 @@ namespace System.Reflection
MemberInfo[] GetMembers(BindingFlags bindingAttr);
// Description of the Binding Process.
- // We must invoke a method that is accessable and for which the provided
+ // We must invoke a method that is accessible and for which the provided
// parameters have the most specific match. A method may be called if
// 1. The number of parameters in the method declaration equals the number of
// arguments provided to the invocation
@@ -59,7 +59,7 @@ namespace System.Reflection
// of methods is filtered by the name, number of arguments and a set of search modifiers
// defined in the Binder.
//
- // After the method is selected, it will be invoked. Accessability is checked
+ // After the method is selected, it will be invoked. Accessibility is checked
// at that point. The search may be control which set of methods are searched based
// upon the accessibility attribute associated with the method.
//
diff --git a/src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs b/src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs
index 07880a768d..85a447707c 100644
--- a/src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs
+++ b/src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs
@@ -21,7 +21,7 @@ namespace System.Reflection
public InvalidFilterCriteriaException(string message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_INVALIDFILTERCRITERIA;
+ HResult = HResults.COR_E_INVALIDFILTERCRITERIA;
}
protected InvalidFilterCriteriaException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/Reflection/MethodInfo.Internal.cs b/src/mscorlib/shared/System/Reflection/MethodInfo.Internal.cs
new file mode 100644
index 0000000000..a36ef900da
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/MethodInfo.Internal.cs
@@ -0,0 +1,17 @@
+// 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.Reflection
+{
+ public abstract partial class MethodInfo : MethodBase
+ {
+#if CORECLR
+ internal
+#else
+ // Not an api but needs to be public so that Reflection.Core can access it.
+ public
+#endif
+ virtual int GenericParameterCount => GetGenericArguments().Length;
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/MethodInfo.cs b/src/mscorlib/shared/System/Reflection/MethodInfo.cs
index 3f60149e87..95c62ba7f0 100644
--- a/src/mscorlib/shared/System/Reflection/MethodInfo.cs
+++ b/src/mscorlib/shared/System/Reflection/MethodInfo.cs
@@ -4,7 +4,7 @@
namespace System.Reflection
{
- public abstract class MethodInfo : MethodBase
+ public abstract partial class MethodInfo : MethodBase
{
protected MethodInfo() { }
diff --git a/src/mscorlib/shared/System/Reflection/Pointer.cs b/src/mscorlib/shared/System/Reflection/Pointer.cs
index 55376c66c0..e1a9990cf0 100644
--- a/src/mscorlib/shared/System/Reflection/Pointer.cs
+++ b/src/mscorlib/shared/System/Reflection/Pointer.cs
@@ -46,6 +46,6 @@ namespace System.Reflection
}
internal Type GetPointerType() => _ptrType;
- internal object GetPointerValue() => (IntPtr)_ptr;
+ internal IntPtr GetPointerValue() => (IntPtr)_ptr;
}
}
diff --git a/src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs b/src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs
index ca0c6ab0db..0e86d34056 100644
--- a/src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs
+++ b/src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs
@@ -13,7 +13,7 @@ namespace System.Reflection
{
Types = classes;
LoaderExceptions = exceptions;
- HResult = __HResults.COR_E_REFLECTIONTYPELOAD;
+ HResult = HResults.COR_E_REFLECTIONTYPELOAD;
}
public ReflectionTypeLoadException(Type[] classes, Exception[] exceptions, string message)
@@ -21,7 +21,7 @@ namespace System.Reflection
{
Types = classes;
LoaderExceptions = exceptions;
- HResult = __HResults.COR_E_REFLECTIONTYPELOAD;
+ HResult = HResults.COR_E_REFLECTIONTYPELOAD;
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/Reflection/SignatureArrayType.cs b/src/mscorlib/shared/System/Reflection/SignatureArrayType.cs
new file mode 100644
index 0000000000..52011b8a9d
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/SignatureArrayType.cs
@@ -0,0 +1,46 @@
+// 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.Reflection
+{
+ internal sealed class SignatureArrayType : SignatureHasElementType
+ {
+ internal SignatureArrayType(SignatureType elementType, int rank, bool isMultiDim)
+ : base(elementType)
+ {
+ Debug.Assert(rank > 0);
+ Debug.Assert(rank == 1 || isMultiDim);
+
+ _rank = rank;
+ _isMultiDim = isMultiDim;
+ }
+
+ protected sealed override bool IsArrayImpl() => true;
+ protected sealed override bool IsByRefImpl() => false;
+ protected sealed override bool IsPointerImpl() => false;
+
+ public sealed override bool IsSZArray => !_isMultiDim;
+ public sealed override bool IsVariableBoundArray => _isMultiDim;
+
+ public sealed override int GetArrayRank() => _rank;
+
+ protected sealed override string Suffix
+ {
+ get
+ {
+ if (!_isMultiDim)
+ return "[]";
+ else if (_rank == 1)
+ return "[*]";
+ else
+ return "[" + new string(',', _rank - 1) + "]";
+ }
+ }
+
+ private readonly int _rank;
+ private readonly bool _isMultiDim;
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/SignatureByRefType.cs b/src/mscorlib/shared/System/Reflection/SignatureByRefType.cs
new file mode 100644
index 0000000000..eb5f6de42e
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/SignatureByRefType.cs
@@ -0,0 +1,27 @@
+// 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;
+
+namespace System.Reflection
+{
+ internal sealed class SignatureByRefType : SignatureHasElementType
+ {
+ internal SignatureByRefType(SignatureType elementType)
+ : base(elementType)
+ {
+ }
+
+ protected sealed override bool IsArrayImpl() => false;
+ protected sealed override bool IsByRefImpl() => true;
+ protected sealed override bool IsPointerImpl() => false;
+
+ public sealed override bool IsSZArray => false;
+ public sealed override bool IsVariableBoundArray => false;
+
+ public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass);
+
+ protected sealed override string Suffix => "&";
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/SignatureConstructedGenericType.cs b/src/mscorlib/shared/System/Reflection/SignatureConstructedGenericType.cs
new file mode 100644
index 0000000000..afcdfda352
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/SignatureConstructedGenericType.cs
@@ -0,0 +1,73 @@
+// 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.Text;
+using System.Diagnostics;
+
+namespace System.Reflection
+{
+ internal sealed class SignatureConstructedGenericType : SignatureType
+ {
+ internal SignatureConstructedGenericType(Type genericTypeDefinition, Type[] genericTypeArguments)
+ {
+ Debug.Assert(genericTypeDefinition != null && genericTypeArguments != null);
+ _genericTypeDefinition = genericTypeDefinition;
+ _genericTypeArguments = (Type[])(genericTypeArguments.Clone());
+ }
+
+ public sealed override bool IsTypeDefinition => false;
+ public sealed override bool IsGenericTypeDefinition => false;
+ protected sealed override bool HasElementTypeImpl() => false;
+ protected sealed override bool IsArrayImpl() => false;
+ protected sealed override bool IsByRefImpl() => false;
+ public sealed override bool IsByRefLike => _genericTypeDefinition.IsByRefLike;
+ protected sealed override bool IsPointerImpl() => false;
+ public sealed override bool IsSZArray => false;
+ public sealed override bool IsVariableBoundArray => false;
+ public sealed override bool IsConstructedGenericType => true;
+ public sealed override bool IsGenericParameter => false;
+ public sealed override bool IsGenericMethodParameter => false;
+ public sealed override bool ContainsGenericParameters
+ {
+ get
+ {
+ for (int i = 0; i < _genericTypeArguments.Length; i++)
+ {
+ if (_genericTypeArguments[i].ContainsGenericParameters)
+ return true;
+ }
+ return false;
+ }
+ }
+
+ internal sealed override SignatureType ElementType => null;
+ public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass);
+ public sealed override Type GetGenericTypeDefinition() => _genericTypeDefinition;
+ public sealed override Type[] GetGenericArguments() => GenericTypeArguments;
+ public sealed override Type[] GenericTypeArguments => (Type[])(_genericTypeArguments.Clone());
+ public sealed override int GenericParameterPosition => throw new InvalidOperationException(SR.Arg_NotGenericParameter);
+
+ public sealed override string Name => _genericTypeDefinition.Name;
+ public sealed override string Namespace => _genericTypeDefinition.Namespace;
+
+ public sealed override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(_genericTypeDefinition.ToString());
+ sb.Append('[');
+ for (int i = 0; i < _genericTypeArguments.Length; i++)
+ {
+ if (i != 0)
+ sb.Append(',');
+ sb.Append(_genericTypeArguments[i].ToString());
+ }
+ sb.Append(']');
+ return sb.ToString();
+ }
+
+ private readonly Type _genericTypeDefinition;
+ private readonly Type[] _genericTypeArguments;
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/SignatureGenericMethodParameterType.cs b/src/mscorlib/shared/System/Reflection/SignatureGenericMethodParameterType.cs
new file mode 100644
index 0000000000..ae73272ab7
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/SignatureGenericMethodParameterType.cs
@@ -0,0 +1,18 @@
+// 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.Reflection
+{
+ internal sealed class SignatureGenericMethodParameterType : SignatureGenericParameterType
+ {
+ internal SignatureGenericMethodParameterType(int position)
+ : base(position)
+ {
+ }
+
+ public sealed override bool IsGenericMethodParameter => true;
+
+ public sealed override string Name => "!!" + GenericParameterPosition;
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/SignatureGenericParameterType.cs b/src/mscorlib/shared/System/Reflection/SignatureGenericParameterType.cs
new file mode 100644
index 0000000000..fee7bce353
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/SignatureGenericParameterType.cs
@@ -0,0 +1,46 @@
+// 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;
+
+namespace System.Reflection
+{
+ internal abstract class SignatureGenericParameterType : SignatureType
+ {
+ protected SignatureGenericParameterType(int position)
+ {
+ Debug.Assert(position >= 0);
+ _position = position;
+ }
+
+ public sealed override bool IsTypeDefinition => false;
+ public sealed override bool IsGenericTypeDefinition => false;
+ protected sealed override bool HasElementTypeImpl() => false;
+ protected sealed override bool IsArrayImpl() => false;
+ protected sealed override bool IsByRefImpl() => false;
+ public sealed override bool IsByRefLike => false;
+ protected sealed override bool IsPointerImpl() => false;
+ public sealed override bool IsSZArray => false;
+ public sealed override bool IsVariableBoundArray => false;
+ public sealed override bool IsConstructedGenericType => false;
+ public sealed override bool IsGenericParameter => true;
+ public abstract override bool IsGenericMethodParameter { get; }
+ public sealed override bool ContainsGenericParameters => true;
+
+ internal sealed override SignatureType ElementType => null;
+ public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass);
+ public sealed override Type GetGenericTypeDefinition() => throw new InvalidOperationException(SR.InvalidOperation_NotGenericType);
+ public sealed override Type[] GetGenericArguments() => Array.Empty<Type>();
+ public sealed override Type[] GenericTypeArguments => Array.Empty<Type>();
+ public sealed override int GenericParameterPosition => _position;
+
+ public abstract override string Name { get; }
+ public sealed override string Namespace => null;
+
+ public sealed override string ToString() => Name;
+
+ private readonly int _position;
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/SignatureHasElementType.cs b/src/mscorlib/shared/System/Reflection/SignatureHasElementType.cs
new file mode 100644
index 0000000000..e1aa6c3584
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/SignatureHasElementType.cs
@@ -0,0 +1,48 @@
+// 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;
+
+namespace System.Reflection
+{
+ internal abstract class SignatureHasElementType : SignatureType
+ {
+ protected SignatureHasElementType(SignatureType elementType)
+ {
+ Debug.Assert(elementType != null);
+ _elementType = elementType;
+ }
+
+ public sealed override bool IsTypeDefinition => false;
+ public sealed override bool IsGenericTypeDefinition => false;
+ protected sealed override bool HasElementTypeImpl() => true;
+ protected abstract override bool IsArrayImpl();
+ protected abstract override bool IsByRefImpl();
+ public sealed override bool IsByRefLike => false;
+ protected abstract override bool IsPointerImpl();
+ public abstract override bool IsSZArray { get; }
+ public abstract override bool IsVariableBoundArray { get; }
+ public sealed override bool IsConstructedGenericType => false;
+ public sealed override bool IsGenericParameter => false;
+ public sealed override bool IsGenericMethodParameter => false;
+ public sealed override bool ContainsGenericParameters => _elementType.ContainsGenericParameters;
+
+ internal sealed override SignatureType ElementType => _elementType;
+ public abstract override int GetArrayRank();
+ public sealed override Type GetGenericTypeDefinition() => throw new InvalidOperationException(SR.InvalidOperation_NotGenericType);
+ public sealed override Type[] GetGenericArguments() => Array.Empty<Type>();
+ public sealed override Type[] GenericTypeArguments => Array.Empty<Type>();
+ public sealed override int GenericParameterPosition => throw new InvalidOperationException(SR.Arg_NotGenericParameter);
+
+ public sealed override string Name => _elementType.Name + Suffix;
+ public sealed override string Namespace => _elementType.Namespace;
+
+ public sealed override string ToString() => _elementType.ToString() + Suffix;
+
+ protected abstract string Suffix { get; }
+
+ private readonly SignatureType _elementType;
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/SignaturePointerType.cs b/src/mscorlib/shared/System/Reflection/SignaturePointerType.cs
new file mode 100644
index 0000000000..a75a208165
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/SignaturePointerType.cs
@@ -0,0 +1,27 @@
+// 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;
+
+namespace System.Reflection
+{
+ internal sealed class SignaturePointerType : SignatureHasElementType
+ {
+ internal SignaturePointerType(SignatureType elementType)
+ : base(elementType)
+ {
+ }
+
+ protected sealed override bool IsArrayImpl() => false;
+ protected sealed override bool IsByRefImpl() => false;
+ protected sealed override bool IsPointerImpl() => true;
+
+ public sealed override bool IsSZArray => false;
+ public sealed override bool IsVariableBoundArray => false;
+
+ public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass);
+
+ protected sealed override string Suffix => "*";
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/SignatureType.cs b/src/mscorlib/shared/System/Reflection/SignatureType.cs
new file mode 100644
index 0000000000..c3dfc49174
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/SignatureType.cs
@@ -0,0 +1,139 @@
+// 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.Globalization;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace System.Reflection
+{
+ //
+ // Signature Types are highly restricted Type objects that can be passed in the Type[] parameter to Type.GetMethod(). Their primary
+ // use is to pass types representing generic parameters defined by the method, or types composed from them. Passing in the normal
+ // generic parameter Type obtained from MethodInfo.GetGenericArguments() is usually impractical (if you had the method, you wouldn't
+ // be looking for it!)
+ //
+ internal abstract class SignatureType : Type
+ {
+ public sealed override bool IsSignatureType => true;
+
+ // Type flavor predicates
+ public abstract override bool IsTypeDefinition { get; }
+ protected abstract override bool HasElementTypeImpl();
+ protected abstract override bool IsArrayImpl();
+ public abstract override bool IsSZArray { get; }
+ public abstract override bool IsVariableBoundArray { get; }
+ protected abstract override bool IsByRefImpl();
+ public abstract override bool IsByRefLike { get; }
+ protected abstract override bool IsPointerImpl();
+ public sealed override bool IsGenericType => IsGenericTypeDefinition || IsConstructedGenericType;
+ public abstract override bool IsGenericTypeDefinition { get; }
+ public abstract override bool IsConstructedGenericType { get; }
+ public abstract override bool IsGenericParameter { get; }
+ public abstract bool IsGenericMethodParameter { get; }
+ public abstract override bool ContainsGenericParameters { get; }
+ public sealed override MemberTypes MemberType => MemberTypes.TypeInfo;
+
+ // Compositors
+ public sealed override Type MakeArrayType() => new SignatureArrayType(this, rank: 1, isMultiDim: false);
+ public sealed override Type MakeArrayType(int rank)
+ {
+ if (rank <= 0)
+ throw new IndexOutOfRangeException();
+ return new SignatureArrayType(this, rank: rank, isMultiDim: true);
+ }
+ public sealed override Type MakeByRefType() => new SignatureByRefType(this);
+ public sealed override Type MakePointerType() => new SignaturePointerType(this);
+ public sealed override Type MakeGenericType(params Type[] typeArguments) => throw new NotSupportedException(SR.NotSupported_SignatureType); // There is no SignatureType for type definition types so it would never be legal to call this.
+
+ // Dissectors
+ public sealed override Type GetElementType() => ElementType;
+ public abstract override int GetArrayRank();
+ public abstract override Type GetGenericTypeDefinition();
+ public abstract override Type[] GenericTypeArguments { get; }
+ public abstract override Type[] GetGenericArguments();
+ public abstract override int GenericParameterPosition { get; }
+ internal abstract SignatureType ElementType { get; }
+
+ // Identity
+#if DEBUG
+ public sealed override bool Equals(object o) => base.Equals(o);
+ public sealed override bool Equals(Type o) => base.Equals(o);
+ public sealed override int GetHashCode() => base.GetHashCode();
+#endif
+ public sealed override Type UnderlyingSystemType => this; // Equals(Type) depends on this.
+
+ // Naming and diagnostics
+ public abstract override string Name { get; }
+ public abstract override string Namespace { get; }
+ public sealed override string FullName => null;
+ public sealed override string AssemblyQualifiedName => null;
+ public abstract override string ToString();
+
+ // Not supported on Signature Types
+ public sealed override Assembly Assembly => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Module Module => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Type ReflectedType => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Type BaseType => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Type[] GetInterfaces() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool IsAssignableFrom(Type c) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override int MetadataToken => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Type DeclaringType => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override MethodBase DeclaringMethod => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Type[] GetGenericParameterConstraints() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override GenericParameterAttributes GenericParameterAttributes => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool IsEnumDefined(object value) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override string GetEnumName(object value) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override string[] GetEnumNames() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Type GetEnumUnderlyingType() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Array GetEnumValues() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Guid GUID => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ protected sealed override TypeCode GetTypeCodeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ protected sealed override TypeAttributes GetAttributeFlagsImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override EventInfo GetEvent(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override EventInfo[] GetEvents(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override FieldInfo GetField(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override FieldInfo[] GetFields(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override MemberInfo[] GetMembers(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override MethodInfo[] GetMethods(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Type GetNestedType(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Type[] GetNestedTypes(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ protected sealed override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ protected sealed override MethodInfo GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ protected sealed override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override MemberInfo[] FindMembers(MemberTypes memberType, BindingFlags bindingAttr, MemberFilter filter, object filterCriteria) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override MemberInfo[] GetMember(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override MemberInfo[] GetDefaultMembers() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override EventInfo[] GetEvents() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override object[] GetCustomAttributes(bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override IList<CustomAttributeData> GetCustomAttributesData() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Type GetInterface(string name, bool ignoreCase) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ protected sealed override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ protected sealed override bool IsCOMObjectImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ protected sealed override bool IsPrimitiveImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override IEnumerable<CustomAttributeData> CustomAttributes => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override Type[] FindInterfaces(TypeFilter filter, object filterCriteria) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override InterfaceMapping GetInterfaceMap(Type interfaceType) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ protected sealed override bool IsContextfulImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool IsEnum => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool IsEquivalentTo(Type other) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool IsInstanceOfType(object o) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ protected sealed override bool IsMarshalByRefImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool IsSecurityCritical => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool IsSecuritySafeCritical => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool IsSecurityTransparent => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool IsSerializable => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override bool IsSubclassOf(Type c) => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ protected sealed override bool IsValueTypeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override StructLayoutAttribute StructLayoutAttribute => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ public sealed override RuntimeTypeHandle TypeHandle => throw new NotSupportedException(SR.NotSupported_SignatureType);
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/SignatureTypeExtensions.cs b/src/mscorlib/shared/System/Reflection/SignatureTypeExtensions.cs
new file mode 100644
index 0000000000..b30da50073
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/SignatureTypeExtensions.cs
@@ -0,0 +1,227 @@
+// 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.Diagnostics;
+
+namespace System.Reflection
+{
+#if CORECLR
+ internal
+#else
+ public // Needs to be public so that Reflection.Core can see it.
+#endif
+ static class SignatureTypeExtensions
+ {
+ /// <summary>
+ /// This is semantically identical to
+ ///
+ /// parameter.ParameterType == pattern.TryResolveAgainstGenericMethod(parameter.Member)
+ ///
+ /// but without the allocation overhead of TryResolve.
+ /// </summary>
+ public static bool MatchesParameterTypeExactly(this Type pattern, ParameterInfo parameter)
+ {
+ if (pattern is SignatureType signatureType)
+ return signatureType.MatchesExactly(parameter.ParameterType);
+ else
+ return pattern == (object)(parameter.ParameterType);
+ }
+
+ /// <summary>
+ /// This is semantically identical to
+ ///
+ /// actual == pattern.TryResolveAgainstGenericMethod(parameterMember)
+ ///
+ /// but without the allocation overhead of TryResolve.
+ /// </summary>
+ internal static bool MatchesExactly(this SignatureType pattern, Type actual)
+ {
+ if (pattern.IsSZArray)
+ {
+ return actual.IsSZArray && pattern.ElementType.MatchesExactly(actual.GetElementType());
+ }
+ else if (pattern.IsVariableBoundArray)
+ {
+ return actual.IsVariableBoundArray && pattern.GetArrayRank() == actual.GetArrayRank() && pattern.ElementType.MatchesExactly(actual.GetElementType());
+ }
+ else if (pattern.IsByRef)
+ {
+ return actual.IsByRef && pattern.ElementType.MatchesExactly(actual.GetElementType());
+ }
+ else if (pattern.IsPointer)
+ {
+ return actual.IsPointer && pattern.ElementType.MatchesExactly(actual.GetElementType());
+ }
+ else if (pattern.IsConstructedGenericType)
+ {
+ if (!actual.IsConstructedGenericType)
+ return false;
+ if (!(pattern.GetGenericTypeDefinition() == actual.GetGenericTypeDefinition()))
+ return false;
+ Type[] patternGenericTypeArguments = pattern.GenericTypeArguments;
+ Type[] actualGenericTypeArguments = actual.GenericTypeArguments;
+ int count = patternGenericTypeArguments.Length;
+ if (count != actualGenericTypeArguments.Length)
+ return false;
+ for (int i = 0; i < count; i++)
+ {
+ Type patternGenericTypeArgument = patternGenericTypeArguments[i];
+ if (patternGenericTypeArgument is SignatureType signatureType)
+ {
+ if (!signatureType.MatchesExactly(actualGenericTypeArguments[i]))
+ return false;
+ }
+ else
+ {
+ if (patternGenericTypeArgument != actualGenericTypeArguments[i])
+ return false;
+ }
+ }
+ return true;
+ }
+ else if (pattern.IsGenericMethodParameter)
+ {
+ if (!(actual.IsGenericParameter && actual.DeclaringMethod != null))
+ return false;
+ if (pattern.GenericParameterPosition != actual.GenericParameterPosition)
+ return false;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Translates a SignatureType into its equivalent resolved Type by recursively substituting all generic parameter references
+ /// with its corresponding generic parameter definition. This is slow so MatchesExactly or MatchesParameterTypeExactly should be
+ /// substituted instead whenever possible. This is only used by the DefaultBinder when its fast-path checks have been exhausted and
+ /// it needs to call non-trivial methods like IsAssignableFrom which SignatureTypes will never support.
+ ///
+ /// Because this method is used to eliminate method candidates in a GetMethod() lookup, it is entirely possible that the Type
+ /// might not be creatable due to conflicting generic constraints. Since this merely implies that this candidate is not
+ /// the method we're looking for, we return null rather than let the TypeLoadException bubble up. The DefaultBinder will catch
+ /// the null and continue its search for a better candidate.
+ /// </summary>
+ internal static Type TryResolveAgainstGenericMethod(this SignatureType signatureType, MethodInfo genericMethod)
+ {
+ return signatureType.TryResolve(genericMethod.GetGenericArguments());
+ }
+
+ private static Type TryResolve(this SignatureType signatureType, Type[] genericMethodParameters)
+ {
+ if (signatureType.IsSZArray)
+ {
+ return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeArrayType();
+ }
+ else if (signatureType.IsVariableBoundArray)
+ {
+ return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeArrayType(signatureType.GetArrayRank());
+ }
+ else if (signatureType.IsByRef)
+ {
+ return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeByRefType();
+ }
+ else if (signatureType.IsPointer)
+ {
+ return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakePointerType();
+ }
+ else if (signatureType.IsConstructedGenericType)
+ {
+ Type[] genericTypeArguments = signatureType.GenericTypeArguments;
+ int count = genericTypeArguments.Length;
+ Type[] newGenericTypeArguments = new Type[count];
+ for (int i = 0; i < count; i++)
+ {
+ Type genericTypeArgument = genericTypeArguments[i];
+ if (genericTypeArgument is SignatureType signatureGenericTypeArgument)
+ {
+ newGenericTypeArguments[i] = signatureGenericTypeArgument.TryResolve(genericMethodParameters);
+ if (newGenericTypeArguments[i] == null)
+ return null;
+ }
+ else
+ {
+ newGenericTypeArguments[i] = genericTypeArgument;
+ }
+ }
+ return signatureType.GetGenericTypeDefinition().TryMakeGenericType(newGenericTypeArguments);
+ }
+ else if (signatureType.IsGenericMethodParameter)
+ {
+ int position = signatureType.GenericParameterPosition;
+ if (position >= genericMethodParameters.Length)
+ return null;
+ return genericMethodParameters[position];
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private static Type TryMakeArrayType(this Type type)
+ {
+ try
+ {
+ return type.MakeArrayType();
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private static Type TryMakeArrayType(this Type type, int rank)
+ {
+ try
+ {
+ return type.MakeArrayType(rank);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private static Type TryMakeByRefType(this Type type)
+ {
+ try
+ {
+ return type.MakeByRefType();
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private static Type TryMakePointerType(this Type type)
+ {
+ try
+ {
+ return type.MakePointerType();
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private static Type TryMakeGenericType(this Type type, Type[] instantiation)
+ {
+ try
+ {
+ return type.MakeGenericType(instantiation);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/TargetException.cs b/src/mscorlib/shared/System/Reflection/TargetException.cs
index 6e43f56fa4..2309daace1 100644
--- a/src/mscorlib/shared/System/Reflection/TargetException.cs
+++ b/src/mscorlib/shared/System/Reflection/TargetException.cs
@@ -21,7 +21,7 @@ namespace System.Reflection
public TargetException(string message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_TARGET;
+ HResult = HResults.COR_E_TARGET;
}
protected TargetException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/Reflection/TargetInvocationException.cs b/src/mscorlib/shared/System/Reflection/TargetInvocationException.cs
index 8d0bfef40d..7c4f60a03e 100644
--- a/src/mscorlib/shared/System/Reflection/TargetInvocationException.cs
+++ b/src/mscorlib/shared/System/Reflection/TargetInvocationException.cs
@@ -11,13 +11,13 @@ namespace System.Reflection
public TargetInvocationException(Exception inner)
: base(SR.Arg_TargetInvocationException, inner)
{
- HResult = __HResults.COR_E_TARGETINVOCATION;
+ HResult = HResults.COR_E_TARGETINVOCATION;
}
public TargetInvocationException(string message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_TARGETINVOCATION;
+ HResult = HResults.COR_E_TARGETINVOCATION;
}
}
}
diff --git a/src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs b/src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs
index e200cdb94f..f31758b7ba 100644
--- a/src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs
+++ b/src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs
@@ -11,19 +11,19 @@ namespace System.Reflection
public TargetParameterCountException()
: base(SR.Arg_TargetParameterCountException)
{
- HResult = __HResults.COR_E_TARGETPARAMCOUNT;
+ HResult = HResults.COR_E_TARGETPARAMCOUNT;
}
public TargetParameterCountException(string message)
: base(message)
{
- HResult = __HResults.COR_E_TARGETPARAMCOUNT;
+ HResult = HResults.COR_E_TARGETPARAMCOUNT;
}
public TargetParameterCountException(string message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_TARGETPARAMCOUNT;
+ HResult = HResults.COR_E_TARGETPARAMCOUNT;
}
}
}
diff --git a/src/mscorlib/shared/System/Reflection/TypeDelegator.cs b/src/mscorlib/shared/System/Reflection/TypeDelegator.cs
index bcbff05d62..1dba278893 100644
--- a/src/mscorlib/shared/System/Reflection/TypeDelegator.cs
+++ b/src/mscorlib/shared/System/Reflection/TypeDelegator.cs
@@ -108,6 +108,7 @@ namespace System.Reflection
protected override bool IsPointerImpl() => typeImpl.IsPointer;
protected override bool IsValueTypeImpl() => typeImpl.IsValueType;
protected override bool IsCOMObjectImpl() => typeImpl.IsCOMObject;
+ public override bool IsByRefLike => typeImpl.IsByRefLike;
public override bool IsConstructedGenericType => typeImpl.IsConstructedGenericType;
public override Type GetElementType() => typeImpl.GetElementType();
protected override bool HasElementTypeImpl() => typeImpl.HasElementType;
diff --git a/src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs b/src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs
index ec814393d0..e11d15d200 100644
--- a/src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs
+++ b/src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs
@@ -12,19 +12,19 @@ namespace System.Resources
public MissingManifestResourceException()
: base(SR.Arg_MissingManifestResourceException)
{
- HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE;
+ HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE;
}
public MissingManifestResourceException(string message)
: base(message)
{
- HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE;
+ HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE;
}
public MissingManifestResourceException(string message, Exception inner)
: base(message, inner)
{
- HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE;
+ HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE;
}
protected MissingManifestResourceException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs b/src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs
index d2ddc992ac..615aed6a8c 100644
--- a/src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs
+++ b/src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs
@@ -27,26 +27,26 @@ namespace System.Resources
public MissingSatelliteAssemblyException()
: base(SR.MissingSatelliteAssembly_Default)
{
- HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY;
+ HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY;
}
public MissingSatelliteAssemblyException(string message)
: base(message)
{
- HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY;
+ HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY;
}
public MissingSatelliteAssemblyException(string message, String cultureName)
: base(message)
{
- HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY;
+ HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY;
_cultureName = cultureName;
}
public MissingSatelliteAssemblyException(string message, Exception inner)
: base(message, inner)
{
- HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY;
+ HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY;
}
protected MissingSatelliteAssemblyException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs
new file mode 100644
index 0000000000..688a3a01ba
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs
@@ -0,0 +1,21 @@
+// 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.Runtime.CompilerServices
+{
+ /// <summary>
+ /// Indicates the type of the async method builder that should be used by a language compiler to
+ /// build the attributed type when used as the return type of an async method.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | AttributeTargets.Enum, Inherited = false, AllowMultiple = false)]
+ public sealed class AsyncMethodBuilderAttribute : Attribute
+ {
+ /// <summary>Initializes the <see cref="AsyncMethodBuilderAttribute"/>.</summary>
+ /// <param name="builderType">The <see cref="Type"/> of the associated builder.</param>
+ public AsyncMethodBuilderAttribute(Type builderType) => BuilderType = builderType;
+
+ /// <summary>Gets the <see cref="Type"/> of the associated builder.</summary>
+ public Type BuilderType { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs
new file mode 100644
index 0000000000..813d9e5cd7
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs
@@ -0,0 +1,111 @@
+// 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.Security;
+using System.Threading.Tasks;
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>Represents a builder for asynchronous methods that returns a <see cref="ValueTask{TResult}"/>.</summary>
+ /// <typeparam name="TResult">The type of the result.</typeparam>
+ [StructLayout(LayoutKind.Auto)]
+ public struct AsyncValueTaskMethodBuilder<TResult>
+ {
+ /// <summary>The <see cref="AsyncTaskMethodBuilder{TResult}"/> to which most operations are delegated.</summary>
+ private AsyncTaskMethodBuilder<TResult> _methodBuilder; // mutable struct; do not make it readonly
+ /// <summary>The result for this builder, if it's completed before any awaits occur.</summary>
+ private TResult _result;
+ /// <summary>true if <see cref="_result"/> contains the synchronous result for the async method; otherwise, false.</summary>
+ private bool _haveResult;
+ /// <summary>true if the builder should be used for setting/getting the result; otherwise, false.</summary>
+ private bool _useBuilder;
+
+ /// <summary>Creates an instance of the <see cref="AsyncValueTaskMethodBuilder{TResult}"/> struct.</summary>
+ /// <returns>The initialized instance.</returns>
+ public static AsyncValueTaskMethodBuilder<TResult> Create() =>
+#if CORECLR
+ // _methodBuilder should be initialized to AsyncTaskMethodBuilder<TResult>.Create(), but on coreclr
+ // that Create() is a nop, so we can just return the default here.
+ default(AsyncValueTaskMethodBuilder<TResult>);
+#else
+ // corert's AsyncTaskMethodBuilder<TResult>.Create() currently does additional debugger-related
+ // work, so we need to delegate to it.
+ new AsyncValueTaskMethodBuilder<TResult>() { _methodBuilder = AsyncTaskMethodBuilder<TResult>.Create() };
+#endif
+
+ /// <summary>Begins running the builder with the associated state machine.</summary>
+ /// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
+ /// <param name="stateMachine">The state machine instance, passed by reference.</param>
+ public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine =>
+ _methodBuilder.Start(ref stateMachine); // will provide the right ExecutionContext semantics
+
+ /// <summary>Associates the builder with the specified state machine.</summary>
+ /// <param name="stateMachine">The state machine instance to associate with the builder.</param>
+ public void SetStateMachine(IAsyncStateMachine stateMachine) => _methodBuilder.SetStateMachine(stateMachine);
+
+ /// <summary>Marks the task as successfully completed.</summary>
+ /// <param name="result">The result to use to complete the task.</param>
+ public void SetResult(TResult result)
+ {
+ if (_useBuilder)
+ {
+ _methodBuilder.SetResult(result);
+ }
+ else
+ {
+ _result = result;
+ _haveResult = true;
+ }
+ }
+
+ /// <summary>Marks the task as failed and binds the specified exception to the task.</summary>
+ /// <param name="exception">The exception to bind to the task.</param>
+ public void SetException(Exception exception) => _methodBuilder.SetException(exception);
+
+ /// <summary>Gets the task for this builder.</summary>
+ public ValueTask<TResult> Task
+ {
+ get
+ {
+ if (_haveResult)
+ {
+ return new ValueTask<TResult>(_result);
+ }
+ else
+ {
+ _useBuilder = true;
+ return new ValueTask<TResult>(_methodBuilder.Task);
+ }
+ }
+ }
+
+ /// <summary>Schedules the state machine to proceed to the next action when the specified awaiter completes.</summary>
+ /// <typeparam name="TAwaiter">The type of the awaiter.</typeparam>
+ /// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
+ /// <param name="awaiter">the awaiter</param>
+ /// <param name="stateMachine">The state machine.</param>
+ public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
+ where TAwaiter : INotifyCompletion
+ where TStateMachine : IAsyncStateMachine
+ {
+ _useBuilder = true;
+ _methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine);
+ }
+
+ /// <summary>Schedules the state machine to proceed to the next action when the specified awaiter completes.</summary>
+ /// <typeparam name="TAwaiter">The type of the awaiter.</typeparam>
+ /// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
+ /// <param name="awaiter">the awaiter</param>
+ /// <param name="stateMachine">The state machine.</param>
+ [SecuritySafeCritical]
+ public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
+ where TAwaiter : ICriticalNotifyCompletion
+ where TStateMachine : IAsyncStateMachine
+ {
+ _useBuilder = true;
+ _methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs
new file mode 100644
index 0000000000..4ec931c4af
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs
@@ -0,0 +1,71 @@
+// 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.Threading.Tasks;
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>Provides an awaitable type that enables configured awaits on a <see cref="ValueTask{TResult}"/>.</summary>
+ /// <typeparam name="TResult">The type of the result produced.</typeparam>
+ [StructLayout(LayoutKind.Auto)]
+ public struct ConfiguredValueTaskAwaitable<TResult>
+ {
+ /// <summary>The wrapped <see cref="ValueTask{TResult}"/>.</summary>
+ private readonly ValueTask<TResult> _value;
+ /// <summary>true to attempt to marshal the continuation back to the original context captured; otherwise, false.</summary>
+ private readonly bool _continueOnCapturedContext;
+
+ /// <summary>Initializes the awaitable.</summary>
+ /// <param name="value">The wrapped <see cref="ValueTask{TResult}"/>.</param>
+ /// <param name="continueOnCapturedContext">
+ /// true to attempt to marshal the continuation back to the original synchronization context captured; otherwise, false.
+ /// </param>
+ internal ConfiguredValueTaskAwaitable(ValueTask<TResult> value, bool continueOnCapturedContext)
+ {
+ _value = value;
+ _continueOnCapturedContext = continueOnCapturedContext;
+ }
+
+ /// <summary>Returns an awaiter for this <see cref="ConfiguredValueTaskAwaitable{TResult}"/> instance.</summary>
+ public ConfiguredValueTaskAwaiter GetAwaiter() =>
+ new ConfiguredValueTaskAwaiter(_value, _continueOnCapturedContext);
+
+ /// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
+ [StructLayout(LayoutKind.Auto)]
+ public struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion
+ {
+ /// <summary>The value being awaited.</summary>
+ private ValueTask<TResult> _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies
+ /// <summary>The value to pass to ConfigureAwait.</summary>
+ private readonly bool _continueOnCapturedContext;
+
+ /// <summary>Initializes the awaiter.</summary>
+ /// <param name="value">The value to be awaited.</param>
+ /// <param name="continueOnCapturedContext">The value to pass to ConfigureAwait.</param>
+ internal ConfiguredValueTaskAwaiter(ValueTask<TResult> value, bool continueOnCapturedContext)
+ {
+ _value = value;
+ _continueOnCapturedContext = continueOnCapturedContext;
+ }
+
+ /// <summary>Gets whether the <see cref="ConfiguredValueTaskAwaitable{TResult}"/> has completed.</summary>
+ public bool IsCompleted => _value.IsCompleted;
+
+ /// <summary>Gets the result of the ValueTask.</summary>
+ public TResult GetResult() =>
+ _value._task == null ?
+ _value._result :
+ _value._task.GetAwaiter().GetResult();
+
+ /// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
+ public void OnCompleted(Action continuation) =>
+ _value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
+
+ /// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
+ public void UnsafeOnCompleted(Action continuation) =>
+ _value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/CustomConstantAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/CustomConstantAttribute.cs
new file mode 100644
index 0000000000..f75693eb40
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/CustomConstantAttribute.cs
@@ -0,0 +1,12 @@
+// 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.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)]
+ public abstract class CustomConstantAttribute : Attribute
+ {
+ public abstract Object Value { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs
new file mode 100644
index 0000000000..813e6803bf
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs
@@ -0,0 +1,19 @@
+// 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.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)]
+ public sealed class DateTimeConstantAttribute : CustomConstantAttribute
+ {
+ private DateTime _date;
+
+ public DateTimeConstantAttribute(long ticks)
+ {
+ _date = new DateTime(ticks);
+ }
+
+ public override Object Value => _date;
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/DecimalConstantAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/DecimalConstantAttribute.cs
new file mode 100644
index 0000000000..19db84eb43
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/DecimalConstantAttribute.cs
@@ -0,0 +1,39 @@
+// 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.
+
+// Note: If you add a new ctor overloads you need to update ParameterInfo.RawDefaultValue
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)]
+ public sealed class DecimalConstantAttribute : Attribute
+ {
+ private Decimal _dec;
+
+ [CLSCompliant(false)]
+ public DecimalConstantAttribute(
+ byte scale,
+ byte sign,
+ uint hi,
+ uint mid,
+ uint low
+ )
+ {
+ _dec = new Decimal((int)low, (int)mid, (int)hi, (sign != 0), scale);
+ }
+
+ public DecimalConstantAttribute(
+ byte scale,
+ byte sign,
+ int hi,
+ int mid,
+ int low
+ )
+ {
+ _dec = new Decimal(low, mid, hi, (sign != 0), scale);
+ }
+
+ public Decimal Value => _dec;
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs
new file mode 100644
index 0000000000..dd01bacc49
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs
@@ -0,0 +1,13 @@
+// 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.Runtime.CompilerServices
+{
+ // Calls to methods or references to fields marked with this attribute may be replaced at
+ // some call sites with jit intrinsic expansions.
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)]
+ internal sealed class IntrinsicAttribute : Attribute
+ {
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/MethodImplAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/MethodImplAttribute.cs
new file mode 100644
index 0000000000..8e8f93c268
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/MethodImplAttribute.cs
@@ -0,0 +1,29 @@
+// 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.Runtime.CompilerServices
+{
+ // Custom attribute to specify additional method properties.
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)]
+ sealed public class MethodImplAttribute : Attribute
+ {
+ public MethodCodeType MethodCodeType;
+
+ public MethodImplAttribute(MethodImplOptions methodImplOptions)
+ {
+ Value = methodImplOptions;
+ }
+
+ public MethodImplAttribute(short value)
+ {
+ Value = (MethodImplOptions)value;
+ }
+
+ public MethodImplAttribute()
+ {
+ }
+
+ public MethodImplOptions Value { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeWrappedException.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeWrappedException.cs
new file mode 100644
index 0000000000..e4af9be678
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeWrappedException.cs
@@ -0,0 +1,33 @@
+// 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.Serialization;
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>
+ /// Exception used to wrap all non-CLS compliant exceptions.
+ /// </summary>
+ public sealed class RuntimeWrappedException : Exception
+ {
+ private Object _wrappedException; // EE expects this name
+
+ // Not an api but has to be public as System.Linq.Expression invokes this through Reflection when an expression
+ // throws an object that doesn't derive from Exception.
+ public RuntimeWrappedException(Object thrownObject)
+ : base(SR.RuntimeWrappedException)
+ {
+ HResult = HResults.COR_E_RUNTIMEWRAPPED;
+ _wrappedException = thrownObject;
+ }
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData(info, context);
+ }
+
+ public Object WrappedException => _wrappedException;
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
new file mode 100644
index 0000000000..c419482521
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
@@ -0,0 +1,37 @@
+// 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.Threading.Tasks;
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>Provides an awaiter for a <see cref="ValueTask{TResult}"/>.</summary>
+ public struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion
+ {
+ /// <summary>The value being awaited.</summary>
+ private ValueTask<TResult> _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies
+
+ /// <summary>Initializes the awaiter.</summary>
+ /// <param name="value">The value to be awaited.</param>
+ internal ValueTaskAwaiter(ValueTask<TResult> value) => _value = value;
+
+ /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> has completed.</summary>
+ public bool IsCompleted => _value.IsCompleted;
+
+ /// <summary>Gets the result of the ValueTask.</summary>
+ public TResult GetResult() =>
+ _value._task == null ?
+ _value._result :
+ _value._task.GetAwaiter().GetResult();
+
+ /// <summary>Schedules the continuation action for this ValueTask.</summary>
+ public void OnCompleted(Action continuation) =>
+ _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().OnCompleted(continuation);
+
+ /// <summary>Schedules the continuation action for this ValueTask.</summary>
+ public void UnsafeOnCompleted(Action continuation) =>
+ _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().UnsafeOnCompleted(continuation);
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/ExceptionServices/CorruptingExceptionCommon.cs b/src/mscorlib/shared/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs
index 00067748f2..cc1bc81e5a 100644
--- a/src/mscorlib/src/System/Runtime/ExceptionServices/CorruptingExceptionCommon.cs
+++ b/src/mscorlib/shared/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs
@@ -2,20 +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: Contains common usage support entities for Corrupting Exceptions
-**
-** Created: 06/20/2008
-**
-**
-**
-=============================================================================*/
-
-using System;
-
namespace System.Runtime.ExceptionServices
{
// This attribute can be applied to methods to indicate that ProcessCorruptedState
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/BestFitMappingAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/BestFitMappingAttribute.cs
new file mode 100644
index 0000000000..4ebee1538c
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/BestFitMappingAttribute.cs
@@ -0,0 +1,19 @@
+// 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.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
+ public sealed class BestFitMappingAttribute : Attribute
+ {
+ public BestFitMappingAttribute(bool BestFitMapping)
+ {
+ this.BestFitMapping = BestFitMapping;
+ }
+
+ public bool BestFitMapping { get; }
+
+ public bool ThrowOnUnmappableChar;
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/DefaultCharSetAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/DefaultCharSetAttribute.cs
new file mode 100644
index 0000000000..7a486f7017
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/DefaultCharSetAttribute.cs
@@ -0,0 +1,17 @@
+// 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.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Module, Inherited = false)]
+ public sealed class DefaultCharSetAttribute : Attribute
+ {
+ public DefaultCharSetAttribute(CharSet charSet)
+ {
+ CharSet = charSet;
+ }
+
+ public CharSet CharSet { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs
new file mode 100644
index 0000000000..1ff27fbbd5
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs
@@ -0,0 +1,17 @@
+// 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.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method, AllowMultiple = false)]
+ public sealed class DefaultDllImportSearchPathsAttribute : Attribute
+ {
+ public DefaultDllImportSearchPathsAttribute(DllImportSearchPath paths)
+ {
+ Paths = paths;
+ }
+
+ public DllImportSearchPath Paths { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/DllImportAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/DllImportAttribute.cs
new file mode 100644
index 0000000000..97f870d49c
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/DllImportAttribute.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.
+
+namespace System.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Method, Inherited = false)]
+ public sealed class DllImportAttribute : Attribute
+ {
+ public DllImportAttribute(string dllName)
+ {
+ Value = dllName;
+ }
+
+ public string Value { get; }
+
+ public string EntryPoint;
+ public CharSet CharSet;
+ public bool SetLastError;
+ public bool ExactSpelling;
+ public CallingConvention CallingConvention;
+ public bool BestFitMapping;
+ public bool PreserveSig;
+ public bool ThrowOnUnmappableChar;
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/DllImportSearchPath.cs b/src/mscorlib/shared/System/Runtime/InteropServices/DllImportSearchPath.cs
new file mode 100644
index 0000000000..8dbdb40be9
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/DllImportSearchPath.cs
@@ -0,0 +1,18 @@
+// 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.Runtime.InteropServices
+{
+ [Flags]
+ public enum DllImportSearchPath
+ {
+ UseDllDirectoryForDependencies = 0x100,
+ ApplicationDirectory = 0x200,
+ UserDirectories = 0x400,
+ System32 = 0x800,
+ SafeDirectories = 0x1000,
+ AssemblyDirectory = 0x2,
+ LegacyBehavior = 0x0
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs b/src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs
index 81b9a46928..0861d19362 100644
--- a/src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs
@@ -26,19 +26,19 @@ namespace System.Runtime.InteropServices
public ExternalException()
: base(SR.Arg_ExternalException)
{
- HResult = __HResults.E_FAIL;
+ HResult = HResults.E_FAIL;
}
public ExternalException(string message)
: base(message)
{
- HResult = __HResults.E_FAIL;
+ HResult = HResults.E_FAIL;
}
public ExternalException(string message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.E_FAIL;
+ HResult = HResults.E_FAIL;
}
public ExternalException(string message, int errorCode)
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/FieldOffsetAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/FieldOffsetAttribute.cs
new file mode 100644
index 0000000000..27e1097749
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/FieldOffsetAttribute.cs
@@ -0,0 +1,17 @@
+// 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.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Field, Inherited = false)]
+ public sealed class FieldOffsetAttribute : Attribute
+ {
+ public FieldOffsetAttribute(int offset)
+ {
+ Value = offset;
+ }
+
+ public int Value { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/InAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/InAttribute.cs
new file mode 100644
index 0000000000..39f5a958bc
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/InAttribute.cs
@@ -0,0 +1,14 @@
+// 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.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+ public sealed class InAttribute : Attribute
+ {
+ public InAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/MarshalAsAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/MarshalAsAttribute.cs
new file mode 100644
index 0000000000..4a64050ed1
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/MarshalAsAttribute.cs
@@ -0,0 +1,39 @@
+// 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.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.ReturnValue, Inherited = false)]
+ public sealed class MarshalAsAttribute : Attribute
+ {
+ public MarshalAsAttribute(UnmanagedType unmanagedType)
+ {
+ Value = unmanagedType;
+ }
+ public MarshalAsAttribute(short unmanagedType)
+ {
+ Value = (UnmanagedType)unmanagedType;
+ }
+
+ public UnmanagedType Value { get; }
+
+ // Fields used with SubType = SafeArray.
+ public VarEnum SafeArraySubType;
+ public Type SafeArrayUserDefinedSubType;
+
+ // Field used with iid_is attribute (interface pointers).
+ public int IidParameterIndex;
+
+ // Fields used with SubType = ByValArray and LPArray.
+ // Array size = parameter(PI) * PM + C
+ public UnmanagedType ArraySubType;
+ public short SizeParamIndex; // param index PI
+ public int SizeConst; // constant C
+
+ // Fields used with SubType = CustomMarshaler
+ public string MarshalType; // Name of marshaler class
+ public Type MarshalTypeRef; // Type of marshaler class
+ public string MarshalCookie; // cookie to pass to marshaler
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/OptionalAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/OptionalAttribute.cs
new file mode 100644
index 0000000000..5ac75d7b3e
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/OptionalAttribute.cs
@@ -0,0 +1,14 @@
+// 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.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+ public sealed class OptionalAttribute : Attribute
+ {
+ public OptionalAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/OutAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/OutAttribute.cs
new file mode 100644
index 0000000000..338ceac91e
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/OutAttribute.cs
@@ -0,0 +1,14 @@
+// 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.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+ public sealed class OutAttribute : Attribute
+ {
+ public OutAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/PreserveSigAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/PreserveSigAttribute.cs
new file mode 100644
index 0000000000..464e1abcbe
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/PreserveSigAttribute.cs
@@ -0,0 +1,14 @@
+// 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.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Method, Inherited = false)]
+ public sealed class PreserveSigAttribute : Attribute
+ {
+ public PreserveSigAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/StructLayoutAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/StructLayoutAttribute.cs
new file mode 100644
index 0000000000..c4cce9956e
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/StructLayoutAttribute.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.
+
+namespace System.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
+ public sealed class StructLayoutAttribute : Attribute
+ {
+ public StructLayoutAttribute(LayoutKind layoutKind)
+ {
+ Value = layoutKind;
+ }
+
+ public StructLayoutAttribute(short layoutKind)
+ {
+ Value = (LayoutKind)layoutKind;
+ }
+
+ public LayoutKind Value { get; }
+
+ public int Pack;
+ public int Size;
+ public CharSet CharSet;
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs
index 2d69c95afe..c4f96903ee 100644
--- a/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs
@@ -7,11 +7,6 @@ namespace System.Runtime.InteropServices
[AttributeUsage(AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
public sealed class UnmanagedFunctionPointerAttribute : Attribute
{
- public bool BestFitMapping;
- public bool SetLastError;
- public bool ThrowOnUnmappableChar;
- public CharSet CharSet;
-
public UnmanagedFunctionPointerAttribute()
{
CallingConvention = CallingConvention.Winapi;
@@ -23,5 +18,10 @@ namespace System.Runtime.InteropServices
}
public CallingConvention CallingConvention { get; }
+
+ public bool BestFitMapping;
+ public bool SetLastError;
+ public bool ThrowOnUnmappableChar;
+ public CharSet CharSet;
}
}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs b/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs
index bfe3313659..3e0af092c0 100644
--- a/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs
+++ b/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs
@@ -15,19 +15,19 @@ namespace System.Runtime.Serialization
public SerializationException()
: base(s_nullMessage)
{
- HResult = __HResults.COR_E_SERIALIZATION;
+ HResult = HResults.COR_E_SERIALIZATION;
}
public SerializationException(String message)
: base(message)
{
- HResult = __HResults.COR_E_SERIALIZATION;
+ HResult = HResults.COR_E_SERIALIZATION;
}
public SerializationException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_SERIALIZATION;
+ HResult = HResults.COR_E_SERIALIZATION;
}
protected SerializationException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/src/System/SByte.cs b/src/mscorlib/shared/System/SByte.cs
index 8a0ab3e129..c984767336 100644
--- a/src/mscorlib/src/System/SByte.cs
+++ b/src/mscorlib/shared/System/SByte.cs
@@ -2,28 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-/*============================================================
-**
-**
-**
-** Purpose:
-**
-**
-===========================================================*/
-
+using System.Diagnostics.Contracts;
using System.Globalization;
-using System;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Diagnostics.Contracts;
+using System.Runtime.Versioning;
namespace System
{
- // A place holder class for signed bytes.
[Serializable]
- [CLSCompliant(false), System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct SByte : IComparable, IFormattable, IConvertible
- , IComparable<SByte>, IEquatable<SByte>
+ [CLSCompliant(false)] [StructLayout(LayoutKind.Sequential)]
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public struct SByte : IComparable, IConvertible, IFormattable, IComparable<SByte>, IEquatable<SByte>
{
private sbyte m_value; // Do not rename (binary serialization)
@@ -68,7 +58,7 @@ namespace System
return m_value == ((SByte)obj).m_value;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public bool Equals(SByte obj)
{
return m_value == obj;
@@ -121,20 +111,23 @@ namespace System
[CLSCompliant(false)]
public static sbyte Parse(String s)
{
- return Parse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
[CLSCompliant(false)]
public static sbyte Parse(String s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
- return Parse(s, style, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo);
}
[CLSCompliant(false)]
public static sbyte Parse(String s, IFormatProvider provider)
{
- return Parse(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
// Parses a signed byte from a String in the given style. If
@@ -145,11 +138,25 @@ namespace System
public static sbyte Parse(String s, NumberStyles style, IFormatProvider provider)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Parse(s, style, NumberFormatInfo.GetInstance(provider));
}
private static sbyte Parse(String s, NumberStyles style, NumberFormatInfo info)
{
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), style, info);
+ }
+
+ private static sbyte Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
+ {
int i = 0;
try
{
@@ -176,17 +183,37 @@ namespace System
[CLSCompliant(false)]
public static bool TryParse(String s, out SByte result)
{
- return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return TryParse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
[CLSCompliant(false)]
public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out SByte result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ [CLSCompliant(false)]
+ public static bool TryParse(ReadOnlySpan<char> s, out sbyte result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result);
}
- private static bool TryParse(String s, NumberStyles style, NumberFormatInfo info, out SByte result)
+ private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out SByte result)
{
result = 0;
int i;
diff --git a/src/mscorlib/shared/System/Security/SecurityException.cs b/src/mscorlib/shared/System/Security/SecurityException.cs
index 538f475343..2866a4780c 100644
--- a/src/mscorlib/shared/System/Security/SecurityException.cs
+++ b/src/mscorlib/shared/System/Security/SecurityException.cs
@@ -12,32 +12,32 @@ namespace System.Security
public SecurityException()
: base(SR.Arg_SecurityException)
{
- HResult = __HResults.COR_E_SECURITY;
+ HResult = HResults.COR_E_SECURITY;
}
public SecurityException(string message)
: base(message)
{
- HResult = __HResults.COR_E_SECURITY;
+ HResult = HResults.COR_E_SECURITY;
}
public SecurityException(string message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_SECURITY;
+ HResult = HResults.COR_E_SECURITY;
}
public SecurityException(string message, Type type)
: base(message)
{
- HResult = __HResults.COR_E_SECURITY;
+ HResult = HResults.COR_E_SECURITY;
PermissionType = type;
}
public SecurityException(string message, Type type, string state)
: base(message)
{
- HResult = __HResults.COR_E_SECURITY;
+ HResult = HResults.COR_E_SECURITY;
PermissionType = type;
PermissionState = state;
}
diff --git a/src/mscorlib/shared/System/Security/VerificationException.cs b/src/mscorlib/shared/System/Security/VerificationException.cs
index ea5a75906e..bd095f3df4 100644
--- a/src/mscorlib/shared/System/Security/VerificationException.cs
+++ b/src/mscorlib/shared/System/Security/VerificationException.cs
@@ -11,19 +11,19 @@ namespace System.Security
public VerificationException()
: base(SR.Verification_Exception)
{
- HResult = __HResults.COR_E_VERIFICATION;
+ HResult = HResults.COR_E_VERIFICATION;
}
public VerificationException(string message)
: base(message)
{
- HResult = __HResults.COR_E_VERIFICATION;
+ HResult = HResults.COR_E_VERIFICATION;
}
public VerificationException(string message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_VERIFICATION;
+ HResult = HResults.COR_E_VERIFICATION;
}
protected VerificationException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/SerializableAttribute.cs b/src/mscorlib/shared/System/SerializableAttribute.cs
new file mode 100644
index 0000000000..c256931373
--- /dev/null
+++ b/src/mscorlib/shared/System/SerializableAttribute.cs
@@ -0,0 +1,12 @@
+// 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
+{
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate, Inherited = false)]
+ public sealed class SerializableAttribute : Attribute
+ {
+ public SerializableAttribute() { }
+ }
+}
diff --git a/src/mscorlib/src/System/Single.cs b/src/mscorlib/shared/System/Single.cs
index 2bdc2a9696..0be8cfbc37 100644
--- a/src/mscorlib/src/System/Single.cs
+++ b/src/mscorlib/shared/System/Single.cs
@@ -11,20 +11,18 @@
**
===========================================================*/
+using System.Diagnostics.Contracts;
using System.Globalization;
-using System;
-using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
-using System.Runtime.ConstrainedExecution;
-using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
namespace System
{
[Serializable]
- [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct Single : IComparable, IFormattable, IConvertible
- , IComparable<Single>, IEquatable<Single>
+ [StructLayout(LayoutKind.Sequential)]
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public struct Single : IComparable, IConvertible, IFormattable, IComparable<Single>, IEquatable<Single>
{
private float m_value; // Do not rename (binary serialization)
@@ -38,11 +36,12 @@ namespace System
public const float NegativeInfinity = (float)-1.0 / (float)0.0;
public const float NaN = (float)0.0 / (float)0.0;
- internal static float NegativeZero = BitConverter.Int32BitsToSingle(unchecked((int)0x80000000));
+ // We use this explicit definition to avoid the confusion between 0.0 and -0.0.
+ internal const float NegativeZero = (float)-0.0;
/// <summary>Determines whether the specified value is finite (zero, subnormal, or normal).</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsFinite(float f)
{
@@ -52,7 +51,7 @@ namespace System
/// <summary>Determines whether the specified value is infinite.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static bool IsInfinity(float f)
{
@@ -62,7 +61,7 @@ namespace System
/// <summary>Determines whether the specified value is NaN.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static bool IsNaN(float f)
{
@@ -72,7 +71,7 @@ namespace System
/// <summary>Determines whether the specified value is negative.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static bool IsNegative(float f)
{
@@ -82,7 +81,7 @@ namespace System
/// <summary>Determines whether the specified value is negative infinity.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static bool IsNegativeInfinity(float f)
{
@@ -91,7 +90,7 @@ namespace System
/// <summary>Determines whether the specified value is normal.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
// This is probably not worth inlining, it has branches and should be rarely called
public unsafe static bool IsNormal(float f)
{
@@ -102,7 +101,7 @@ namespace System
/// <summary>Determines whether the specified value is positive infinity.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static bool IsPositiveInfinity(float f)
{
@@ -111,7 +110,7 @@ namespace System
/// <summary>Determines whether the specified value is subnormal.</summary>
[Pure]
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
// This is probably not worth inlining, it has branches and should be rarely called
public unsafe static bool IsSubnormal(float f)
{
@@ -162,37 +161,37 @@ namespace System
return 1;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator ==(Single left, Single right)
{
return left == right;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator !=(Single left, Single right)
{
return left != right;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator <(Single left, Single right)
{
return left < right;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator >(Single left, Single right)
{
return left > right;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator <=(Single left, Single right)
{
return left <= right;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public static bool operator >=(Single left, Single right)
{
return left >= right;
@@ -269,67 +268,88 @@ namespace System
//
public static float Parse(String s)
{
- return Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseSingle(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo);
}
public static float Parse(String s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
- return Parse(s, style, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseSingle(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo);
}
public static float Parse(String s, IFormatProvider provider)
{
- return Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseSingle(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider));
}
public static float Parse(String s, NumberStyles style, IFormatProvider provider)
{
NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
- return Parse(s, style, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseSingle(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider));
}
- private static float Parse(String s, NumberStyles style, NumberFormatInfo info)
+ public static float Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
{
- return Number.ParseSingle(s, style, info);
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return Number.ParseSingle(s, style, NumberFormatInfo.GetInstance(provider));
}
public static Boolean TryParse(String s, out Single result)
{
- return TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result);
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return TryParse(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result);
}
public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Single result)
{
NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
- return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result);
- }
- private static Boolean TryParse(String s, NumberStyles style, NumberFormatInfo info, out Single result)
- {
if (s == null)
{
result = 0;
return false;
}
+
+ return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ public static Boolean TryParse(ReadOnlySpan<char> s, out Single result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ private static Boolean TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out Single result)
+ {
bool success = Number.TryParseSingle(s, style, info, out result);
if (!success)
{
- String sTrim = s.Trim();
- if (sTrim.Equals(info.PositiveInfinitySymbol))
+ ReadOnlySpan<char> sTrim = s.Trim();
+ if (StringSpanHelpers.Equals(sTrim, info.PositiveInfinitySymbol))
{
result = PositiveInfinity;
}
- else if (sTrim.Equals(info.NegativeInfinitySymbol))
+ else if (StringSpanHelpers.Equals(sTrim, info.NegativeInfinitySymbol))
{
result = NegativeInfinity;
}
- else if (sTrim.Equals(info.NaNSymbol))
+ else if (StringSpanHelpers.Equals(sTrim, info.NaNSymbol))
{
result = NaN;
}
else
+ {
return false; // We really failed
+ }
}
return true;
}
diff --git a/src/mscorlib/shared/System/Span.NonGeneric.cs b/src/mscorlib/shared/System/Span.NonGeneric.cs
index 62b6faa8ec..0e8bfaa994 100644
--- a/src/mscorlib/shared/System/Span.NonGeneric.cs
+++ b/src/mscorlib/shared/System/Span.NonGeneric.cs
@@ -118,7 +118,7 @@ namespace System
/// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null
/// reference (Nothing in Visual Basic).</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<char> AsSpan(this string text)
+ public static ReadOnlySpan<char> AsReadOnlySpan(this string text)
{
if (text == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
diff --git a/src/mscorlib/shared/System/Span.cs b/src/mscorlib/shared/System/Span.cs
index 88612eb28b..de6787d09d 100644
--- a/src/mscorlib/shared/System/Span.cs
+++ b/src/mscorlib/shared/System/Span.cs
@@ -2,11 +2,10 @@
// 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;
using System.Runtime.CompilerServices;
-using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
-using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
+using System.Runtime.Versioning;
#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
@@ -22,7 +21,9 @@ namespace System
/// Span represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed
/// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
/// </summary>
+ [IsReadOnly]
[IsByRefLike]
+ [NonVersionable]
public struct Span<T>
{
/// <summary>A byref or a native ptr.</summary>
@@ -54,32 +55,6 @@ namespace System
/// <summary>
/// Creates a new span over the portion of the target array beginning
- /// at 'start' index and covering the remainder of the array.
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <param name="start">The index at which to begin the span.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> is not in the range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span(T[] array, int start)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- if (default(T) == null && array.GetType() != typeof(T[]))
- ThrowHelper.ThrowArrayTypeMismatchException();
- if ((uint)start > (uint)array.Length)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
- _length = array.Length - start;
- }
-
- /// <summary>
- /// Creates a new span over the portion of the target array beginning
/// at 'start' index and ending at 'end' index (exclusive).
/// </summary>
/// <param name="array">The target array.</param>
@@ -142,6 +117,7 @@ namespace System
/// <param name="objectData">A reference to data within that object.</param>
/// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public static Span<T> DangerousCreate(object obj, ref T objectData, int length) => new Span<T>(ref objectData, length);
// Constructor for internal use only.
@@ -158,6 +134,8 @@ namespace System
/// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
/// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
/// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public ref T DangerousGetPinnableReference()
{
return ref _pointer.Value;
@@ -166,12 +144,26 @@ namespace System
/// <summary>
/// The number of items in the span.
/// </summary>
- public int Length => _length;
+ public int Length
+ {
+ [NonVersionable]
+ get
+ {
+ return _length;
+ }
+ }
/// <summary>
/// Returns true if Length is 0.
/// </summary>
- public bool IsEmpty => _length == 0;
+ public bool IsEmpty
+ {
+ [NonVersionable]
+ get
+ {
+ return _length == 0;
+ }
+ }
/// <summary>
/// Returns a reference to specified element of the Span.
@@ -194,6 +186,7 @@ namespace System
[Intrinsic]
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [NonVersionable]
get
{
if ((uint)index >= (uint)_length)
diff --git a/src/mscorlib/shared/System/StackOverflowException.cs b/src/mscorlib/shared/System/StackOverflowException.cs
index fb0e88246c..8b21eb769a 100644
--- a/src/mscorlib/shared/System/StackOverflowException.cs
+++ b/src/mscorlib/shared/System/StackOverflowException.cs
@@ -20,19 +20,19 @@ namespace System
public StackOverflowException()
: base(SR.Arg_StackOverflowException)
{
- HResult = __HResults.COR_E_STACKOVERFLOW;
+ HResult = HResults.COR_E_STACKOVERFLOW;
}
public StackOverflowException(String message)
: base(message)
{
- HResult = __HResults.COR_E_STACKOVERFLOW;
+ HResult = HResults.COR_E_STACKOVERFLOW;
}
public StackOverflowException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_STACKOVERFLOW;
+ HResult = HResults.COR_E_STACKOVERFLOW;
}
}
}
diff --git a/src/mscorlib/shared/System/StringSpanHelpers.cs b/src/mscorlib/shared/System/StringSpanHelpers.cs
new file mode 100644
index 0000000000..bdfd965641
--- /dev/null
+++ b/src/mscorlib/shared/System/StringSpanHelpers.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.Globalization;
+
+namespace System
+{
+ /// <summary>Helpers for string-like operations on spans of chars.</summary>
+ internal static class StringSpanHelpers
+ {
+ // TODO https://github.com/dotnet/corefx/issues/21395: Provide public, efficient implementations
+
+ public static bool Equals(this ReadOnlySpan<char> left, ReadOnlySpan<char> right, StringComparison comparisonType) =>
+ comparisonType == StringComparison.Ordinal ? Equals(left, right) :
+ comparisonType == StringComparison.OrdinalIgnoreCase ? EqualsOrdinalIgnoreCase(left, right) :
+ throw new ArgumentOutOfRangeException(nameof(comparisonType));
+
+ public static bool Equals(this ReadOnlySpan<char> left, string right) =>
+ Equals(left, right.AsReadOnlySpan());
+
+ public static bool Equals(this ReadOnlySpan<char> left, ReadOnlySpan<char> right)
+ {
+ if (left.Length != right.Length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < left.Length; i++)
+ {
+ if (left[i] != right[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool EqualsOrdinalIgnoreCase(this ReadOnlySpan<char> left, ReadOnlySpan<char> right)
+ {
+ if (left.Length != right.Length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < left.Length; i++)
+ {
+ char x = left[i], y = right[i];
+ if (x != y &&
+ TextInfo.ToUpperAsciiInvariant(x) != TextInfo.ToUpperAsciiInvariant(y))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> source)
+ {
+ int startIndex = 0, endIndex = source.Length - 1;
+
+ while (startIndex <= endIndex && char.IsWhiteSpace(source[startIndex]))
+ {
+ startIndex++;
+ }
+
+ while (endIndex >= startIndex && char.IsWhiteSpace(source[endIndex]))
+ {
+ endIndex--;
+ }
+
+ return source.Slice(startIndex, endIndex - startIndex + 1);
+ }
+
+ public static int IndexOf(this ReadOnlySpan<char> source, char value) =>
+ IndexOf(source, value, 0);
+
+ public static int IndexOf(this ReadOnlySpan<char> source, char value, int startIndex)
+ {
+ for (int i = startIndex; i < source.Length; i++)
+ {
+ if (source[i] == value)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/SystemException.cs b/src/mscorlib/shared/System/SystemException.cs
index d5bcde7efc..64a4b9ca31 100644
--- a/src/mscorlib/shared/System/SystemException.cs
+++ b/src/mscorlib/shared/System/SystemException.cs
@@ -11,19 +11,19 @@ namespace System
public SystemException()
: base(SR.Arg_SystemException)
{
- HResult = __HResults.COR_E_SYSTEM;
+ HResult = HResults.COR_E_SYSTEM;
}
public SystemException(String message)
: base(message)
{
- HResult = __HResults.COR_E_SYSTEM;
+ HResult = HResults.COR_E_SYSTEM;
}
public SystemException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_SYSTEM;
+ HResult = HResults.COR_E_SYSTEM;
}
protected SystemException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/Text/Decoder.cs b/src/mscorlib/shared/System/Text/Decoder.cs
index f109f3db49..82d33d518c 100644
--- a/src/mscorlib/shared/System/Text/Decoder.cs
+++ b/src/mscorlib/shared/System/Text/Decoder.cs
@@ -134,6 +134,14 @@ namespace System.Text
return GetCharCount(arrbyte, 0, count);
}
+ public virtual unsafe int GetCharCount(ReadOnlySpan<byte> bytes, bool flush)
+ {
+ fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference())
+ {
+ return GetCharCount(bytesPtr, bytes.Length, flush);
+ }
+ }
+
// Decodes a range of bytes in a byte array into a range of characters
// in a character array. The method decodes byteCount bytes from
// bytes starting at index byteIndex, storing the resulting
@@ -220,6 +228,15 @@ namespace System.Text
return charCount;
}
+ public virtual unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars, bool flush)
+ {
+ fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference())
+ fixed (char* charsPtr = &chars.DangerousGetPinnableReference())
+ {
+ return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length, flush);
+ }
+ }
+
// This method is used when the output buffer might not be large enough.
// It will decode until it runs out of bytes, and then it will return
// true if it the entire input was converted. In either case it
@@ -326,5 +343,14 @@ namespace System.Text
// Oops, we didn't have anything, we'll have to throw an overflow
throw new ArgumentException(SR.Argument_ConversionOverflow);
}
+
+ public virtual unsafe void Convert(ReadOnlySpan<byte> bytes, Span<char> chars, bool flush, out int bytesUsed, out int charsUsed, out bool completed)
+ {
+ fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference())
+ fixed (char* charsPtr = &chars.DangerousGetPinnableReference())
+ {
+ Convert(bytesPtr, bytes.Length, charsPtr, chars.Length, flush, out bytesUsed, out charsUsed, out completed);
+ }
+ }
}
}
diff --git a/src/mscorlib/shared/System/Text/DecoderExceptionFallback.cs b/src/mscorlib/shared/System/Text/DecoderExceptionFallback.cs
index b465aa68ef..26d604a919 100644
--- a/src/mscorlib/shared/System/Text/DecoderExceptionFallback.cs
+++ b/src/mscorlib/shared/System/Text/DecoderExceptionFallback.cs
@@ -106,19 +106,19 @@ namespace System.Text
public DecoderFallbackException()
: base(SR.Arg_ArgumentException)
{
- HResult = __HResults.COR_E_ARGUMENT;
+ HResult = HResults.COR_E_ARGUMENT;
}
public DecoderFallbackException(String message)
: base(message)
{
- HResult = __HResults.COR_E_ARGUMENT;
+ HResult = HResults.COR_E_ARGUMENT;
}
public DecoderFallbackException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_ARGUMENT;
+ HResult = HResults.COR_E_ARGUMENT;
}
public DecoderFallbackException(String message, byte[] bytesUnknown, int index)
diff --git a/src/mscorlib/shared/System/Text/Encoder.cs b/src/mscorlib/shared/System/Text/Encoder.cs
index b0602f3cce..79a633f1ff 100644
--- a/src/mscorlib/shared/System/Text/Encoder.cs
+++ b/src/mscorlib/shared/System/Text/Encoder.cs
@@ -132,6 +132,14 @@ namespace System.Text
return GetByteCount(arrChar, 0, count, flush);
}
+ public virtual unsafe int GetByteCount(ReadOnlySpan<char> chars, bool flush)
+ {
+ fixed (char* charsPtr = &chars.DangerousGetPinnableReference())
+ {
+ return GetByteCount(charsPtr, chars.Length, flush);
+ }
+ }
+
// Encodes a range of characters in a character array into a range of bytes
// in a byte array. The method encodes charCount characters from
// chars starting at index charIndex, storing the resulting
@@ -214,6 +222,15 @@ namespace System.Text
return byteCount;
}
+ public virtual unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, bool flush)
+ {
+ fixed (char* charsPtr = &chars.DangerousGetPinnableReference())
+ fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference())
+ {
+ return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length, flush);
+ }
+ }
+
// This method is used to avoid running out of output buffer space.
// It will encode until it runs out of chars, and then it will return
// true if it the entire input was converted. In either case it
@@ -320,6 +337,15 @@ namespace System.Text
// Oops, we didn't have anything, we'll have to throw an overflow
throw new ArgumentException(SR.Argument_ConversionOverflow);
}
+
+ public virtual unsafe void Convert(ReadOnlySpan<char> chars, Span<byte> bytes, bool flush, out int charsUsed, out int bytesUsed, out bool completed)
+ {
+ fixed (char* charsPtr = &chars.DangerousGetPinnableReference())
+ fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference())
+ {
+ Convert(charsPtr, chars.Length, bytesPtr, bytes.Length, flush, out charsUsed, out bytesUsed, out completed);
+ }
+ }
}
}
diff --git a/src/mscorlib/shared/System/Text/EncoderExceptionFallback.cs b/src/mscorlib/shared/System/Text/EncoderExceptionFallback.cs
index 98eff25b94..71426e5f31 100644
--- a/src/mscorlib/shared/System/Text/EncoderExceptionFallback.cs
+++ b/src/mscorlib/shared/System/Text/EncoderExceptionFallback.cs
@@ -108,19 +108,19 @@ namespace System.Text
public EncoderFallbackException()
: base(SR.Arg_ArgumentException)
{
- HResult = __HResults.COR_E_ARGUMENT;
+ HResult = HResults.COR_E_ARGUMENT;
}
public EncoderFallbackException(String message)
: base(message)
{
- HResult = __HResults.COR_E_ARGUMENT;
+ HResult = HResults.COR_E_ARGUMENT;
}
public EncoderFallbackException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_ARGUMENT;
+ HResult = HResults.COR_E_ARGUMENT;
}
internal EncoderFallbackException(
diff --git a/src/mscorlib/shared/System/Text/Encoding.cs b/src/mscorlib/shared/System/Text/Encoding.cs
index 4f23d2a4a7..bf8cb03db2 100644
--- a/src/mscorlib/shared/System/Text/Encoding.cs
+++ b/src/mscorlib/shared/System/Text/Encoding.cs
@@ -382,6 +382,8 @@ namespace System.Text
return Array.Empty<byte>();
}
+ public virtual ReadOnlySpan<byte> Preamble => GetPreamble();
+
private void GetDataItem()
{
if (_dataItem == null)
@@ -733,6 +735,14 @@ namespace System.Text
return GetByteCount(arrChar, 0, count);
}
+ public virtual unsafe int GetByteCount(ReadOnlySpan<char> chars)
+ {
+ fixed (char* charsPtr = &chars.DangerousGetPinnableReference())
+ {
+ return GetByteCount(charsPtr, chars.Length);
+ }
+ }
+
// For NLS Encodings, workhorse takes an encoder (may be null)
// Always validate parameters before calling internal version, which will only assert.
internal virtual unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder)
@@ -916,6 +926,15 @@ namespace System.Text
return byteCount;
}
+ public virtual unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes)
+ {
+ fixed (char* charsPtr = &chars.DangerousGetPinnableReference())
+ fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference())
+ {
+ return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length);
+ }
+ }
+
// Returns the number of characters produced by decoding the given byte
// array.
//
@@ -962,6 +981,14 @@ namespace System.Text
return GetCharCount(arrbyte, 0, count);
}
+ public virtual unsafe int GetCharCount(ReadOnlySpan<byte> bytes)
+ {
+ fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference())
+ {
+ return GetCharCount(bytesPtr, bytes.Length);
+ }
+ }
+
// This is our internal workhorse
// Always validate parameters before calling internal version, which will only assert.
internal virtual unsafe int GetCharCount(byte* bytes, int count, DecoderNLS decoder)
@@ -1070,6 +1097,14 @@ namespace System.Text
return charCount;
}
+ public virtual unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars)
+ {
+ fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference())
+ fixed (char* charsPtr = &chars.DangerousGetPinnableReference())
+ {
+ return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length);
+ }
+ }
// This is our internal workhorse
// Always validate parameters before calling internal version, which will only assert.
@@ -1093,6 +1128,15 @@ namespace System.Text
return String.CreateStringFromEncoding(bytes, byteCount, this);
}
+ public unsafe string GetString(ReadOnlySpan<byte> bytes)
+ {
+ fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference())
+ {
+ return GetString(bytesPtr, bytes.Length);
+ }
+ }
+
+
// Returns the code page identifier of this encoding. The returned value is
// an integer between 0 and 65535 if the encoding has a code page
// identifier, or -1 if the encoding does not represent a code page.
diff --git a/src/mscorlib/shared/System/Text/Normalization.cs b/src/mscorlib/shared/System/Text/Normalization.cs
index dc8bc2af71..976756251a 100644
--- a/src/mscorlib/shared/System/Text/Normalization.cs
+++ b/src/mscorlib/shared/System/Text/Normalization.cs
@@ -4,7 +4,6 @@
namespace System.Text
{
- // This is the enumeration for Normalization Forms
public enum NormalizationForm
{
FormC = 1,
@@ -12,18 +11,4 @@ namespace System.Text
FormKC = 5,
FormKD = 6
}
-
- internal enum ExtendedNormalizationForms
- {
- FormC = 1,
- FormD = 2,
- FormKC = 5,
- FormKD = 6,
- FormIdna = 0xd,
- FormCDisallowUnassigned = 0x101,
- FormDDisallowUnassigned = 0x102,
- FormKCDisallowUnassigned = 0x105,
- FormKDDisallowUnassigned = 0x106,
- FormIdnaDisallowUnassigned = 0x10d
- }
}
diff --git a/src/mscorlib/shared/System/Text/StringBuilder.cs b/src/mscorlib/shared/System/Text/StringBuilder.cs
index 34533a92ca..a73e6757d4 100644
--- a/src/mscorlib/shared/System/Text/StringBuilder.cs
+++ b/src/mscorlib/shared/System/Text/StringBuilder.cs
@@ -822,11 +822,6 @@ namespace System.Text
throw new ArgumentNullException(nameof(destination));
}
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.Arg_NegativeArgCount);
- }
-
if (destinationIndex < 0)
{
throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(destinationIndex)));
@@ -836,6 +831,17 @@ namespace System.Text
{
throw new ArgumentException(SR.ArgumentOutOfRange_OffsetOut);
}
+ Contract.EndContractBlock();
+
+ CopyTo(sourceIndex, new Span<char>(destination).Slice(destinationIndex), count);
+ }
+
+ public void CopyTo(int sourceIndex, Span<char> destination, int count)
+ {
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.Arg_NegativeArgCount);
+ }
if ((uint)sourceIndex > (uint)Length)
{
@@ -852,7 +858,7 @@ namespace System.Text
StringBuilder chunk = this;
int sourceEndIndex = sourceIndex + count;
- int curDestIndex = destinationIndex + count;
+ int curDestIndex = count;
while (count > 0)
{
int chunkEndIndex = sourceEndIndex - chunk.m_ChunkOffset;
@@ -1033,6 +1039,21 @@ namespace System.Text
return this;
}
+ public StringBuilder Append(ReadOnlySpan<char> value)
+ {
+ if (value.Length > 0)
+ {
+ unsafe
+ {
+ fixed (char* valueChars = &value.DangerousGetPinnableReference())
+ {
+ Append(valueChars, value.Length);
+ }
+ }
+ }
+ return this;
+ }
+
#region AppendJoin
public unsafe StringBuilder AppendJoin(string separator, params object[] values)
@@ -1263,6 +1284,27 @@ namespace System.Text
public StringBuilder Insert(int index, Object value) => (value == null) ? this : Insert(index, value.ToString(), 1);
+ public StringBuilder Insert(int index, ReadOnlySpan<char> value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ Contract.EndContractBlock();
+
+ if ((uint)index > (uint)Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
+ }
+
+ if (value.Length > 0)
+ {
+ unsafe
+ {
+ fixed (char* sourcePtr = &value.DangerousGetPinnableReference())
+ Insert(index, sourcePtr, value.Length);
+ }
+ }
+ return this;
+ }
+
public StringBuilder AppendFormat(String format, Object arg0) => AppendFormatHelper(null, format, new ParamsArray(arg0));
public StringBuilder AppendFormat(String format, Object arg0, Object arg1) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1));
@@ -1884,7 +1926,7 @@ namespace System.Text
Debug.Assert(gapEnd <= sourceChunk.m_ChunkLength, "gap too big");
if (delta != 0) // can skip the sliding of gaps if source an target string are the same size.
{
- // Copy the gap data between the current replacement and the the next replacement
+ // Copy the gap data between the current replacement and the next replacement
fixed (char* sourcePtr = &sourceChunk.m_ChunkChars[gapStart])
ReplaceInPlaceAtChunk(ref targetChunk, ref targetIndexInChunk, sourcePtr, gapEnd - gapStart);
}
@@ -1944,7 +1986,7 @@ namespace System.Text
/// </summary>
/// <param name="chunk">
/// The chunk in which to start replacing characters.
- /// Receieves the chunk in which character replacement ends.
+ /// Receives the chunk in which character replacement ends.
/// </param>
/// <param name="indexInChunk">
/// The index in <paramref name="chunk"/> to start replacing characters at.
@@ -2001,22 +2043,23 @@ namespace System.Text
}
}
- private static void ThreadSafeCopy(char[] source, int sourceIndex, char[] destination, int destinationIndex, int count)
+ private static unsafe void ThreadSafeCopy(char[] source, int sourceIndex, Span<char> destination, int destinationIndex, int count)
{
if (count > 0)
{
- if ((uint)sourceIndex <= (uint)source.Length && (sourceIndex + count) <= source.Length)
+ if ((uint)sourceIndex > (uint)source.Length || count > source.Length - sourceIndex)
{
- unsafe
- {
- fixed (char* sourcePtr = &source[sourceIndex])
- ThreadSafeCopy(sourcePtr, destination, destinationIndex, count);
- }
+ throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index);
}
- else
+
+ if ((uint)destinationIndex > (uint)destination.Length || count > destination.Length - destinationIndex)
{
- throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index);
+ throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_Index);
}
+
+ fixed (char* sourcePtr = &source[sourceIndex])
+ fixed (char* destinationPtr = &destination.DangerousGetPinnableReference())
+ string.wstrcpy(destinationPtr + destinationIndex, sourcePtr, count);
}
}
diff --git a/src/mscorlib/shared/System/Text/UTF32Encoding.cs b/src/mscorlib/shared/System/Text/UTF32Encoding.cs
index 10161d193e..260518e21c 100644
--- a/src/mscorlib/shared/System/Text/UTF32Encoding.cs
+++ b/src/mscorlib/shared/System/Text/UTF32Encoding.cs
@@ -39,6 +39,9 @@ namespace System.Text
internal static readonly UTF32Encoding s_default = new UTF32Encoding(bigEndian: false, byteOrderMark: true);
internal static readonly UTF32Encoding s_bigEndianDefault = new UTF32Encoding(bigEndian: true, byteOrderMark: true);
+ private static readonly byte[] s_bigEndianPreamble = new byte[4] { 0x00, 0x00, 0xFE, 0xFF };
+ private static readonly byte[] s_littleEndianPreamble = new byte[4] { 0xFF, 0xFE, 0x00, 0x00 };
+
private bool _emitUTF32ByteOrderMark = false;
private bool _isThrowException = false;
private bool _bigEndian = false;
@@ -1177,6 +1180,10 @@ namespace System.Text
return Array.Empty<byte>();
}
+ public override ReadOnlySpan<byte> Preamble =>
+ GetType() != typeof(UTF32Encoding) ? GetPreamble() : // in case a derived UTF32Encoding overrode GetPreamble
+ _emitUTF32ByteOrderMark ? (_bigEndian ? s_bigEndianPreamble : s_littleEndianPreamble) :
+ Array.Empty<byte>();
public override bool Equals(Object value)
{
diff --git a/src/mscorlib/shared/System/Text/UTF8Encoding.cs b/src/mscorlib/shared/System/Text/UTF8Encoding.cs
index 02b18935e4..974bf75650 100644
--- a/src/mscorlib/shared/System/Text/UTF8Encoding.cs
+++ b/src/mscorlib/shared/System/Text/UTF8Encoding.cs
@@ -54,15 +54,19 @@ namespace System.Text
internal sealed class UTF8EncodingSealed : UTF8Encoding
{
public UTF8EncodingSealed(bool encoderShouldEmitUTF8Identifier) : base(encoderShouldEmitUTF8Identifier) { }
+
+ public override ReadOnlySpan<byte> Preamble => _emitUTF8Identifier ? s_preamble : Array.Empty<byte>();
}
// Used by Encoding.UTF8 for lazy initialization
// The initialization code will not be run until a static member of the class is referenced
internal static readonly UTF8EncodingSealed s_default = new UTF8EncodingSealed(encoderShouldEmitUTF8Identifier: true);
+ internal static readonly byte[] s_preamble = new byte[3] { 0xEF, 0xBB, 0xBF };
+
// Yes, the idea of emitting U+FEFF as a UTF-8 identifier has made it into
// the standard.
- private bool _emitUTF8Identifier = false;
+ internal readonly bool _emitUTF8Identifier = false;
private bool _isThrowException = false;
@@ -2497,6 +2501,10 @@ namespace System.Text
return Array.Empty<byte>();
}
+ public override ReadOnlySpan<byte> Preamble =>
+ GetType() != typeof(UTF8Encoding) ? GetPreamble() : // in case a derived UTF8Encoding overrode GetPreamble
+ _emitUTF8Identifier ? s_preamble :
+ Array.Empty<byte>();
public override bool Equals(Object value)
{
diff --git a/src/mscorlib/shared/System/Text/UnicodeEncoding.cs b/src/mscorlib/shared/System/Text/UnicodeEncoding.cs
index 8e44317ce2..78355299c1 100644
--- a/src/mscorlib/shared/System/Text/UnicodeEncoding.cs
+++ b/src/mscorlib/shared/System/Text/UnicodeEncoding.cs
@@ -20,6 +20,9 @@ namespace System.Text
internal static readonly UnicodeEncoding s_bigEndianDefault = new UnicodeEncoding(bigEndian: true, byteOrderMark: true);
internal static readonly UnicodeEncoding s_littleEndianDefault = new UnicodeEncoding(bigEndian: false, byteOrderMark: true);
+ private static readonly byte[] s_bigEndianPreamble = new byte[2] { 0xfe, 0xff };
+ private static readonly byte[] s_littleEndianPreamble = new byte[2] { 0xff, 0xfe };
+
internal bool isThrowException = false;
internal bool bigEndian = false;
@@ -1898,6 +1901,10 @@ namespace System.Text
return Array.Empty<Byte>();
}
+ public override ReadOnlySpan<byte> Preamble =>
+ GetType() != typeof(UnicodeEncoding) ? GetPreamble() : // in case a derived UnicodeEncoding overrode GetPreamble
+ byteOrderMark ? (bigEndian ? s_bigEndianPreamble : s_littleEndianPreamble) :
+ Array.Empty<byte>();
public override int GetMaxByteCount(int charCount)
{
diff --git a/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs b/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs
index 15bc5a7341..c43148d22a 100644
--- a/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs
+++ b/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs
@@ -22,39 +22,39 @@ namespace System.Threading
public AbandonedMutexException()
: base(SR.Threading_AbandonedMutexException)
{
- HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ HResult = HResults.COR_E_ABANDONEDMUTEX;
}
public AbandonedMutexException(String message)
: base(message)
{
- HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ HResult = HResults.COR_E_ABANDONEDMUTEX;
}
public AbandonedMutexException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ HResult = HResults.COR_E_ABANDONEDMUTEX;
}
public AbandonedMutexException(int location, WaitHandle handle)
: base(SR.Threading_AbandonedMutexException)
{
- HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ HResult = HResults.COR_E_ABANDONEDMUTEX;
SetupException(location, handle);
}
public AbandonedMutexException(String message, int location, WaitHandle handle)
: base(message)
{
- HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ HResult = HResults.COR_E_ABANDONEDMUTEX;
SetupException(location, handle);
}
public AbandonedMutexException(String message, Exception inner, int location, WaitHandle handle)
: base(message, inner)
{
- HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ HResult = HResults.COR_E_ABANDONEDMUTEX;
SetupException(location, handle);
}
diff --git a/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs b/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs
index 89380fee60..e2b1eb983b 100644
--- a/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs
+++ b/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs
@@ -18,7 +18,7 @@ namespace System.Threading
/// Indicates whether the object has been disposed.
/// </param>
/// <remarks>
- /// If the refount reaches zero before the object is disposed, this method will be called with
+ /// If the refcount reaches zero before the object is disposed, this method will be called with
/// <paramref name="disposed"/> set to false. If the object is then disposed, this method will be
/// called again, with <paramref name="disposed"/> set to true. If the refcount reaches zero
/// after the object has already been disposed, this will be called a single time, with
diff --git a/src/mscorlib/shared/System/Threading/LazyInitializer.cs b/src/mscorlib/shared/System/Threading/LazyInitializer.cs
index b03d1db302..f422ab9172 100644
--- a/src/mscorlib/shared/System/Threading/LazyInitializer.cs
+++ b/src/mscorlib/shared/System/Threading/LazyInitializer.cs
@@ -25,7 +25,7 @@ namespace System.Threading
/// Initializes a target reference type with the type's default constructor if the target has not
/// already been initialized.
/// </summary>
- /// <typeparam name="T">The refence type of the reference to be initialized.</typeparam>
+ /// <typeparam name="T">The reference type of the reference to be initialized.</typeparam>
/// <param name="target">A reference of type <typeparamref name="T"/> to initialize if it has not
/// already been initialized.</param>
/// <returns>The initialized reference of type <typeparamref name="T"/>.</returns>
diff --git a/src/mscorlib/shared/System/Threading/ReaderWriterLockSlim.cs b/src/mscorlib/shared/System/Threading/ReaderWriterLockSlim.cs
index 98517ad85f..1b2147e6e1 100644
--- a/src/mscorlib/shared/System/Threading/ReaderWriterLockSlim.cs
+++ b/src/mscorlib/shared/System/Threading/ReaderWriterLockSlim.cs
@@ -4,7 +4,6 @@
using Internal.Runtime.Augments;
using System.Diagnostics; // for TraceInformation
-using System.Threading;
using System.Runtime.CompilerServices;
namespace System.Threading
@@ -54,8 +53,10 @@ namespace System.Threading
/// </summary>
public class ReaderWriterLockSlim : IDisposable
{
+ private static readonly int ProcessorCount = Environment.ProcessorCount;
+
//Specifying if locked can be reacquired recursively.
- private bool _fIsReentrant;
+ private readonly bool _fIsReentrant;
// Lock specification for myLock: This lock protects exactly the local fields associated with this
// instance of ReaderWriterLockSlim. It does NOT protect the memory associated with
@@ -74,8 +75,7 @@ namespace System.Threading
private uint _numWriteUpgradeWaiters; // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1).
private uint _numUpgradeWaiters;
- //Variable used for quick check when there are no waiters.
- private bool _fNoWaiters;
+ private WaiterStates _waiterStates;
private int _upgradeLockOwnerId;
private int _writeLockOwnerId;
@@ -146,10 +146,37 @@ namespace System.Threading
_fIsReentrant = true;
}
InitializeThreadCounts();
- _fNoWaiters = true;
+ _waiterStates = WaiterStates.NoWaiters;
_lockID = Interlocked.Increment(ref s_nextLockID);
}
+ private bool HasNoWaiters
+ {
+ get
+ {
+#if DEBUG
+ Debug.Assert(MyLockHeld);
+#endif
+
+ return (_waiterStates & WaiterStates.NoWaiters) != WaiterStates.None;
+ }
+ set
+ {
+#if DEBUG
+ Debug.Assert(MyLockHeld);
+#endif
+
+ if (value)
+ {
+ _waiterStates |= WaiterStates.NoWaiters;
+ }
+ else
+ {
+ _waiterStates &= ~WaiterStates.NoWaiters;
+ }
+ }
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsRWEntryEmpty(ReaderWriterCount rwc)
{
@@ -352,8 +379,7 @@ namespace System.Threading
}
bool retVal = true;
-
- int spincount = 0;
+ int spinCount = 0;
for (; ;)
{
@@ -368,13 +394,17 @@ namespace System.Threading
break;
}
- if (spincount < MaxSpinCount)
+ if (timeout.IsExpired)
{
ExitMyLock();
- if (timeout.IsExpired)
- return false;
- spincount++;
- SpinWait(spincount);
+ return false;
+ }
+
+ if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyRead())
+ {
+ ExitMyLock();
+ spinCount++;
+ SpinWait(spinCount);
EnterMyLock();
//The per-thread structure may have been recycled as the lock is acquired (due to message pumping), load again.
if (IsRwHashEntryChanged(lrwc))
@@ -391,7 +421,13 @@ namespace System.Threading
continue; // since we left the lock, start over.
}
- retVal = WaitOnEvent(_readEvent, ref _numReadWaiters, timeout, isWriteWaiter: false);
+ retVal =
+ WaitOnEvent(
+ _readEvent,
+ ref _numReadWaiters,
+ timeout,
+ isWriteWaiter: false,
+ waiterSignaledState: WaiterStates.None);
if (!retVal)
{
return false;
@@ -480,8 +516,8 @@ namespace System.Threading
}
}
- int spincount = 0;
bool retVal = true;
+ int spinCount = 0;
for (; ;)
{
@@ -528,13 +564,17 @@ namespace System.Threading
}
}
- if (spincount < MaxSpinCount)
+ if (timeout.IsExpired)
{
ExitMyLock();
- if (timeout.IsExpired)
- return false;
- spincount++;
- SpinWait(spincount);
+ return false;
+ }
+
+ if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyWrite(upgradingToWrite))
+ {
+ ExitMyLock();
+ spinCount++;
+ SpinWait(spinCount);
EnterMyLock();
continue;
}
@@ -549,7 +589,13 @@ namespace System.Threading
Debug.Assert(_numWriteUpgradeWaiters == 0, "There can be at most one thread with the upgrade lock held.");
- retVal = WaitOnEvent(_waitUpgradeEvent, ref _numWriteUpgradeWaiters, timeout, isWriteWaiter: true);
+ retVal =
+ WaitOnEvent(
+ _waitUpgradeEvent,
+ ref _numWriteUpgradeWaiters,
+ timeout,
+ isWriteWaiter: true,
+ waiterSignaledState: WaiterStates.None);
//The lock is not held in case of failure.
if (!retVal)
@@ -564,7 +610,13 @@ namespace System.Threading
continue; // since we left the lock, start over.
}
- retVal = WaitOnEvent(_writeEvent, ref _numWriteWaiters, timeout, isWriteWaiter: true);
+ retVal =
+ WaitOnEvent(
+ _writeEvent,
+ ref _numWriteWaiters,
+ timeout,
+ isWriteWaiter: true,
+ waiterSignaledState: WaiterStates.WriteWaiterSignaled);
//The lock is not held in case of failure.
if (!retVal)
return false;
@@ -671,8 +723,7 @@ namespace System.Threading
}
bool retVal = true;
-
- int spincount = 0;
+ int spinCount = 0;
for (; ;)
{
@@ -686,13 +737,17 @@ namespace System.Threading
break;
}
- if (spincount < MaxSpinCount)
+ if (timeout.IsExpired)
{
ExitMyLock();
- if (timeout.IsExpired)
- return false;
- spincount++;
- SpinWait(spincount);
+ return false;
+ }
+
+ if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyRead())
+ {
+ ExitMyLock();
+ spinCount++;
+ SpinWait(spinCount);
EnterMyLock();
continue;
}
@@ -705,7 +760,13 @@ namespace System.Threading
}
//Only one thread with the upgrade lock held can proceed.
- retVal = WaitOnEvent(_upgradeEvent, ref _numUpgradeWaiters, timeout, isWriteWaiter: false);
+ retVal =
+ WaitOnEvent(
+ _upgradeEvent,
+ ref _numUpgradeWaiters,
+ timeout,
+ isWriteWaiter: false,
+ waiterSignaledState: WaiterStates.UpgradeableReadWaiterSignaled);
if (!retVal)
return false;
}
@@ -890,14 +951,20 @@ namespace System.Threading
EventWaitHandle waitEvent,
ref uint numWaiters,
TimeoutTracker timeout,
- bool isWriteWaiter)
+ bool isWriteWaiter,
+ WaiterStates waiterSignaledState)
{
#if DEBUG
Debug.Assert(MyLockHeld);
#endif
+ Debug.Assert(
+ waiterSignaledState == WaiterStates.None ||
+ waiterSignaledState == WaiterStates.WriteWaiterSignaled ||
+ waiterSignaledState == WaiterStates.UpgradeableReadWaiterSignaled);
+
waitEvent.Reset();
numWaiters++;
- _fNoWaiters = false;
+ HasNoWaiters = false;
//Setting these bits will prevent new readers from getting in.
if (_numWriteWaiters == 1)
@@ -917,8 +984,19 @@ namespace System.Threading
EnterMyLock();
--numWaiters;
+ if (waitSuccessful && waiterSignaledState != WaiterStates.None)
+ {
+ // Indicate that a signaled waiter of this type has woken. Since non-read waiters are signaled to wake one
+ // at a time, we avoid waking up more than one waiter of that type upon successive enter/exit loops until
+ // the signaled thread actually wakes up. For example, if there are multiple write waiters and one thread is
+ // repeatedly entering and exiting a write lock, every exit would otherwise signal a different write waiter
+ // to wake up unnecessarily when only one woken waiter may actually succeed in entering the write lock.
+ Debug.Assert((_waiterStates & waiterSignaledState) != WaiterStates.None);
+ _waiterStates &= ~waiterSignaledState;
+ }
+
if (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0 && _numUpgradeWaiters == 0 && _numReadWaiters == 0)
- _fNoWaiters = true;
+ HasNoWaiters = true;
if (_numWriteWaiters == 0)
ClearWritersWaiting();
@@ -948,7 +1026,7 @@ namespace System.Threading
#if DEBUG
Debug.Assert(MyLockHeld);
#endif
- if (_fNoWaiters)
+ if (HasNoWaiters)
{
ExitMyLock();
return;
@@ -984,8 +1062,20 @@ namespace System.Threading
}
else if (readercount == 0 && _numWriteWaiters > 0)
{
+ // Check if a waiter of the same type has already been signaled but hasn't woken yet. If so, avoid signaling
+ // and waking another waiter unnecessarily.
+ WaiterStates signaled = _waiterStates & WaiterStates.WriteWaiterSignaled;
+ if (signaled == WaiterStates.None)
+ {
+ _waiterStates |= WaiterStates.WriteWaiterSignaled;
+ }
+
ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock)
- _writeEvent.Set(); // release one writer.
+
+ if (signaled == WaiterStates.None)
+ {
+ _writeEvent.Set(); // release one writer.
+ }
}
else
{
@@ -999,7 +1089,7 @@ namespace System.Threading
Debug.Assert(MyLockHeld);
#endif
- if (_numWriteWaiters != 0 || _numWriteUpgradeWaiters != 0 || _fNoWaiters)
+ if (_numWriteWaiters != 0 || _numWriteUpgradeWaiters != 0 || HasNoWaiters)
{
ExitMyLock();
return;
@@ -1009,6 +1099,19 @@ namespace System.Threading
bool setReadEvent = _numReadWaiters != 0;
bool setUpgradeEvent = _numUpgradeWaiters != 0 && _upgradeLockOwnerId == -1;
+ if (setUpgradeEvent)
+ {
+ // Check if a waiter of the same type has already been signaled but hasn't woken yet. If so, avoid signaling
+ // and waking another waiter unnecessarily.
+ if ((_waiterStates & WaiterStates.UpgradeableReadWaiterSignaled) == WaiterStates.None)
+ {
+ _waiterStates |= WaiterStates.UpgradeableReadWaiterSignaled;
+ }
+ else
+ {
+ setUpgradeEvent = false;
+ }
+ }
ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock)
@@ -1059,33 +1162,62 @@ namespace System.Threading
return _owners & READER_MASK;
}
+ private bool ShouldSpinForEnterAnyRead()
+ {
+ // If there is a write waiter or write upgrade waiter, the waiter would block a reader from acquiring the RW lock
+ // because the waiter takes precedence. In that case, the reader is not likely to make progress by spinning.
+ // Although another thread holding a write lock would prevent this thread from acquiring a read lock, it is by
+ // itself not a good enough reason to skip spinning.
+ return HasNoWaiters || (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0);
+ }
+
+ private bool ShouldSpinForEnterAnyWrite(bool isUpgradeToWrite)
+ {
+ // If there is a write upgrade waiter, the waiter would block a writer from acquiring the RW lock because the waiter
+ // holds a read lock. In that case, the writer is not likely to make progress by spinning. Regarding upgrading to a
+ // write lock, there is no type of waiter that would block the upgrade from happening. Although another thread
+ // holding a read or write lock would prevent this thread from acquiring the write lock, it is by itself not a good
+ // enough reason to skip spinning.
+ return isUpgradeToWrite || _numWriteUpgradeWaiters == 0;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool TryEnterMyLock()
+ {
+ return Interlocked.CompareExchange(ref _myLock, 1, 0) == 0;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnterMyLock()
{
- if (Interlocked.CompareExchange(ref _myLock, 1, 0) != 0)
+ if (!TryEnterMyLock())
+ {
EnterMyLockSpin();
+ }
}
private void EnterMyLockSpin()
{
- int pc = Environment.ProcessorCount;
- for (int i = 0; ; i++)
+ int processorCount = ProcessorCount;
+ for (int spinIndex = 0; ; spinIndex++)
{
- if (i < LockSpinCount && pc > 1)
+ if (spinIndex < LockSpinCount && processorCount > 1)
{
- RuntimeThread.SpinWait(LockSpinCycles * (i + 1)); // Wait a few dozen instructions to let another processor release lock.
+ RuntimeThread.SpinWait(LockSpinCycles * (spinIndex + 1)); // Wait a few dozen instructions to let another processor release lock.
}
- else if (i < (LockSpinCount + LockSleep0Count))
+ else if (spinIndex < (LockSpinCount + LockSleep0Count))
{
- RuntimeThread.Sleep(0); // Give up my quantum.
+ RuntimeThread.Sleep(0); // Give up my quantum.
}
else
{
- RuntimeThread.Sleep(1); // Give up my quantum.
+ RuntimeThread.Sleep(1); // Give up my quantum.
}
- if (_myLock == 0 && Interlocked.CompareExchange(ref _myLock, 1, 0) == 0)
+ if (_myLock == 0 && TryEnterMyLock())
+ {
return;
+ }
}
}
@@ -1099,12 +1231,12 @@ namespace System.Threading
private bool MyLockHeld { get { return _myLock != 0; } }
#endif
- private static void SpinWait(int SpinCount)
+ private static void SpinWait(int spinCount)
{
//Exponential back-off
- if ((SpinCount < 5) && (Environment.ProcessorCount > 1))
+ if ((spinCount < 5) && (ProcessorCount > 1))
{
- RuntimeThread.SpinWait(LockSpinCycles * SpinCount);
+ RuntimeThread.SpinWait(LockSpinCycles * spinCount);
}
else
{
@@ -1307,5 +1439,19 @@ namespace System.Threading
return (int)_numWriteWaiters;
}
}
+
+ [Flags]
+ private enum WaiterStates : byte
+ {
+ None = 0x0,
+
+ // Used for quick check when there are no waiters
+ NoWaiters = 0x1,
+
+ // Used to avoid signaling more than one waiter to wake up when only one can make progress, see WaitOnEvent
+ WriteWaiterSignaled = 0x2,
+ UpgradeableReadWaiterSignaled = 0x4
+ // Write upgrade waiters are excluded because there can only be one at any given time
+ }
}
}
diff --git a/src/mscorlib/shared/System/Threading/SpinWait.cs b/src/mscorlib/shared/System/Threading/SpinWait.cs
index d25d54f26f..414ad1852f 100644
--- a/src/mscorlib/shared/System/Threading/SpinWait.cs
+++ b/src/mscorlib/shared/System/Threading/SpinWait.cs
@@ -69,9 +69,26 @@ namespace System.Threading
// numbers may seem fairly arbitrary, but were derived with at least some
// thought in the design document. I fully expect they will need to change
// over time as we gain more experience with performance.
- internal const int YIELD_THRESHOLD = 10; // When to switch over to a true yield.
- internal const int SLEEP_0_EVERY_HOW_MANY_TIMES = 5; // After how many yields should we Sleep(0)?
- internal const int SLEEP_1_EVERY_HOW_MANY_TIMES = 20; // After how many yields should we Sleep(1)?
+ internal const int YieldThreshold = 10; // When to switch over to a true yield.
+ private const int Sleep0EveryHowManyYields = 5; // After how many yields should we Sleep(0)?
+ internal const int DefaultSleep1Threshold = 20; // After how many yields should we Sleep(1) frequently?
+
+ /// <summary>
+ /// A suggested number of spin iterations before doing a proper wait, such as waiting on an event that becomes signaled
+ /// when the resource becomes available.
+ /// </summary>
+ /// <remarks>
+ /// These numbers were arrived at by experimenting with different numbers in various cases that currently use it. It's
+ /// only a suggested value and typically works well when the proper wait is something like an event.
+ ///
+ /// Spinning less can lead to early waiting and more context switching, spinning more can decrease latency but may use
+ /// up some CPU time unnecessarily. Depends on the situation too, for instance SemaphoreSlim uses more iterations
+ /// because the waiting there is currently a lot more expensive (involves more spinning, taking a lock, etc.). It also
+ /// depends on the likelihood of the spin being successful and how long the wait would be but those are not accounted
+ /// for here.
+ /// </remarks>
+ internal static readonly int SpinCountforSpinBeforeWait = PlatformHelper.IsSingleProcessor ? 1 : 35;
+ internal const int Sleep1ThresholdForSpinBeforeWait = 40; // should be greater than SpinCountforSpinBeforeWait
// The number of times we've spun already.
private int _count;
@@ -81,7 +98,12 @@ namespace System.Threading
/// </summary>
public int Count
{
- get { return _count; }
+ get => _count;
+ internal set
+ {
+ Debug.Assert(value >= 0);
+ _count = value;
+ }
}
/// <summary>
@@ -94,10 +116,7 @@ namespace System.Threading
/// On a single-CPU machine, <see cref="SpinOnce"/> always yields the processor. On machines with
/// multiple CPUs, <see cref="SpinOnce"/> may yield after an unspecified number of calls.
/// </remarks>
- public bool NextSpinWillYield
- {
- get { return _count > YIELD_THRESHOLD || PlatformHelper.IsSingleProcessor; }
- }
+ public bool NextSpinWillYield => _count >= YieldThreshold || PlatformHelper.IsSingleProcessor;
/// <summary>
/// Performs a single spin.
@@ -108,7 +127,27 @@ namespace System.Threading
/// </remarks>
public void SpinOnce()
{
- if (NextSpinWillYield)
+ SpinOnce(DefaultSleep1Threshold);
+ }
+
+ internal void SpinOnce(int sleep1Threshold)
+ {
+ Debug.Assert(sleep1Threshold >= YieldThreshold || PlatformHelper.IsSingleProcessor); // so that NextSpinWillYield behaves as requested
+
+ // (_count - YieldThreshold) % 2 == 0: The purpose of this check is to interleave Thread.Yield/Sleep(0) with
+ // Thread.SpinWait. Otherwise, the following issues occur:
+ // - When there are no threads to switch to, Yield and Sleep(0) become no-op and it turns the spin loop into a
+ // busy-spin that may quickly reach the max spin count and cause the thread to enter a wait state, or may
+ // just busy-spin for longer than desired before a Sleep(1). Completing the spin loop too early can cause
+ // excessive context switcing if a wait follows, and entering the Sleep(1) stage too early can cause
+ // excessive delays.
+ // - If there are multiple threads doing Yield and Sleep(0) (typically from the same spin loop due to
+ // contention), they may switch between one another, delaying work that can make progress.
+ if ((
+ _count >= YieldThreshold &&
+ (_count >= sleep1Threshold || (_count - YieldThreshold) % 2 == 0)
+ ) ||
+ PlatformHelper.IsSingleProcessor)
{
//
// We must yield.
@@ -125,19 +164,21 @@ namespace System.Threading
// configured to use the (default) coarse-grained system timer.
//
- int yieldsSoFar = (_count >= YIELD_THRESHOLD ? _count - YIELD_THRESHOLD : _count);
-
- if ((yieldsSoFar % SLEEP_1_EVERY_HOW_MANY_TIMES) == (SLEEP_1_EVERY_HOW_MANY_TIMES - 1))
+ if (_count >= sleep1Threshold)
{
RuntimeThread.Sleep(1);
}
- else if ((yieldsSoFar % SLEEP_0_EVERY_HOW_MANY_TIMES) == (SLEEP_0_EVERY_HOW_MANY_TIMES - 1))
- {
- RuntimeThread.Sleep(0);
- }
else
{
- RuntimeThread.Yield();
+ int yieldsSoFar = _count >= YieldThreshold ? (_count - YieldThreshold) / 2 : _count;
+ if ((yieldsSoFar % Sleep0EveryHowManyYields) == (Sleep0EveryHowManyYields - 1))
+ {
+ RuntimeThread.Sleep(0);
+ }
+ else
+ {
+ RuntimeThread.Yield();
+ }
}
}
else
@@ -153,11 +194,24 @@ namespace System.Threading
// number of spins we are willing to tolerate to reduce delay to the caller,
// since we expect most callers will eventually block anyway.
//
- RuntimeThread.SpinWait(4 << _count);
+ // 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
+ // allow other useful work to run. Long YieldProcessor() loops can help to reduce contention, but Sleep(1) is
+ // usually better for that.
+ //
+ // RuntimeThread.OptimalMaxSpinWaitsPerSpinIteration:
+ // - See Thread::InitializeYieldProcessorNormalized(), which describes and calculates this value.
+ //
+ int n = RuntimeThread.OptimalMaxSpinWaitsPerSpinIteration;
+ if (_count <= 30 && (1 << _count) < n)
+ {
+ n = 1 << _count;
+ }
+ RuntimeThread.SpinWait(n);
}
// Finally, increment our spin counter.
- _count = (_count == int.MaxValue ? YIELD_THRESHOLD : _count + 1);
+ _count = (_count == int.MaxValue ? YieldThreshold : _count + 1);
}
/// <summary>
@@ -299,9 +353,7 @@ namespace System.Threading
/// <summary>
/// Gets whether the current machine has only a single processor.
/// </summary>
- internal static bool IsSingleProcessor
- {
- get { return ProcessorCount == 1; }
- }
+ /// <remarks>This typically does not change on a machine, so it's checked only once.</remarks>
+ internal static readonly bool IsSingleProcessor = ProcessorCount == 1;
}
}
diff --git a/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs b/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs
index c64fc9ced8..5ab186ec9a 100644
--- a/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs
+++ b/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs
@@ -21,19 +21,19 @@ namespace System.Threading
public SynchronizationLockException()
: base(SR.Arg_SynchronizationLockException)
{
- HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK;
+ HResult = HResults.COR_E_SYNCHRONIZATIONLOCK;
}
public SynchronizationLockException(String message)
: base(message)
{
- HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK;
+ HResult = HResults.COR_E_SYNCHRONIZATIONLOCK;
}
public SynchronizationLockException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK;
+ HResult = HResults.COR_E_SYNCHRONIZATIONLOCK;
}
protected SynchronizationLockException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs b/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs
new file mode 100644
index 0000000000..384e4a8ab3
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs
@@ -0,0 +1,169 @@
+// 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.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Provides a value type that wraps a <see cref="Task{TResult}"/> and a <typeparamref name="TResult"/>,
+ /// only one of which is used.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result.</typeparam>
+ /// <remarks>
+ /// <para>
+ /// Methods may return an instance of this value type when it's likely that the result of their
+ /// operations will be available synchronously and when the method is expected to be invoked so
+ /// frequently that the cost of allocating a new <see cref="Task{TResult}"/> for each call will
+ /// be prohibitive.
+ /// </para>
+ /// <para>
+ /// There are tradeoffs to using a <see cref="ValueTask{TResult}"/> instead of a <see cref="Task{TResult}"/>.
+ /// For example, while a <see cref="ValueTask{TResult}"/> can help avoid an allocation in the case where the
+ /// successful result is available synchronously, it also contains two fields whereas a <see cref="Task{TResult}"/>
+ /// as a reference type is a single field. This means that a method call ends up returning two fields worth of
+ /// data instead of one, which is more data to copy. It also means that if a method that returns one of these
+ /// is awaited within an async method, the state machine for that async method will be larger due to needing
+ /// to store the struct that's two fields instead of a single reference.
+ /// </para>
+ /// <para>
+ /// Further, for uses other than consuming the result of an asynchronous operation via await,
+ /// <see cref="ValueTask{TResult}"/> can lead to a more convoluted programming model, which can in turn actually
+ /// lead to more allocations. For example, consider a method that could return either a <see cref="Task{TResult}"/>
+ /// with a cached task as a common result or a <see cref="ValueTask{TResult}"/>. If the consumer of the result
+ /// wants to use it as a <see cref="Task{TResult}"/>, such as to use with in methods like Task.WhenAll and Task.WhenAny,
+ /// the <see cref="ValueTask{TResult}"/> would first need to be converted into a <see cref="Task{TResult}"/> using
+ /// <see cref="ValueTask{TResult}.AsTask"/>, which leads to an allocation that would have been avoided if a cached
+ /// <see cref="Task{TResult}"/> had been used in the first place.
+ /// </para>
+ /// <para>
+ /// As such, the default choice for any asynchronous method should be to return a <see cref="Task"/> or
+ /// <see cref="Task{TResult}"/>. Only if performance analysis proves it worthwhile should a <see cref="ValueTask{TResult}"/>
+ /// be used instead of <see cref="Task{TResult}"/>. There is no non-generic version of <see cref="ValueTask{TResult}"/>
+ /// as the Task.CompletedTask property may be used to hand back a successfully completed singleton in the case where
+ /// a <see cref="Task"/>-returning method completes synchronously and successfully.
+ /// </para>
+ /// </remarks>
+ [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))]
+ [StructLayout(LayoutKind.Auto)]
+ public struct ValueTask<TResult> : IEquatable<ValueTask<TResult>>
+ {
+ /// <summary>The task to be used if the operation completed asynchronously or if it completed synchronously but non-successfully.</summary>
+ internal readonly Task<TResult> _task;
+ /// <summary>The result to be used if the operation completed successfully synchronously.</summary>
+ internal readonly TResult _result;
+
+ /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with the result of the successful operation.</summary>
+ /// <param name="result">The result.</param>
+ public ValueTask(TResult result)
+ {
+ _task = null;
+ _result = result;
+ }
+
+ /// <summary>
+ /// Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="Task{TResult}"/> that represents the operation.
+ /// </summary>
+ /// <param name="task">The task.</param>
+ public ValueTask(Task<TResult> task)
+ {
+ if (task == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task);
+ }
+
+ _task = task;
+ _result = default(TResult);
+ }
+
+ /// <summary>Returns the hash code for this instance.</summary>
+ public override int GetHashCode() =>
+ _task != null ? _task.GetHashCode() :
+ _result != null ? _result.GetHashCode() :
+ 0;
+
+ /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="object"/>.</summary>
+ public override bool Equals(object obj) =>
+ obj is ValueTask<TResult> &&
+ Equals((ValueTask<TResult>)obj);
+
+ /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="ValueTask{TResult}"/> value.</summary>
+ public bool Equals(ValueTask<TResult> other) =>
+ _task != null || other._task != null ?
+ _task == other._task :
+ EqualityComparer<TResult>.Default.Equals(_result, other._result);
+
+ /// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are equal.</summary>
+ public static bool operator==(ValueTask<TResult> left, ValueTask<TResult> right) =>
+ left.Equals(right);
+
+ /// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are not equal.</summary>
+ public static bool operator!=(ValueTask<TResult> left, ValueTask<TResult> right) =>
+ !left.Equals(right);
+
+ /// <summary>
+ /// Gets a <see cref="Task{TResult}"/> object to represent this ValueTask. It will
+ /// either return the wrapped task object if one exists, or it'll manufacture a new
+ /// task object to represent the result.
+ /// </summary>
+ public Task<TResult> AsTask() =>
+ // Return the task if we were constructed from one, otherwise manufacture one. We don't
+ // cache the generated task into _task as it would end up changing both equality comparison
+ // and the hash code we generate in GetHashCode.
+ _task ?? AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result);
+
+ /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a completed operation.</summary>
+ public bool IsCompleted => _task == null || _task.IsCompleted;
+
+ /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a successfully completed operation.</summary>
+ public bool IsCompletedSuccessfully => _task == null || _task.IsCompletedSuccessfully;
+
+ /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a failed operation.</summary>
+ public bool IsFaulted => _task != null && _task.IsFaulted;
+
+ /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a canceled operation.</summary>
+ public bool IsCanceled => _task != null && _task.IsCanceled;
+
+ /// <summary>Gets the result.</summary>
+ public TResult Result => _task == null ? _result : _task.GetAwaiter().GetResult();
+
+ /// <summary>Gets an awaiter for this value.</summary>
+ public ValueTaskAwaiter<TResult> GetAwaiter() => new ValueTaskAwaiter<TResult>(this);
+
+ /// <summary>Configures an awaiter for this value.</summary>
+ /// <param name="continueOnCapturedContext">
+ /// true to attempt to marshal the continuation back to the captured context; otherwise, false.
+ /// </param>
+ public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext) =>
+ new ConfiguredValueTaskAwaitable<TResult>(this, continueOnCapturedContext);
+
+ /// <summary>Gets a string-representation of this <see cref="ValueTask{TResult}"/>.</summary>
+ public override string ToString()
+ {
+ if (_task != null)
+ {
+ return _task.IsCompletedSuccessfully && _task.Result != null ?
+ _task.Result.ToString() :
+ string.Empty;
+ }
+ else
+ {
+ return _result != null ?
+ _result.ToString() :
+ string.Empty;
+ }
+ }
+
+ // TODO https://github.com/dotnet/corefx/issues/22171:
+ // Remove CreateAsyncMethodBuilder once the C# compiler relies on the AsyncBuilder attribute.
+
+ /// <summary>Creates a method builder for use with an async method.</summary>
+ /// <returns>The created builder.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)] // intended only for compiler consumption
+ public static AsyncValueTaskMethodBuilder<TResult> CreateAsyncMethodBuilder() => AsyncValueTaskMethodBuilder<TResult>.Create();
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ThreadAbortException.cs b/src/mscorlib/shared/System/Threading/ThreadAbortException.cs
index ebbc29a702..01fdf1317b 100644
--- a/src/mscorlib/shared/System/Threading/ThreadAbortException.cs
+++ b/src/mscorlib/shared/System/Threading/ThreadAbortException.cs
@@ -22,7 +22,7 @@ namespace System.Threading
{
internal ThreadAbortException()
{
- HResult = __HResults.COR_E_THREADABORTED;
+ HResult = HResults.COR_E_THREADABORTED;
}
public object ExceptionState => null;
diff --git a/src/mscorlib/shared/System/Threading/ThreadStartException.cs b/src/mscorlib/shared/System/Threading/ThreadStartException.cs
index 7a87943ed1..35930d1d2d 100644
--- a/src/mscorlib/shared/System/Threading/ThreadStartException.cs
+++ b/src/mscorlib/shared/System/Threading/ThreadStartException.cs
@@ -11,13 +11,13 @@ namespace System.Threading
internal ThreadStartException()
: base(SR.Arg_ThreadStartException)
{
- HResult = __HResults.COR_E_THREADSTART;
+ HResult = HResults.COR_E_THREADSTART;
}
internal ThreadStartException(Exception reason)
: base(SR.Arg_ThreadStartException, reason)
{
- HResult = __HResults.COR_E_THREADSTART;
+ HResult = HResults.COR_E_THREADSTART;
}
}
}
diff --git a/src/mscorlib/shared/System/Threading/ThreadStateException.cs b/src/mscorlib/shared/System/Threading/ThreadStateException.cs
index 9477cb1ae4..d8f97a4f3b 100644
--- a/src/mscorlib/shared/System/Threading/ThreadStateException.cs
+++ b/src/mscorlib/shared/System/Threading/ThreadStateException.cs
@@ -21,19 +21,19 @@ namespace System.Threading
public ThreadStateException()
: base(SR.Arg_ThreadStateException)
{
- HResult = __HResults.COR_E_THREADSTATE;
+ HResult = HResults.COR_E_THREADSTATE;
}
public ThreadStateException(String message)
: base(message)
{
- HResult = __HResults.COR_E_THREADSTATE;
+ HResult = HResults.COR_E_THREADSTATE;
}
public ThreadStateException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_THREADSTATE;
+ HResult = HResults.COR_E_THREADSTATE;
}
protected ThreadStateException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/Threading/TimeoutHelper.cs b/src/mscorlib/shared/System/Threading/TimeoutHelper.cs
index c66c9add92..c96a4d00d6 100644
--- a/src/mscorlib/shared/System/Threading/TimeoutHelper.cs
+++ b/src/mscorlib/shared/System/Threading/TimeoutHelper.cs
@@ -13,7 +13,7 @@ namespace System.Threading
internal static class TimeoutHelper
{
/// <summary>
- /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from postive to negative every ~ 25 days
+ /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from positive to negative every ~ 25 days
/// then ~25 days to back to positive again, uint is sued to ignore the sign and double the range to 50 days
/// </summary>
/// <returns></returns>
@@ -26,7 +26,7 @@ namespace System.Threading
/// Helper function to measure and update the elapsed time
/// </summary>
/// <param name="startTime"> The first time (in milliseconds) observed when the wait started</param>
- /// <param name="originalWaitMillisecondsTimeout">The orginal wait timeoutout in milliseconds</param>
+ /// <param name="originalWaitMillisecondsTimeout">The original wait timeout in milliseconds</param>
/// <returns>The new wait time in milliseconds, -1 if the time expired</returns>
public static int UpdateTimeOut(uint startTime, int originalWaitMillisecondsTimeout)
{
diff --git a/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs b/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs
index 770e70d7ab..47e127191d 100644
--- a/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs
+++ b/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs
@@ -10,17 +10,17 @@ namespace System.Threading
{
public WaitHandleCannotBeOpenedException() : base(SR.Threading_WaitHandleCannotBeOpenedException)
{
- HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED;
+ HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED;
}
public WaitHandleCannotBeOpenedException(String message) : base(message)
{
- HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED;
+ HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED;
}
public WaitHandleCannotBeOpenedException(String message, Exception innerException) : base(message, innerException)
{
- HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED;
+ HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED;
}
protected WaitHandleCannotBeOpenedException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/TimeSpan.cs b/src/mscorlib/shared/System/TimeSpan.cs
index 39be059362..263a0a5824 100644
--- a/src/mscorlib/shared/System/TimeSpan.cs
+++ b/src/mscorlib/shared/System/TimeSpan.cs
@@ -316,55 +316,119 @@ namespace System
}
public static TimeSpan Parse(String s)
{
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
/* Constructs a TimeSpan from a string. Leading and trailing white space characters are allowed. */
- return TimeSpanParse.Parse(s, null);
+ return TimeSpanParse.Parse(s.AsReadOnlySpan(), null);
}
public static TimeSpan Parse(String input, IFormatProvider formatProvider)
{
+ if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
+ return TimeSpanParse.Parse(input.AsReadOnlySpan(), formatProvider);
+ }
+ public static TimeSpan Parse(ReadOnlySpan<char> input, IFormatProvider formatProvider = null)
+ {
return TimeSpanParse.Parse(input, formatProvider);
}
public static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider)
{
- return TimeSpanParse.ParseExact(input, format, formatProvider, TimeSpanStyles.None);
+ if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
+ return TimeSpanParse.ParseExact(input.AsReadOnlySpan(), format, formatProvider, TimeSpanStyles.None);
}
public static TimeSpan ParseExact(String input, String[] formats, IFormatProvider formatProvider)
{
- return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None);
+ if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
+ return TimeSpanParse.ParseExactMultiple(input.AsReadOnlySpan(), formats, formatProvider, TimeSpanStyles.None);
}
public static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles)
{
ValidateStyles(styles, nameof(styles));
+ if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
+ return TimeSpanParse.ParseExact(input.AsReadOnlySpan(), format, formatProvider, styles);
+ }
+ public static TimeSpan ParseExact(ReadOnlySpan<char> input, string format, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
+ {
+ ValidateStyles(styles, nameof(styles));
return TimeSpanParse.ParseExact(input, format, formatProvider, styles);
}
public static TimeSpan ParseExact(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles)
{
ValidateStyles(styles, nameof(styles));
+ if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
+ return TimeSpanParse.ParseExactMultiple(input.AsReadOnlySpan(), formats, formatProvider, styles);
+ }
+ public static TimeSpan ParseExact(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
+ {
+ ValidateStyles(styles, nameof(styles));
return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles);
}
public static Boolean TryParse(String s, out TimeSpan result)
{
- return TimeSpanParse.TryParse(s, null, out result);
+ if (s == null)
+ {
+ result = default(TimeSpan);
+ return false;
+ }
+ return TimeSpanParse.TryParse(s.AsReadOnlySpan(), null, out result);
}
public static Boolean TryParse(String input, IFormatProvider formatProvider, out TimeSpan result)
{
+ if (input == null)
+ {
+ result = default(TimeSpan);
+ return false;
+ }
+ return TimeSpanParse.TryParse(input.AsReadOnlySpan(), formatProvider, out result);
+ }
+ public static bool TryParse(ReadOnlySpan<char> input, out TimeSpan result, IFormatProvider formatProvider = null)
+ {
return TimeSpanParse.TryParse(input, formatProvider, out result);
}
public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, out TimeSpan result)
{
- return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result);
+ if (input == null)
+ {
+ result = default(TimeSpan);
+ return false;
+ }
+ return TimeSpanParse.TryParseExact(input.AsReadOnlySpan(), format, formatProvider, TimeSpanStyles.None, out result);
}
public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, out TimeSpan result)
{
- return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result);
+ if (input == null)
+ {
+ result = default(TimeSpan);
+ return false;
+ }
+ return TimeSpanParse.TryParseExactMultiple(input.AsReadOnlySpan(), formats, formatProvider, TimeSpanStyles.None, out result);
}
public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
{
ValidateStyles(styles, nameof(styles));
+ if (input == null)
+ {
+ result = default(TimeSpan);
+ return false;
+ }
+ return TimeSpanParse.TryParseExact(input.AsReadOnlySpan(), format, formatProvider, styles, out result);
+ }
+ public static bool TryParseExact(ReadOnlySpan<char> input, string format, IFormatProvider formatProvider, out TimeSpan result, TimeSpanStyles styles = TimeSpanStyles.None)
+ {
+ ValidateStyles(styles, nameof(styles));
return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result);
}
public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
{
ValidateStyles(styles, nameof(styles));
+ if (input == null)
+ {
+ result = default(TimeSpan);
+ return false;
+ }
+ return TimeSpanParse.TryParseExactMultiple(input.AsReadOnlySpan(), formats, formatProvider, styles, out result);
+ }
+ public static bool TryParseExact(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, out TimeSpan result, TimeSpanStyles styles = TimeSpanStyles.None)
+ {
+ ValidateStyles(styles, nameof(styles));
return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result);
}
public override String ToString()
@@ -379,6 +443,10 @@ namespace System
{
return TimeSpanFormat.Format(this, format, formatProvider);
}
+ public bool TryFormat(Span<char> destination, out int charsWritten, string format = null, IFormatProvider formatProvider = null)
+ {
+ return TimeSpanFormat.TryFormat(this, destination, out charsWritten, format, formatProvider);
+ }
#endregion
public static TimeSpan operator -(TimeSpan t)
diff --git a/src/mscorlib/shared/System/TimeoutException.cs b/src/mscorlib/shared/System/TimeoutException.cs
index 4ba95bc47b..b50ae2f902 100644
--- a/src/mscorlib/shared/System/TimeoutException.cs
+++ b/src/mscorlib/shared/System/TimeoutException.cs
@@ -20,19 +20,19 @@ namespace System
public TimeoutException()
: base(SR.Arg_TimeoutException)
{
- HResult = __HResults.COR_E_TIMEOUT;
+ HResult = HResults.COR_E_TIMEOUT;
}
public TimeoutException(String message)
: base(message)
{
- HResult = __HResults.COR_E_TIMEOUT;
+ HResult = HResults.COR_E_TIMEOUT;
}
public TimeoutException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_TIMEOUT;
+ HResult = HResults.COR_E_TIMEOUT;
}
protected TimeoutException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/Type.cs b/src/mscorlib/shared/System/Type.cs
index 2ba58918a0..ab3d55bc66 100644
--- a/src/mscorlib/shared/System/Type.cs
+++ b/src/mscorlib/shared/System/Type.cs
@@ -47,6 +47,8 @@ namespace System
public virtual bool IsSZArray { get { throw NotImplemented.ByDesign; } }
public virtual bool IsVariableBoundArray => IsArray && !IsSZArray;
+ public virtual bool IsByRefLike => throw new NotSupportedException(SR.NotSupported_SubclassOverride);
+
public bool HasElementType => HasElementTypeImpl();
protected abstract bool HasElementTypeImpl();
public abstract Type GetElementType();
@@ -106,6 +108,8 @@ namespace System
public bool IsValueType => IsValueTypeImpl();
protected virtual bool IsValueTypeImpl() => IsSubclassOf(typeof(ValueType));
+ public virtual bool IsSignatureType => false;
+
public virtual bool IsSecurityCritical { get { throw NotImplemented.ByDesign; } }
public virtual bool IsSecuritySafeCritical { get { throw NotImplemented.ByDesign; } }
public virtual bool IsSecurityTransparent { get { throw NotImplemented.ByDesign; } }
@@ -177,6 +181,27 @@ namespace System
protected abstract MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers);
+ public MethodInfo GetMethod(string name, int genericParameterCount, Type[] types) => GetMethod(name, genericParameterCount, types, null);
+ public MethodInfo GetMethod(string name, int genericParameterCount, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, genericParameterCount, Type.DefaultLookup, null, types, modifiers);
+ public MethodInfo GetMethod(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, genericParameterCount, bindingAttr, binder, CallingConventions.Any, types, modifiers);
+ public MethodInfo GetMethod(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+ if (genericParameterCount < 0)
+ throw new ArgumentException(SR.ArgumentOutOfRange_NeedNonNegNum, nameof(genericParameterCount));
+ if (types == null)
+ throw new ArgumentNullException(nameof(types));
+ for (int i = 0; i < types.Length; i++)
+ {
+ if (types[i] == null)
+ throw new ArgumentNullException(nameof(types));
+ }
+ return GetMethodImpl(name, genericParameterCount, bindingAttr, binder, callConvention, types, modifiers);
+ }
+
+ protected virtual MethodInfo GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException();
+
public MethodInfo[] GetMethods() => GetMethods(Type.DefaultLookup);
public abstract MethodInfo[] GetMethods(BindingFlags bindingAttr);
@@ -317,6 +342,13 @@ namespace System
public virtual Type MakeGenericType(params Type[] typeArguments) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
public virtual Type MakePointerType() { throw new NotSupportedException(); }
+ public static Type MakeGenericMethodParameter(int position)
+ {
+ if (position < 0)
+ throw new ArgumentException(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(position));
+ return new SignatureGenericMethodParameterType(position);
+ }
+
public override string ToString() => "Type: " + Name; // Why do we add the "Type: " prefix?
public override bool Equals(object o) => o == null ? false : Equals(o as Type);
diff --git a/src/mscorlib/shared/System/TypeAccessException.cs b/src/mscorlib/shared/System/TypeAccessException.cs
index 302dcb1ac1..ff081582b6 100644
--- a/src/mscorlib/shared/System/TypeAccessException.cs
+++ b/src/mscorlib/shared/System/TypeAccessException.cs
@@ -13,19 +13,19 @@ namespace System
public TypeAccessException()
: base(SR.Arg_TypeAccessException)
{
- HResult = __HResults.COR_E_TYPEACCESS;
+ HResult = HResults.COR_E_TYPEACCESS;
}
public TypeAccessException(string message)
: base(message)
{
- HResult = __HResults.COR_E_TYPEACCESS;
+ HResult = HResults.COR_E_TYPEACCESS;
}
public TypeAccessException(string message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_TYPEACCESS;
+ HResult = HResults.COR_E_TYPEACCESS;
}
protected TypeAccessException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/TypeInitializationException.cs b/src/mscorlib/shared/System/TypeInitializationException.cs
index 8d0b8a9f79..03a1bad3a7 100644
--- a/src/mscorlib/shared/System/TypeInitializationException.cs
+++ b/src/mscorlib/shared/System/TypeInitializationException.cs
@@ -28,7 +28,7 @@ namespace System
private TypeInitializationException()
: base(SR.TypeInitialization_Default)
{
- HResult = __HResults.COR_E_TYPEINITIALIZATION;
+ HResult = HResults.COR_E_TYPEINITIALIZATION;
}
@@ -41,14 +41,14 @@ namespace System
// for Interop only, though it's not particularly useful.
internal TypeInitializationException(String message) : base(message)
{
- HResult = __HResults.COR_E_TYPEINITIALIZATION;
+ HResult = HResults.COR_E_TYPEINITIALIZATION;
}
internal TypeInitializationException(String fullTypeName, String message, Exception innerException)
: base(message, innerException)
{
_typeName = fullTypeName;
- HResult = __HResults.COR_E_TYPEINITIALIZATION;
+ HResult = HResults.COR_E_TYPEINITIALIZATION;
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/shared/System/TypeUnloadedException.cs b/src/mscorlib/shared/System/TypeUnloadedException.cs
index c7ed71c9cb..97039bb8f1 100644
--- a/src/mscorlib/shared/System/TypeUnloadedException.cs
+++ b/src/mscorlib/shared/System/TypeUnloadedException.cs
@@ -11,19 +11,19 @@ namespace System
public TypeUnloadedException()
: base(SR.Arg_TypeUnloadedException)
{
- HResult = __HResults.COR_E_TYPEUNLOADED;
+ HResult = HResults.COR_E_TYPEUNLOADED;
}
public TypeUnloadedException(string message)
: base(message)
{
- HResult = __HResults.COR_E_TYPEUNLOADED;
+ HResult = HResults.COR_E_TYPEUNLOADED;
}
public TypeUnloadedException(string message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_TYPEUNLOADED;
+ HResult = HResults.COR_E_TYPEUNLOADED;
}
protected TypeUnloadedException(SerializationInfo info, StreamingContext context)
diff --git a/src/mscorlib/src/System/UInt16.cs b/src/mscorlib/shared/System/UInt16.cs
index 746ed7ba49..4cd290bc45 100644
--- a/src/mscorlib/src/System/UInt16.cs
+++ b/src/mscorlib/shared/System/UInt16.cs
@@ -11,19 +11,19 @@
**
===========================================================*/
+using System.Diagnostics.Contracts;
using System.Globalization;
-using System;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Diagnostics.Contracts;
+using System.Runtime.Versioning;
namespace System
{
- // Wrapper for unsigned 16 bit integers.
[Serializable]
- [CLSCompliant(false), System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct UInt16 : IComparable, IFormattable, IConvertible
- , IComparable<UInt16>, IEquatable<UInt16>
+ [CLSCompliant(false)]
+ [StructLayout(LayoutKind.Sequential)]
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public struct UInt16 : IComparable, IConvertible, IFormattable, IComparable<UInt16>, IEquatable<UInt16>
{
private ushort m_value; // Do not rename (binary serialization)
@@ -64,7 +64,7 @@ namespace System
return m_value == ((UInt16)obj).m_value;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public bool Equals(UInt16 obj)
{
return m_value == obj;
@@ -105,31 +105,42 @@ namespace System
[CLSCompliant(false)]
public static ushort Parse(String s)
{
- return Parse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
[CLSCompliant(false)]
public static ushort Parse(String s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
- return Parse(s, style, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo);
}
[CLSCompliant(false)]
public static ushort Parse(String s, IFormatProvider provider)
{
- return Parse(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
[CLSCompliant(false)]
public static ushort Parse(String s, NumberStyles style, IFormatProvider provider)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ [CLSCompliant(false)]
+ public static ushort Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Parse(s, style, NumberFormatInfo.GetInstance(provider));
}
- private static ushort Parse(String s, NumberStyles style, NumberFormatInfo info)
+ private static ushort Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
{
uint i = 0;
try
@@ -148,17 +159,37 @@ namespace System
[CLSCompliant(false)]
public static bool TryParse(String s, out UInt16 result)
{
- return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return TryParse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
[CLSCompliant(false)]
public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt16 result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ [CLSCompliant(false)]
+ public static bool TryParse(ReadOnlySpan<char> s, out ushort result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result);
}
- private static bool TryParse(String s, NumberStyles style, NumberFormatInfo info, out UInt16 result)
+ private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out UInt16 result)
{
result = 0;
UInt32 i;
diff --git a/src/mscorlib/src/System/UInt32.cs b/src/mscorlib/shared/System/UInt32.cs
index f696816b98..49835613b2 100644
--- a/src/mscorlib/src/System/UInt32.cs
+++ b/src/mscorlib/shared/System/UInt32.cs
@@ -12,20 +12,19 @@
**
===========================================================*/
+using System.Diagnostics.Contracts;
using System.Globalization;
-using System;
-using System.Runtime;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Diagnostics.Contracts;
+using System.Runtime.Versioning;
namespace System
{
- // * Wrapper for unsigned 32 bit integers.
[Serializable]
- [CLSCompliant(false), System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct UInt32 : IComparable, IFormattable, IConvertible
- , IComparable<UInt32>, IEquatable<UInt32>
+ [CLSCompliant(false)]
+ [StructLayout(LayoutKind.Sequential)]
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public struct UInt32 : IComparable, IConvertible, IFormattable, IComparable<UInt32>, IEquatable<UInt32>
{
private uint m_value; // Do not rename (binary serialization)
@@ -75,7 +74,7 @@ namespace System
return m_value == ((UInt32)obj).m_value;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public bool Equals(UInt32 obj)
{
return m_value == obj;
@@ -115,40 +114,71 @@ namespace System
[CLSCompliant(false)]
public static uint Parse(String s)
{
- return Number.ParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseUInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
[CLSCompliant(false)]
public static uint Parse(String s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
- return Number.ParseUInt32(s, style, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseUInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo);
}
[CLSCompliant(false)]
public static uint Parse(String s, IFormatProvider provider)
{
- return Number.ParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseUInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
[CLSCompliant(false)]
public static uint Parse(String s, NumberStyles style, IFormatProvider provider)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseUInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ [CLSCompliant(false)]
+ public static uint Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.ParseUInt32(s, style, NumberFormatInfo.GetInstance(provider));
}
[CLSCompliant(false)]
public static bool TryParse(String s, out UInt32 result)
{
- return Number.TryParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return Number.TryParseUInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
[CLSCompliant(false)]
public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt32 result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return Number.TryParseUInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ [CLSCompliant(false)]
+ public static bool TryParse(ReadOnlySpan<char> s, out UInt32 result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result);
}
diff --git a/src/mscorlib/src/System/UInt64.cs b/src/mscorlib/shared/System/UInt64.cs
index 03cdcc5f40..424d48b8b1 100644
--- a/src/mscorlib/src/System/UInt64.cs
+++ b/src/mscorlib/shared/System/UInt64.cs
@@ -11,19 +11,19 @@
**
===========================================================*/
+using System.Diagnostics.Contracts;
using System.Globalization;
-using System;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using System.Diagnostics.Contracts;
+using System.Runtime.Versioning;
namespace System
{
- // Wrapper for unsigned 64 bit integers.
[Serializable]
- [CLSCompliant(false), System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct UInt64 : IComparable, IFormattable, IConvertible
- , IComparable<UInt64>, IEquatable<UInt64>
+ [CLSCompliant(false)]
+ [StructLayout(LayoutKind.Sequential)]
+ [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public struct UInt64 : IComparable, IConvertible, IFormattable, IComparable<UInt64>, IEquatable<UInt64>
{
private ulong m_value; // Do not rename (binary serialization)
@@ -72,7 +72,7 @@ namespace System
return m_value == ((UInt64)obj).m_value;
}
- [System.Runtime.Versioning.NonVersionable]
+ [NonVersionable]
public bool Equals(UInt64 obj)
{
return m_value == obj;
@@ -111,39 +111,70 @@ namespace System
[CLSCompliant(false)]
public static ulong Parse(String s)
{
- return Number.ParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseUInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
[CLSCompliant(false)]
public static ulong Parse(String s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
- return Number.ParseUInt64(s, style, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseUInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo);
}
[CLSCompliant(false)]
public static ulong Parse(string s, IFormatProvider provider)
{
- return Number.ParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseUInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
[CLSCompliant(false)]
public static ulong Parse(String s, NumberStyles style, IFormatProvider provider)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseUInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ [CLSCompliant(false)]
+ public static ulong Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.ParseUInt64(s, style, NumberFormatInfo.GetInstance(provider));
}
[CLSCompliant(false)]
public static Boolean TryParse(String s, out UInt64 result)
{
- return Number.TryParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return Number.TryParseUInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
[CLSCompliant(false)]
public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt64 result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
+
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return Number.TryParseUInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ [CLSCompliant(false)]
+ public static Boolean TryParse(ReadOnlySpan<char> s, out UInt64 result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result);
}
diff --git a/src/mscorlib/shared/System/UnauthorizedAccessException.cs b/src/mscorlib/shared/System/UnauthorizedAccessException.cs
index 667d576292..679a1a762d 100644
--- a/src/mscorlib/shared/System/UnauthorizedAccessException.cs
+++ b/src/mscorlib/shared/System/UnauthorizedAccessException.cs
@@ -24,19 +24,19 @@ namespace System
public UnauthorizedAccessException()
: base(SR.Arg_UnauthorizedAccessException)
{
- HResult = __HResults.COR_E_UNAUTHORIZEDACCESS;
+ HResult = HResults.COR_E_UNAUTHORIZEDACCESS;
}
public UnauthorizedAccessException(String message)
: base(message)
{
- HResult = __HResults.COR_E_UNAUTHORIZEDACCESS;
+ HResult = HResults.COR_E_UNAUTHORIZEDACCESS;
}
public UnauthorizedAccessException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_UNAUTHORIZEDACCESS;
+ HResult = HResults.COR_E_UNAUTHORIZEDACCESS;
}
protected UnauthorizedAccessException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/shared/System/UnitySerializationHolder.cs b/src/mscorlib/shared/System/UnitySerializationHolder.cs
new file mode 100644
index 0000000000..bbfebff8a6
--- /dev/null
+++ b/src/mscorlib/shared/System/UnitySerializationHolder.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.Runtime.Serialization;
+
+namespace System
+{
+ /// <summary>
+ /// Holds Null class for which we guarantee that there is only ever one instance of.
+ /// This only exists for compatibility with .NET Framework.
+ /// </summary>
+ [Serializable]
+#if CORECLR
+ internal
+#else
+ public // On CoreRT this must be public.
+#endif
+ sealed class UnitySerializationHolder : ISerializable, IObjectReference
+ {
+ internal const int NullUnity = 0x0002;
+ private readonly int _unityType;
+ private readonly string _data;
+
+ /// <summary>
+ /// A helper method that returns the SerializationInfo that a class utilizing
+ /// UnitySerializationHelper should return from a call to GetObjectData. It contains
+ /// the unityType (defined above) and any optional data (used only for the reflection types).
+ /// </summary>
+ internal static void GetUnitySerializationInfo(SerializationInfo info, int unityType)
+ {
+ info.SetType(typeof(UnitySerializationHolder));
+ info.AddValue("Data", null, typeof(string));
+ info.AddValue("UnityType", unityType);
+ info.AddValue("AssemblyName", string.Empty);
+ }
+
+ public UnitySerializationHolder(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ // We are ignoring any other serialization input as we are only concerned about DBNull.
+ // We also store data and use it for erorr logging.
+ _unityType = info.GetInt32("UnityType");
+ _data = info.GetString("Data");
+ }
+
+ public void GetObjectData(SerializationInfo info, StreamingContext context) =>
+ throw new NotSupportedException(SR.NotSupported_UnitySerHolder);
+
+ public object GetRealObject(StreamingContext context)
+ {
+ // We are only support deserializing DBNull and throwing for everything else.
+ if (_unityType != NullUnity)
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidUnity, _data ?? "UnityType"));
+ }
+
+ // We are always returning the same DBNull instance.
+ return DBNull.Value;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Version.cs b/src/mscorlib/shared/System/Version.cs
index a2140ab137..9c66d9b904 100644
--- a/src/mscorlib/shared/System/Version.cs
+++ b/src/mscorlib/shared/System/Version.cs
@@ -197,82 +197,118 @@ namespace System
return accumulator;
}
- public override String ToString()
+ public override string ToString() =>
+ ToString(DefaultFormatFieldCount);
+
+ public string ToString(int fieldCount) =>
+ fieldCount == 0 ? string.Empty :
+ fieldCount == 1 ? _Major.ToString() :
+ StringBuilderCache.GetStringAndRelease(ToCachedStringBuilder(fieldCount));
+
+ public bool TryFormat(Span<char> destination, out int charsWritten) =>
+ TryFormat(destination, DefaultFormatFieldCount, out charsWritten);
+
+ public bool TryFormat(Span<char> destination, int fieldCount, out int charsWritten)
{
- if (_Build == -1) return (ToString(2));
- if (_Revision == -1) return (ToString(3));
- return (ToString(4));
+ if (fieldCount == 0)
+ {
+ charsWritten = 0;
+ return true;
+ }
+
+ // TODO https://github.com/dotnet/corefx/issues/22403: fieldCount==1 can just use int.TryFormat
+
+ StringBuilder sb = ToCachedStringBuilder(fieldCount);
+ if (sb.Length <= destination.Length)
+ {
+ sb.CopyTo(0, destination, sb.Length);
+ StringBuilderCache.Release(sb);
+ charsWritten = sb.Length;
+ return true;
+ }
+
+ StringBuilderCache.Release(sb);
+ charsWritten = 0;
+ return false;
}
- public String ToString(int fieldCount)
+ private int DefaultFormatFieldCount =>
+ _Build == -1 ? 2 :
+ _Revision == -1 ? 3 :
+ 4;
+
+ private StringBuilder ToCachedStringBuilder(int fieldCount)
{
- StringBuilder sb;
- switch (fieldCount)
+ if (fieldCount == 1)
{
- case 0:
- return (String.Empty);
- case 1:
- return (_Major.ToString());
- case 2:
- sb = StringBuilderCache.Acquire();
- AppendPositiveNumber(_Major, sb);
- sb.Append('.');
- AppendPositiveNumber(_Minor, sb);
- return StringBuilderCache.GetStringAndRelease(sb);
- default:
- if (_Build == -1)
- throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "2"), nameof(fieldCount));
+ StringBuilder sb = StringBuilderCache.Acquire();
+ AppendNonNegativeNumber(_Major, sb);
+ return sb;
+ }
+ else if (fieldCount == 2)
+ {
+ StringBuilder sb = StringBuilderCache.Acquire();
+ AppendNonNegativeNumber(_Major, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Minor, sb);
+ return sb;
+ }
+ else
+ {
+ if (_Build == -1)
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "2"), nameof(fieldCount));
+ }
- if (fieldCount == 3)
- {
- sb = StringBuilderCache.Acquire();
- AppendPositiveNumber(_Major, sb);
- sb.Append('.');
- AppendPositiveNumber(_Minor, sb);
- sb.Append('.');
- AppendPositiveNumber(_Build, sb);
- return StringBuilderCache.GetStringAndRelease(sb);
- }
+ if (fieldCount == 3)
+ {
+ StringBuilder sb = StringBuilderCache.Acquire();
+ AppendNonNegativeNumber(_Major, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Minor, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Build, sb);
+ return sb;
+ }
- if (_Revision == -1)
- throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "3"), nameof(fieldCount));
+ if (_Revision == -1)
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "3"), nameof(fieldCount));
+ }
- if (fieldCount == 4)
- {
- sb = StringBuilderCache.Acquire();
- AppendPositiveNumber(_Major, sb);
- sb.Append('.');
- AppendPositiveNumber(_Minor, sb);
- sb.Append('.');
- AppendPositiveNumber(_Build, sb);
- sb.Append('.');
- AppendPositiveNumber(_Revision, sb);
- return StringBuilderCache.GetStringAndRelease(sb);
- }
+ if (fieldCount == 4)
+ {
+ StringBuilder sb = StringBuilderCache.Acquire();
+ AppendNonNegativeNumber(_Major, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Minor, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Build, sb);
+ sb.Append('.');
+ AppendNonNegativeNumber(_Revision, sb);
+ return sb;
+ }
- throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "4"), nameof(fieldCount));
+ throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "4"), nameof(fieldCount));
}
}
+ // TODO https://github.com/dotnet/corefx/issues/22616:
+ // Use StringBuilder.Append(int) once it's been updated to use spans internally.
//
- // AppendPositiveNumber is an optimization to append a number to a StringBuilder object without
+ // AppendNonNegativeNumber is an optimization to append a number to a StringBuilder object without
// doing any boxing and not even creating intermediate string.
// Note: as we always have positive numbers then it is safe to convert the number to string
// regardless of the current culture as we'll not have any punctuation marks in the number
- //
- private const int ZERO_CHAR_VALUE = (int)'0';
- private static void AppendPositiveNumber(int num, StringBuilder sb)
+ private static void AppendNonNegativeNumber(int num, StringBuilder sb)
{
Debug.Assert(num >= 0, "AppendPositiveNumber expect positive numbers");
int index = sb.Length;
- int reminder;
-
do
{
- reminder = num % 10;
- num = num / 10;
- sb.Insert(index, (char)(ZERO_CHAR_VALUE + reminder));
+ num = Math.DivRem(num, 10, out int remainder);
+ sb.Insert(index, (char)('0' + remainder));
} while (num > 0);
}
@@ -282,104 +318,108 @@ namespace System
{
throw new ArgumentNullException(nameof(input));
}
- Contract.EndContractBlock();
- VersionResult r = new VersionResult();
- r.Init(nameof(input), true);
- if (!TryParseVersion(input, ref r))
- {
- throw r.GetVersionParseException();
- }
- return r.m_parsedVersion;
+ return ParseVersion(input.AsReadOnlySpan(), throwOnFailure: true);
}
- public static bool TryParse(string input, out Version result)
- {
- VersionResult r = new VersionResult();
- r.Init(nameof(input), false);
- bool b = TryParseVersion(input, ref r);
- result = r.m_parsedVersion;
- return b;
- }
+ public static Version Parse(ReadOnlySpan<char> input) =>
+ ParseVersion(input, throwOnFailure: true);
- private static bool TryParseVersion(string version, ref VersionResult result)
+ public static bool TryParse(string input, out Version result)
{
- int major, minor, build, revision;
-
- if ((Object)version == null)
+ if (input == null)
{
- result.SetFailure(ParseFailureKind.ArgumentNullException);
+ result = null;
return false;
}
- String[] parsedComponents = version.Split('.');
- int parsedComponentsLength = parsedComponents.Length;
- if ((parsedComponentsLength < 2) || (parsedComponentsLength > 4))
+ return (result = ParseVersion(input.AsReadOnlySpan(), throwOnFailure: false)) != null;
+ }
+
+ public static bool TryParse(ReadOnlySpan<char> input, out Version result) =>
+ (result = ParseVersion(input, throwOnFailure: false)) != null;
+
+ private static Version ParseVersion(ReadOnlySpan<char> input, bool throwOnFailure)
+ {
+ // Find the separator between major and minor. It must exist.
+ int majorEnd = input.IndexOf('.');
+ if (majorEnd < 0)
{
- result.SetFailure(ParseFailureKind.ArgumentException);
- return false;
+ if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input));
+ return null;
}
- if (!TryParseComponent(parsedComponents[0], nameof(version), ref result, out major))
+ // Find the ends of the optional minor and build portions.
+ // We musn't have any separators after build.
+ int buildEnd = -1;
+ int minorEnd = input.IndexOf('.', majorEnd + 1);
+ if (minorEnd != -1)
{
- return false;
+ buildEnd = input.IndexOf('.', minorEnd + 1);
+ if (buildEnd != -1)
+ {
+ if (input.IndexOf('.', buildEnd + 1) != -1)
+ {
+ if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input));
+ return null;
+ }
+ }
}
- if (!TryParseComponent(parsedComponents[1], nameof(version), ref result, out minor))
+ int major, minor, build, revision;
+
+ // Parse the major version
+ if (!TryParseComponent(input.Slice(0, majorEnd), nameof(input), throwOnFailure, out major))
{
- return false;
+ return null;
}
- parsedComponentsLength -= 2;
-
- if (parsedComponentsLength > 0)
+ if (minorEnd != -1)
{
- if (!TryParseComponent(parsedComponents[2], "build", ref result, out build))
+ // If there's more than a major and minor, parse the minor, too.
+ if (!TryParseComponent(input.Slice(majorEnd + 1, minorEnd - majorEnd - 1), nameof(input), throwOnFailure, out minor))
{
- return false;
+ return null;
}
- parsedComponentsLength--;
-
- if (parsedComponentsLength > 0)
+ if (buildEnd != -1)
{
- if (!TryParseComponent(parsedComponents[3], "revision", ref result, out revision))
- {
- return false;
- }
- else
- {
- result.m_parsedVersion = new Version(major, minor, build, revision);
- }
+ // major.minor.build.revision
+ return
+ TryParseComponent(input.Slice(minorEnd + 1, buildEnd - minorEnd - 1), nameof(build), throwOnFailure, out build) &&
+ TryParseComponent(input.Slice(buildEnd + 1), nameof(revision), throwOnFailure, out revision) ?
+ new Version(major, minor, build, revision) :
+ null;
}
else
{
- result.m_parsedVersion = new Version(major, minor, build);
+ // major.minor.build
+ return TryParseComponent(input.Slice(minorEnd + 1), nameof(build), throwOnFailure, out build) ?
+ new Version(major, minor, build) :
+ null;
}
}
else
{
- result.m_parsedVersion = new Version(major, minor);
+ // major.minor
+ return TryParseComponent(input.Slice(majorEnd + 1), nameof(input), throwOnFailure, out minor) ?
+ new Version(major, minor) :
+ null;
}
-
- return true;
}
- private static bool TryParseComponent(string component, string componentName, ref VersionResult result, out int parsedComponent)
+ private static bool TryParseComponent(ReadOnlySpan<char> component, string componentName, bool throwOnFailure, out int parsedComponent)
{
- if (!Int32.TryParse(component, NumberStyles.Integer, CultureInfo.InvariantCulture, out parsedComponent))
+ if (throwOnFailure)
{
- result.SetFailure(ParseFailureKind.FormatException, component);
- return false;
- }
-
- if (parsedComponent < 0)
- {
- result.SetFailure(ParseFailureKind.ArgumentOutOfRangeException, componentName);
- return false;
+ if ((parsedComponent = int.Parse(component, NumberStyles.Integer, CultureInfo.InvariantCulture)) < 0)
+ {
+ throw new ArgumentOutOfRangeException(componentName, SR.ArgumentOutOfRange_Version);
+ }
+ return true;
}
- return true;
+ return int.TryParse(component, out parsedComponent, NumberStyles.Integer, CultureInfo.InvariantCulture) && parsedComponent >= 0;
}
public static bool operator ==(Version v1, Version v2)
@@ -422,75 +462,5 @@ namespace System
{
return (v2 <= v1);
}
-
- internal enum ParseFailureKind
- {
- ArgumentNullException,
- ArgumentException,
- ArgumentOutOfRangeException,
- FormatException
- }
-
- internal struct VersionResult
- {
- internal Version m_parsedVersion;
- internal ParseFailureKind m_failure;
- internal string m_exceptionArgument;
- internal string m_argumentName;
- internal bool m_canThrow;
-
- internal void Init(string argumentName, bool canThrow)
- {
- m_canThrow = canThrow;
- m_argumentName = argumentName;
- }
-
- internal void SetFailure(ParseFailureKind failure)
- {
- SetFailure(failure, String.Empty);
- }
-
- internal void SetFailure(ParseFailureKind failure, string argument)
- {
- m_failure = failure;
- m_exceptionArgument = argument;
- if (m_canThrow)
- {
- throw GetVersionParseException();
- }
- }
-
- internal Exception GetVersionParseException()
- {
- switch (m_failure)
- {
- case ParseFailureKind.ArgumentNullException:
- return new ArgumentNullException(m_argumentName);
- case ParseFailureKind.ArgumentException:
- return new ArgumentException(SR.Arg_VersionString);
- case ParseFailureKind.ArgumentOutOfRangeException:
- return new ArgumentOutOfRangeException(m_exceptionArgument, SR.ArgumentOutOfRange_Version);
- case ParseFailureKind.FormatException:
- // Regenerate the FormatException as would be thrown by Int32.Parse()
- try
- {
- Int32.Parse(m_exceptionArgument, CultureInfo.InvariantCulture);
- }
- catch (FormatException e)
- {
- return e;
- }
- catch (OverflowException e)
- {
- return e;
- }
- Debug.Assert(false, "Int32.Parse() did not throw exception but TryParse failed: " + m_exceptionArgument);
- return new FormatException(SR.Format_InvalidString);
- default:
- Debug.Assert(false, "Unmatched case in Version.GetVersionParseException() for value: " + m_failure);
- return new ArgumentException(SR.Arg_VersionString);
- }
- }
- }
}
}
diff --git a/src/mscorlib/src/Internal/Padding.cs b/src/mscorlib/src/Internal/Padding.cs
new file mode 100644
index 0000000000..d25acdc9c7
--- /dev/null
+++ b/src/mscorlib/src/Internal/Padding.cs
@@ -0,0 +1,27 @@
+// 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;
+
+namespace Internal
+{
+
+ /// <summary>A class for common padding constants and eventually routines.</summary>
+ internal static class PaddingHelpers
+ {
+ /// <summary>A size greater than or equal to the size of the most common CPU cache lines.</summary>
+#if ARM64
+ internal const int CACHE_LINE_SIZE = 128;
+#else
+ internal const int CACHE_LINE_SIZE = 64;
+#endif
+ }
+
+ /// <summary>Padding structure used to minimize false sharing</summary>
+ [StructLayout(LayoutKind.Explicit, Size = PaddingHelpers.CACHE_LINE_SIZE - sizeof(int))]
+ internal struct PaddingFor32
+ {
+ }
+}
+
diff --git a/src/mscorlib/src/Internal/Runtime/Augments/RuntimeThread.cs b/src/mscorlib/src/Internal/Runtime/Augments/RuntimeThread.cs
index 605f974da0..4c67ea3fd6 100644
--- a/src/mscorlib/src/Internal/Runtime/Augments/RuntimeThread.cs
+++ b/src/mscorlib/src/Internal/Runtime/Augments/RuntimeThread.cs
@@ -15,6 +15,8 @@ namespace Internal.Runtime.Augments
{
public class RuntimeThread : CriticalFinalizerObject
{
+ private static int s_optimalMaxSpinWaitsPerSpinIteration;
+
internal RuntimeThread() { }
public static RuntimeThread Create(ThreadStart start) => new Thread(start);
@@ -186,6 +188,33 @@ namespace Internal.Runtime.Augments
private extern bool JoinInternal(int millisecondsTimeout);
public static void Sleep(int millisecondsTimeout) => Thread.Sleep(millisecondsTimeout);
+
+ [DllImport(JitHelpers.QCall)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern int GetOptimalMaxSpinWaitsPerSpinIterationInternal();
+
+ /// <summary>
+ /// Max value to be passed into <see cref="SpinWait(int)"/> for optimal delaying. This value is normalized to be
+ /// appropriate for the processor.
+ /// </summary>
+ internal static int OptimalMaxSpinWaitsPerSpinIteration
+ {
+ get
+ {
+ if (s_optimalMaxSpinWaitsPerSpinIteration != 0)
+ {
+ return s_optimalMaxSpinWaitsPerSpinIteration;
+ }
+
+ // This is done lazily because the first call to the function below in the process triggers a measurement that
+ // takes a nontrivial amount of time. See Thread::InitializeYieldProcessorNormalized(), which describes and
+ // calculates this value.
+ s_optimalMaxSpinWaitsPerSpinIteration = GetOptimalMaxSpinWaitsPerSpinIterationInternal();
+ Debug.Assert(s_optimalMaxSpinWaitsPerSpinIteration > 0);
+ return s_optimalMaxSpinWaitsPerSpinIteration;
+ }
+ }
+
public static void SpinWait(int iterations) => Thread.SpinWait(iterations);
public static bool Yield() => Thread.Yield();
diff --git a/src/mscorlib/src/Interop/Unix/Interop.Libraries.cs b/src/mscorlib/src/Interop/Unix/Interop.Libraries.cs
new file mode 100644
index 0000000000..31df8c75de
--- /dev/null
+++ b/src/mscorlib/src/Interop/Unix/Interop.Libraries.cs
@@ -0,0 +1,14 @@
+// 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.
+
+internal static partial class Interop
+{
+ internal static partial class Libraries
+ {
+ internal const string Kernel32 = "libcoreclr";
+ internal const string User32 = "libcoreclr";
+ internal const string Ole32 = "libcoreclr";
+ internal const string OleAut32 = "libcoreclr";
+ }
+}
diff --git a/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs b/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
index 521ea65e10..0a2057ca53 100644
--- a/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
+++ b/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
@@ -119,27 +119,6 @@ namespace Microsoft.Win32
private volatile RegistryView regView = RegistryView.Default;
/**
- * RegistryInternalCheck values. Useful only for CheckPermission
- */
- private enum RegistryInternalCheck
- {
- CheckSubKeyWritePermission = 0,
- CheckSubKeyReadPermission = 1,
- CheckSubKeyCreatePermission = 2,
- CheckSubTreeReadPermission = 3,
- CheckSubTreeWritePermission = 4,
- CheckSubTreeReadWritePermission = 5,
- CheckValueWritePermission = 6,
- CheckValueCreatePermission = 7,
- CheckValueReadPermission = 8,
- CheckKeyReadPermission = 9,
- CheckSubTreePermission = 10,
- CheckOpenSubKeyWithWritablePermission = 11,
- CheckOpenSubKeyPermission = 12
- };
-
-
- /**
* Creates a RegistryKey.
*
* This key is bound to hkey, if writable is <b>false</b> then no write operations
@@ -318,29 +297,6 @@ namespace Microsoft.Win32
return null;
}
- // This required no security checks. This is to get around the Deleting SubKeys which only require
- // write permission. They call OpenSubKey which required read. Now instead call this function w/o security checks
- internal RegistryKey InternalOpenSubKey(String name, bool writable)
- {
- ValidateKeyName(name);
- EnsureNotDisposed();
-
- SafeRegistryHandle result = null;
- int ret = Win32Native.RegOpenKeyEx(hkey,
- name,
- 0,
- GetRegistryKeyAccess(writable) | (int)regView,
- out result);
-
- if (ret == 0 && !result.IsInvalid)
- {
- RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView);
- key.keyName = keyName + "\\" + name;
- return key;
- }
- return null;
- }
-
/**
* Returns a subkey with read only permissions.
*
@@ -502,9 +458,7 @@ namespace Microsoft.Win32
* Note that <var>name</var> can be null or "", at which point the
* unnamed or default value of this Registry key is returned, if any.
* The default values for RegistryKeys are OS-dependent. NT doesn't
- * have them by default, but they can exist and be of any type. On
- * Win95, the default value is always an empty key of type REG_SZ.
- * Win98 supports default values of any type, but defaults to REG_SZ.
+ * have them by default, but they can exist and be of any type.
*
* @param name Name of value to retrieve.
* @param defaultValue Value to return if <i>name</i> doesn't exist.
@@ -1036,7 +990,7 @@ namespace Microsoft.Win32
throw new IOException(SR.Arg_RegKeyNotFound, errorCode);
default:
- throw new IOException(Win32Native.GetMessage(errorCode), errorCode);
+ throw new IOException(Interop.Kernel32.GetMessage(errorCode), errorCode);
}
}
@@ -1102,14 +1056,6 @@ namespace Microsoft.Win32
}
}
- private bool ContainsRegistryValue(string name)
- {
- int type = 0;
- int datasize = 0;
- int retval = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize);
- return retval == 0;
- }
-
private void EnsureNotDisposed()
{
if (hkey == null)
diff --git a/src/mscorlib/src/Microsoft/Win32/UnsafeNativeMethods.cs b/src/mscorlib/src/Microsoft/Win32/UnsafeNativeMethods.cs
index 1b835d5dd1..f36bc18140 100644
--- a/src/mscorlib/src/Microsoft/Win32/UnsafeNativeMethods.cs
+++ b/src/mscorlib/src/Microsoft/Win32/UnsafeNativeMethods.cs
@@ -4,25 +4,20 @@
namespace Microsoft.Win32
{
- using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System;
- using System.Runtime.CompilerServices;
- using System.Runtime.ConstrainedExecution;
+ using System.Diagnostics.Tracing;
using System.Runtime.InteropServices;
- using System.Runtime.Serialization;
- using System.Runtime.Versioning;
using System.Security;
using System.Text;
- using System.Diagnostics.Tracing;
[SuppressUnmanagedCodeSecurityAttribute()]
internal static class UnsafeNativeMethods
{
- [DllImport(Win32Native.KERNEL32, EntryPoint = "GetTimeZoneInformation", SetLastError = true, ExactSpelling = true)]
+ [DllImport(Interop.Libraries.Kernel32, EntryPoint = "GetTimeZoneInformation", SetLastError = true, ExactSpelling = true)]
internal static extern int GetTimeZoneInformation(out Win32Native.TimeZoneInformation lpTimeZoneInformation);
- [DllImport(Win32Native.KERNEL32, EntryPoint = "GetDynamicTimeZoneInformation", SetLastError = true, ExactSpelling = true)]
+ [DllImport(Interop.Libraries.Kernel32, EntryPoint = "GetDynamicTimeZoneInformation", SetLastError = true, ExactSpelling = true)]
internal static extern int GetDynamicTimeZoneInformation(out Win32Native.DynamicTimeZoneInformation lpDynamicTimeZoneInformation);
//
@@ -36,7 +31,7 @@ namespace Microsoft.Win32
// PULONGLONG pululEnumerator
// );
//
- [DllImport(Win32Native.KERNEL32, EntryPoint = "GetFileMUIPath", SetLastError = true, ExactSpelling = true)]
+ [DllImport(Interop.Libraries.Kernel32, EntryPoint = "GetFileMUIPath", SetLastError = true, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetFileMUIPath(
int flags,
@@ -50,18 +45,6 @@ namespace Microsoft.Win32
ref int fileMuiPathLength,
ref Int64 enumerator);
-
- [DllImport(Win32Native.USER32, EntryPoint = "LoadStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
- internal static extern int LoadString(SafeLibraryHandle handle, int id, [Out] StringBuilder buffer, int bufferLength);
-
- [DllImport(Win32Native.KERNEL32, CharSet = System.Runtime.InteropServices.CharSet.Unicode, SetLastError = true)]
- internal static extern SafeLibraryHandle LoadLibraryEx(string libFilename, IntPtr reserved, int flags);
-
- [DllImport(Win32Native.KERNEL32, CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool FreeLibrary(IntPtr hModule);
-
-
[SuppressUnmanagedCodeSecurityAttribute()]
internal static unsafe class ManifestEtw
{
@@ -247,6 +230,7 @@ namespace Microsoft.Win32
int OutBufferSize,
ref int ReturnLength);
}
+
#if FEATURE_COMINTEROP
[DllImport("combase.dll", PreserveSig = true)]
internal static extern int RoGetActivationFactory(
@@ -254,6 +238,5 @@ namespace Microsoft.Win32
[In] ref Guid iid,
[Out, MarshalAs(UnmanagedType.IInspectable)] out Object factory);
#endif
-
}
}
diff --git a/src/mscorlib/src/Microsoft/Win32/Win32Native.cs b/src/mscorlib/src/Microsoft/Win32/Win32Native.cs
index e4d8b9af24..eaa9cedfc6 100644
--- a/src/mscorlib/src/Microsoft/Win32/Win32Native.cs
+++ b/src/mscorlib/src/Microsoft/Win32/Win32Native.cs
@@ -18,13 +18,13 @@
* For handles, you should use a SafeHandle subclass specific to your handle
* type. For files, we have the following set of interesting definitions:
*
- * [DllImport(KERNEL32, SetLastError=true, CharSet=CharSet.Auto, BestFitMapping=false)]
+ * [DllImport(Interop.Libraries.Kernel32, SetLastError=true, CharSet=CharSet.Auto, BestFitMapping=false)]
* private static extern SafeFileHandle CreateFile(...);
*
- * [DllImport(KERNEL32, SetLastError=true)]
+ * [DllImport(Interop.Libraries.Kernel32, SetLastError=true)]
* unsafe internal static extern int ReadFile(SafeFileHandle handle, ...);
*
- * [DllImport(KERNEL32, SetLastError=true)]
+ * [DllImport(Interop.Libraries.Kernel32, SetLastError=true)]
* internal static extern bool CloseHandle(IntPtr handle);
*
* P/Invoke will create the SafeFileHandle instance for you and assign the
@@ -59,7 +59,7 @@
* [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
* internal struct OSVERSIONINFO { ... }
*
- * [DllImport(KERNEL32, CharSet=CharSet.Auto)]
+ * [DllImport(Interop.Libraries.Kernel32, CharSet=CharSet.Auto)]
* internal static extern bool GetVersionEx(ref OSVERSIONINFO lposvi);
*
* OR:
@@ -67,7 +67,7 @@
* [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
* internal class OSVERSIONINFO { ... }
*
- * [DllImport(KERNEL32, CharSet=CharSet.Auto)]
+ * [DllImport(Interop.Libraries.Kernel32, CharSet=CharSet.Auto)]
* internal static extern bool GetVersionEx([In, Out] OSVERSIONINFO lposvi);
*
* Note that classes require being marked as [In, Out] while value types must
@@ -152,9 +152,6 @@ namespace Microsoft.Win32
internal const int REG_RESOURCE_REQUIREMENTS_LIST = 10;
internal const int REG_QWORD = 11; // 64-bit number
- internal const int HWND_BROADCAST = 0xffff;
- internal const int WM_SETTINGCHANGE = 0x001A;
-
// TimeZone
internal const int TIME_ZONE_ID_INVALID = -1;
internal const int TIME_ZONE_ID_UNKNOWN = 0;
@@ -170,9 +167,6 @@ namespace Microsoft.Win32
internal const int MUI_LANG_NEUTRAL_PE_FILE = 0x100;
internal const int MUI_NON_LANG_NEUTRAL_FILE = 0x200;
- internal const int LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
- internal const int LOAD_STRING_MAX_LENGTH = 500;
-
[StructLayout(LayoutKind.Sequential)]
internal struct SystemTime
{
@@ -356,24 +350,6 @@ namespace Microsoft.Win32
internal const int LPTR = (LMEM_FIXED | LMEM_ZEROINIT);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
- internal class OSVERSIONINFO
- {
- internal OSVERSIONINFO()
- {
- OSVersionInfoSize = (int)Marshal.SizeOf(this);
- }
-
- // The OSVersionInfoSize field must be set to Marshal.SizeOf(this)
- internal int OSVersionInfoSize = 0;
- internal int MajorVersion = 0;
- internal int MinorVersion = 0;
- internal int BuildNumber = 0;
- internal int PlatformId = 0;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
- internal String CSDVersion = null;
- }
-
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal class OSVERSIONINFOEX
{
public OSVERSIONINFOEX()
@@ -460,17 +436,6 @@ namespace Microsoft.Win32
internal uint Type;
}
-#if !FEATURE_PAL
- internal const String KERNEL32 = "kernel32.dll";
- internal const String USER32 = "user32.dll";
- internal const String OLE32 = "ole32.dll";
- internal const String OLEAUT32 = "oleaut32.dll";
-#else //FEATURE_PAL
- internal const String KERNEL32 = "libcoreclr";
- internal const String USER32 = "libcoreclr";
- internal const String OLE32 = "libcoreclr";
- internal const String OLEAUT32 = "libcoreclr";
-#endif //FEATURE_PAL
internal const String ADVAPI32 = "advapi32.dll";
internal const String SHELL32 = "shell32.dll";
internal const String SHIM = "mscoree.dll";
@@ -478,34 +443,10 @@ namespace Microsoft.Win32
internal const String SECUR32 = "secur32.dll";
internal const String MSCORWKS = "coreclr.dll";
- [DllImport(KERNEL32, CharSet = CharSet.Auto, BestFitMapping = true)]
- internal static extern int FormatMessage(int dwFlags, IntPtr lpSource,
- int dwMessageId, int dwLanguageId, [Out]StringBuilder lpBuffer,
- int nSize, IntPtr va_list_arguments);
-
- // Gets an error message for a Win32 error code.
- internal static String GetMessage(int errorCode)
- {
- StringBuilder sb = StringBuilderCache.Acquire(512);
- int result = Win32Native.FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
- IntPtr.Zero, errorCode, 0, sb, sb.Capacity, IntPtr.Zero);
- if (result != 0)
- {
- // result is the # of characters copied to the StringBuilder.
- return StringBuilderCache.GetStringAndRelease(sb);
- }
- else
- {
- StringBuilderCache.Release(sb);
- return SR.Format(SR.UnknownError_Num, errorCode);
- }
- }
-
- [DllImport(KERNEL32, EntryPoint = "LocalAlloc")]
+ [DllImport(Interop.Libraries.Kernel32, EntryPoint = "LocalAlloc")]
internal static extern IntPtr LocalAlloc_NoSafeHandle(int uFlags, UIntPtr sizetdwBytes);
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
internal static extern IntPtr LocalFree(IntPtr handle);
internal static bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX buffer)
@@ -514,99 +455,83 @@ namespace Microsoft.Win32
return GlobalMemoryStatusExNative(ref buffer);
}
- [DllImport(KERNEL32, SetLastError = true, EntryPoint = "GlobalMemoryStatusEx")]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true, EntryPoint = "GlobalMemoryStatusEx")]
private static extern bool GlobalMemoryStatusExNative([In, Out] ref MEMORYSTATUSEX buffer);
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
unsafe internal static extern UIntPtr VirtualQuery(void* address, ref MEMORY_BASIC_INFORMATION buffer, UIntPtr sizeOfBuffer);
// VirtualAlloc should generally be avoided, but is needed in
// the MemoryFailPoint implementation (within a CER) to increase the
// size of the page file, ignoring any host memory allocators.
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
unsafe internal static extern void* VirtualAlloc(void* address, UIntPtr numBytes, int commitOrReserve, int pageProtectionMode);
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
unsafe internal static extern bool VirtualFree(void* address, UIntPtr numBytes, int pageFreeMode);
- [DllImport(KERNEL32, CharSet = CharSet.Ansi, ExactSpelling = true, EntryPoint = "lstrlenA")]
+ [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Ansi, ExactSpelling = true, EntryPoint = "lstrlenA")]
internal static extern int lstrlenA(IntPtr ptr);
- [DllImport(KERNEL32, CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "lstrlenW")]
+ [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "lstrlenW")]
internal static extern int lstrlenW(IntPtr ptr);
- [DllImport(Win32Native.OLEAUT32, CharSet = CharSet.Unicode)]
+ [DllImport(Interop.Libraries.OleAut32, CharSet = CharSet.Unicode)]
internal static extern IntPtr SysAllocStringLen(String src, int len); // BSTR
- [DllImport(Win32Native.OLEAUT32)]
+ [DllImport(Interop.Libraries.OleAut32)]
internal static extern uint SysStringLen(IntPtr bstr);
- [DllImport(Win32Native.OLEAUT32)]
+ [DllImport(Interop.Libraries.OleAut32)]
internal static extern void SysFreeString(IntPtr bstr);
#if FEATURE_COMINTEROP
- [DllImport(Win32Native.OLEAUT32)]
+ [DllImport(Interop.Libraries.OleAut32)]
internal static extern IntPtr SysAllocStringByteLen(byte[] str, uint len); // BSTR
- [DllImport(Win32Native.OLEAUT32)]
+ [DllImport(Interop.Libraries.OleAut32)]
internal static extern uint SysStringByteLen(IntPtr bstr);
#endif
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
internal static extern bool SetEvent(SafeWaitHandle handle);
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
internal static extern bool ResetEvent(SafeWaitHandle handle);
- [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern SafeWaitHandle CreateEventEx(SECURITY_ATTRIBUTES lpSecurityAttributes, string name, uint flags, uint desiredAccess);
- [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern SafeWaitHandle OpenEvent(uint desiredAccess, bool inheritHandle, string name);
- [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern SafeWaitHandle CreateMutexEx(SECURITY_ATTRIBUTES lpSecurityAttributes, string name, uint flags, uint desiredAccess);
- [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern SafeWaitHandle OpenMutex(uint desiredAccess, bool inheritHandle, string name);
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
internal static extern bool ReleaseMutex(SafeWaitHandle handle);
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
internal static extern bool CloseHandle(IntPtr handle);
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
internal static unsafe extern int WriteFile(SafeFileHandle handle, byte* bytes, int numBytesToWrite, out int numBytesWritten, IntPtr mustBeZero);
- [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern SafeWaitHandle CreateSemaphoreEx(SECURITY_ATTRIBUTES lpSecurityAttributes, int initialCount, int maximumCount, string name, uint flags, uint desiredAccess);
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ReleaseSemaphore(SafeWaitHandle handle, int releaseCount, out int previousCount);
- [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern SafeWaitHandle OpenSemaphore(uint desiredAccess, bool inheritHandle, string name);
- // Will be in winnls.h
- internal const int FIND_STARTSWITH = 0x00100000; // see if value is at the beginning of source
- internal const int FIND_ENDSWITH = 0x00200000; // see if value is at the end of source
- internal const int FIND_FROMSTART = 0x00400000; // look for value in source, starting at the beginning
- internal const int FIND_FROMEND = 0x00800000; // look for value in source, starting at the end
-
- [StructLayout(LayoutKind.Sequential)]
- internal struct NlsVersionInfoEx
- {
- internal int dwNLSVersionInfoSize;
- internal int dwNLSVersion;
- internal int dwDefinedVersion;
- internal int dwEffectiveId;
- internal Guid guidCustomVersion;
- }
-
- [DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
internal static extern int GetSystemDirectory([Out]StringBuilder sb, int length);
internal static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); // WinBase.h
@@ -616,17 +541,9 @@ namespace Microsoft.Win32
internal const int STD_OUTPUT_HANDLE = -11;
internal const int STD_ERROR_HANDLE = -12;
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
internal static extern IntPtr GetStdHandle(int nStdHandle); // param is NOT a handle, but it returns one!
- // From wincon.h
- internal const int CTRL_C_EVENT = 0;
- internal const int CTRL_BREAK_EVENT = 1;
- internal const int CTRL_CLOSE_EVENT = 2;
- internal const int CTRL_LOGOFF_EVENT = 5;
- internal const int CTRL_SHUTDOWN_EVENT = 6;
- internal const short KEY_EVENT = 1;
-
// From WinBase.h
internal const int FILE_TYPE_DISK = 0x0001;
internal const int FILE_TYPE_CHAR = 0x0002;
@@ -719,14 +636,6 @@ namespace Microsoft.Win32
// From WinStatus.h
internal const int STATUS_ACCOUNT_RESTRICTION = unchecked((int)0xC000006E);
- // Use this to translate error codes like the above into HRESULTs like
- // 0x80070006 for ERROR_INVALID_HANDLE
- internal static int MakeHRFromErrorCode(int errorCode)
- {
- BCLDebug.Assert((0xFFFF0000 & errorCode) == 0, "This is an HRESULT, not an error code!");
- return unchecked(((int)0x80070000) | errorCode);
- }
-
// Win32 Structs in N/Direct style
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
[BestFitMapping(false)]
@@ -756,51 +665,49 @@ namespace Microsoft.Win32
internal String cAlternateFileName = null;
}
- [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern SafeFindHandle FindFirstFile(String fileName, [In, Out] Win32Native.WIN32_FIND_DATA data);
- [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern bool FindNextFile(
SafeFindHandle hndFindFile,
[In, Out, MarshalAs(UnmanagedType.LPStruct)]
WIN32_FIND_DATA lpFindFileData);
- [DllImport(KERNEL32)]
+ [DllImport(Interop.Libraries.Kernel32)]
internal static extern bool FindClose(IntPtr handle);
- [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern bool GetFileAttributesEx(String name, int fileInfoLevel, ref WIN32_FILE_ATTRIBUTE_DATA lpFileInformation);
- internal const int LCID_SUPPORTED = 0x00000002; // supported locale ids
-
- [DllImport(KERNEL32)]
+ [DllImport(Interop.Libraries.Kernel32)]
internal static extern unsafe int WideCharToMultiByte(uint cp, uint flags, char* pwzSource, int cchSource, byte* pbDestBuffer, int cbDestBuffer, IntPtr null1, IntPtr null2);
- [DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
internal static extern bool SetEnvironmentVariable(string lpName, string lpValue);
- [DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
internal static extern int GetEnvironmentVariable(string lpName, [Out]StringBuilder lpValue, int size);
- [DllImport(KERNEL32, CharSet = CharSet.Unicode)]
+ [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Unicode)]
internal static unsafe extern char* GetEnvironmentStrings();
- [DllImport(KERNEL32, CharSet = CharSet.Unicode)]
+ [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Unicode)]
internal static unsafe extern bool FreeEnvironmentStrings(char* pStrings);
- [DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Auto, SetLastError = true)]
internal static extern uint GetCurrentProcessId();
- [DllImport(OLE32)]
+ [DllImport(Interop.Libraries.Ole32)]
internal extern static int CoCreateGuid(out Guid guid);
- [DllImport(OLE32)]
+ [DllImport(Interop.Libraries.Ole32)]
internal static extern IntPtr CoTaskMemAlloc(UIntPtr cb);
- [DllImport(OLE32)]
+ [DllImport(Interop.Libraries.Ole32)]
internal static extern void CoTaskMemFree(IntPtr ptr);
- [DllImport(OLE32)]
+ [DllImport(Interop.Libraries.Ole32)]
internal static extern IntPtr CoTaskMemRealloc(IntPtr pv, UIntPtr cb);
#if FEATURE_WIN32_REGISTRY
@@ -825,14 +732,6 @@ namespace Microsoft.Win32
int ulOptions, int samDesired, out SafeRegistryHandle hkResult);
[DllImport(ADVAPI32, CharSet = CharSet.Auto, BestFitMapping = false)]
- internal static extern int RegQueryInfoKey(SafeRegistryHandle hKey, [Out]StringBuilder lpClass,
- int[] lpcbClass, IntPtr lpReserved_MustBeZero, ref int lpcSubKeys,
- int[] lpcbMaxSubKeyLen, int[] lpcbMaxClassLen,
- ref int lpcValues, int[] lpcbMaxValueNameLen,
- int[] lpcbMaxValueLen, int[] lpcbSecurityDescriptor,
- int[] lpftLastWriteTime);
-
- [DllImport(ADVAPI32, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern int RegQueryValueEx(SafeRegistryHandle hKey, String lpValueName,
int[] lpReserved, ref int lpType, [Out] byte[] lpData,
ref int lpcbData);
@@ -869,74 +768,13 @@ namespace Microsoft.Win32
int Reserved, RegistryValueKind dwType, String lpData, int cbData);
#endif // FEATURE_WIN32_REGISTRY
- [DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
+ [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
internal static extern int ExpandEnvironmentStrings(String lpSrc, [Out]StringBuilder lpDst, int nSize);
- [DllImport(KERNEL32)]
+ [DllImport(Interop.Libraries.Kernel32)]
internal static extern IntPtr LocalReAlloc(IntPtr handle, IntPtr sizetcbBytes, int uFlags);
- internal const int SHGFP_TYPE_CURRENT = 0; // the current (user) folder path setting
- internal const int UOI_FLAGS = 1;
- internal const int WSF_VISIBLE = 1;
-
- // .NET Framework 4.0 and newer - all versions of windows ||| \public\sdk\inc\shlobj.h
- internal const int CSIDL_FLAG_CREATE = 0x8000; // force folder creation in SHGetFolderPath
- internal const int CSIDL_FLAG_DONT_VERIFY = 0x4000; // return an unverified folder path
- internal const int CSIDL_ADMINTOOLS = 0x0030; // <user name>\Start Menu\Programs\Administrative Tools
- internal const int CSIDL_CDBURN_AREA = 0x003b; // USERPROFILE\Local Settings\Application Data\Microsoft\CD Burning
- internal const int CSIDL_COMMON_ADMINTOOLS = 0x002f; // All Users\Start Menu\Programs\Administrative Tools
- internal const int CSIDL_COMMON_DOCUMENTS = 0x002e; // All Users\Documents
- internal const int CSIDL_COMMON_MUSIC = 0x0035; // All Users\My Music
- internal const int CSIDL_COMMON_OEM_LINKS = 0x003a; // Links to All Users OEM specific apps
- internal const int CSIDL_COMMON_PICTURES = 0x0036; // All Users\My Pictures
- internal const int CSIDL_COMMON_STARTMENU = 0x0016; // All Users\Start Menu
- internal const int CSIDL_COMMON_PROGRAMS = 0X0017; // All Users\Start Menu\Programs
- internal const int CSIDL_COMMON_STARTUP = 0x0018; // All Users\Startup
- internal const int CSIDL_COMMON_DESKTOPDIRECTORY = 0x0019; // All Users\Desktop
- internal const int CSIDL_COMMON_TEMPLATES = 0x002d; // All Users\Templates
- internal const int CSIDL_COMMON_VIDEO = 0x0037; // All Users\My Video
- internal const int CSIDL_FONTS = 0x0014; // windows\fonts
- internal const int CSIDL_MYVIDEO = 0x000e; // "My Videos" folder
- internal const int CSIDL_NETHOOD = 0x0013; // %APPDATA%\Microsoft\Windows\Network Shortcuts
- internal const int CSIDL_PRINTHOOD = 0x001b; // %APPDATA%\Microsoft\Windows\Printer Shortcuts
- internal const int CSIDL_PROFILE = 0x0028; // %USERPROFILE% (%SystemDrive%\Users\%USERNAME%)
- internal const int CSIDL_PROGRAM_FILES_COMMONX86 = 0x002c; // x86 Program Files\Common on RISC
- internal const int CSIDL_PROGRAM_FILESX86 = 0x002a; // x86 C:\Program Files on RISC
- internal const int CSIDL_RESOURCES = 0x0038; // %windir%\Resources
- internal const int CSIDL_RESOURCES_LOCALIZED = 0x0039; // %windir%\resources\0409 (code page)
- internal const int CSIDL_SYSTEMX86 = 0x0029; // %windir%\system32
- internal const int CSIDL_WINDOWS = 0x0024; // GetWindowsDirectory()
-
- // .NET Framework 3.5 and earlier - all versions of windows
- internal const int CSIDL_APPDATA = 0x001a;
- internal const int CSIDL_COMMON_APPDATA = 0x0023;
- internal const int CSIDL_LOCAL_APPDATA = 0x001c;
- internal const int CSIDL_COOKIES = 0x0021;
- internal const int CSIDL_FAVORITES = 0x0006;
- internal const int CSIDL_HISTORY = 0x0022;
- internal const int CSIDL_INTERNET_CACHE = 0x0020;
- internal const int CSIDL_PROGRAMS = 0x0002;
- internal const int CSIDL_RECENT = 0x0008;
- internal const int CSIDL_SENDTO = 0x0009;
- internal const int CSIDL_STARTMENU = 0x000b;
- internal const int CSIDL_STARTUP = 0x0007;
- internal const int CSIDL_SYSTEM = 0x0025;
- internal const int CSIDL_TEMPLATES = 0x0015;
- internal const int CSIDL_DESKTOPDIRECTORY = 0x0010;
- internal const int CSIDL_PERSONAL = 0x0005;
- internal const int CSIDL_PROGRAM_FILES = 0x0026;
- internal const int CSIDL_PROGRAM_FILES_COMMON = 0x002b;
- internal const int CSIDL_DESKTOP = 0x0000;
- internal const int CSIDL_DRIVES = 0x0011;
- internal const int CSIDL_MYMUSIC = 0x000d;
- internal const int CSIDL_MYPICTURES = 0x0027;
-
- internal const int NameSamCompatible = 2;
-
- [DllImport(USER32, SetLastError = true, BestFitMapping = false)]
- internal static extern IntPtr SendMessageTimeout(IntPtr hWnd, int Msg, IntPtr wParam, String lParam, uint fuFlags, uint uTimeout, IntPtr lpdwResult);
-
- [DllImport(KERNEL32, SetLastError = true)]
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal extern static bool QueryUnbiasedInterruptTime(out ulong UnbiasedTime);
diff --git a/src/mscorlib/src/System/Activator.cs b/src/mscorlib/src/System/Activator.cs
index 50c517339b..be938aa43d 100644
--- a/src/mscorlib/src/System/Activator.cs
+++ b/src/mscorlib/src/System/Activator.cs
@@ -51,7 +51,6 @@ namespace System
return CreateInstance(type, bindingAttr, binder, args, culture, null);
}
- [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
static public Object CreateInstance(Type type,
BindingFlags bindingAttr,
Binder binder,
@@ -80,8 +79,7 @@ namespace System
if (rt == null)
throw new ArgumentException(SR.Arg_MustBeType, nameof(type));
- StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return rt.CreateInstanceImpl(bindingAttr, binder, args, culture, activationAttributes, ref stackMark);
+ return rt.CreateInstanceImpl(bindingAttr, binder, args, culture, activationAttributes);
}
static public Object CreateInstance(Type type, params Object[] args)
@@ -111,9 +109,13 @@ namespace System
return Activator.CreateInstance(type, false);
}
- [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
static public Object CreateInstance(Type type, bool nonPublic)
{
+ return CreateInstance(type, nonPublic, wrapExceptions: true);
+ }
+
+ static internal Object CreateInstance(Type type, bool nonPublic, bool wrapExceptions)
+ {
if ((object)type == null)
throw new ArgumentNullException(nameof(type));
Contract.EndContractBlock();
@@ -123,11 +125,9 @@ namespace System
if (rt == null)
throw new ArgumentException(SR.Arg_MustBeType, nameof(type));
- StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return rt.CreateInstanceDefaultCtor(!nonPublic, false, true, ref stackMark);
+ return rt.CreateInstanceDefaultCtor(!nonPublic, false, true, wrapExceptions);
}
- [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
static public T CreateInstance<T>()
{
RuntimeType rt = typeof(T) as RuntimeType;
@@ -137,10 +137,8 @@ namespace System
if (rt.HasElementType)
throw new MissingMethodException(SR.Arg_NoDefCTor);
- StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
-
// Skip the CreateInstanceCheckThis call to avoid perf cost and to maintain compatibility with V2 (throwing the same exceptions).
- return (T)rt.CreateInstanceDefaultCtor(true /*publicOnly*/, true /*skipCheckThis*/, true /*fillCache*/, ref stackMark);
+ return (T)rt.CreateInstanceDefaultCtor(true /*publicOnly*/, true /*skipCheckThis*/, true /*fillCache*/, true /*wrapExceptions*/);
}
}
}
diff --git a/src/mscorlib/src/System/AppContext/AppContextDefaultValues.cs b/src/mscorlib/src/System/AppContext/AppContextDefaultValues.cs
index 9f00e8148c..320a14109b 100644
--- a/src/mscorlib/src/System/AppContext/AppContextDefaultValues.cs
+++ b/src/mscorlib/src/System/AppContext/AppContextDefaultValues.cs
@@ -114,7 +114,7 @@ namespace System
{
value = value.Substring(1);
}
- Version realVersion = new Version(value);
+ Version realVersion = Version.Parse(value);
// The version class will represent some unset values as -1 internally (instead of 0).
version = realVersion.Major * 10000;
if (realVersion.Minor > 0)
diff --git a/src/mscorlib/src/System/AppDomain.cs b/src/mscorlib/src/System/AppDomain.cs
index 553b83feee..1c009ed605 100644
--- a/src/mscorlib/src/System/AppDomain.cs
+++ b/src/mscorlib/src/System/AppDomain.cs
@@ -226,7 +226,7 @@ namespace System
/// <summary>
/// Initialize the compatibility flags to non-NULL values.
- /// This method is also called from the VM when the default domain dosen't have a domain manager.
+ /// This method is also called from the VM when the default domain doesn't have a domain manager.
/// </summary>
private void InitializeCompatibilityFlags()
{
@@ -398,7 +398,7 @@ namespace System
}
[Obsolete("AppDomain.GetCurrentThreadId has been deprecated because it does not provide a stable Id when managed threads are running on fibers (aka lightweight threads). To get a stable identifier for a managed thread, use the ManagedThreadId property on Thread. http://go.microsoft.com/fwlink/?linkid=14202", false)]
- [DllImport(Microsoft.Win32.Win32Native.KERNEL32)]
+ [DllImport(Interop.Libraries.Kernel32)]
public static extern int GetCurrentThreadId();
private AppDomain()
@@ -874,7 +874,7 @@ namespace System
/// <summary>
/// Handle used to marshal an AppDomain to the VM (eg QCall). When marshaled via a QCall, the target
- /// method in the VM will recieve a QCall::AppDomainHandle parameter.
+ /// method in the VM will receive a QCall::AppDomainHandle parameter.
/// </summary>
internal struct AppDomainHandle
{
diff --git a/src/mscorlib/src/System/AppDomainSetup.cs b/src/mscorlib/src/System/AppDomainSetup.cs
index 7d20b57017..d0fdf4e725 100644
--- a/src/mscorlib/src/System/AppDomainSetup.cs
+++ b/src/mscorlib/src/System/AppDomainSetup.cs
@@ -14,7 +14,6 @@ namespace System
{
using System.Text;
using System.Runtime.InteropServices;
- using System.Runtime.Serialization;
using System.Security;
using Path = System.IO.Path;
using System.Diagnostics;
@@ -62,20 +61,8 @@ namespace System
// A collection of strings used to indicate which breaking changes shouldn't be applied
// to an AppDomain. We only use the keys, the values are ignored.
- [OptionalField(VersionAdded = 4)]
private Dictionary<string, object> _CompatFlags;
- [OptionalField(VersionAdded = 5)] // This was added in .NET FX v4.5
- private String _TargetFrameworkName;
-
- [OptionalField(VersionAdded = 5)] // This was added in .NET FX v4.5
- private bool _CheckedForTargetFrameworkName;
-
-#if FEATURE_RANDOMIZED_STRING_HASHING
- [OptionalField(VersionAdded = 5)] // This was added in .NET FX v4.5
- private bool _UseRandomizedStringHashing;
-#endif
-
internal AppDomainSetup(AppDomainSetup copy, bool copyDomainBoundData)
{
string[] mine = Value;
@@ -101,13 +88,6 @@ namespace System
{
SetCompatibilitySwitches(copy._CompatFlags.Keys);
}
-
- _TargetFrameworkName = copy._TargetFrameworkName;
-
-#if FEATURE_RANDOMIZED_STRING_HASHING
- _UseRandomizedStringHashing = copy._UseRandomizedStringHashing;
-#endif
-
}
}
@@ -168,20 +148,11 @@ namespace System
public void SetCompatibilitySwitches(IEnumerable<String> switches)
{
-#if FEATURE_RANDOMIZED_STRING_HASHING
- _UseRandomizedStringHashing = false;
-#endif
if (switches != null)
{
_CompatFlags = new Dictionary<string, object>();
foreach (String str in switches)
{
-#if FEATURE_RANDOMIZED_STRING_HASHING
- if (StringComparer.OrdinalIgnoreCase.Equals("UseRandomizedStringHashAlgorithm", str))
- {
- _UseRandomizedStringHashing = true;
- }
-#endif
_CompatFlags.Add(str, null);
}
}
@@ -196,11 +167,7 @@ namespace System
{
get
{
- return _TargetFrameworkName;
- }
- set
- {
- _TargetFrameworkName = value;
+ return null;
}
}
diff --git a/src/mscorlib/src/System/AppDomainUnloadedException.cs b/src/mscorlib/src/System/AppDomainUnloadedException.cs
index a5e74e2b23..b673d8c998 100644
--- a/src/mscorlib/src/System/AppDomainUnloadedException.cs
+++ b/src/mscorlib/src/System/AppDomainUnloadedException.cs
@@ -21,7 +21,7 @@ namespace System
public AppDomainUnloadedException()
: base(SR.Arg_AppDomainUnloadedException)
{
- HResult = __HResults.COR_E_APPDOMAINUNLOADED;
+ HResult = HResults.COR_E_APPDOMAINUNLOADED;
}
//
diff --git a/src/mscorlib/src/System/Array.cs b/src/mscorlib/src/System/Array.cs
index 62a4ecf16e..01c1ac0234 100644
--- a/src/mscorlib/src/System/Array.cs
+++ b/src/mscorlib/src/System/Array.cs
@@ -2418,13 +2418,14 @@ namespace System
private sealed class SZArrayEnumerator : IEnumerator, ICloneable
{
- private Array _array;
+ private readonly Array _array;
private int _index;
- private int _endIndex; // cache array length, since it's a little slow.
+ private int _endIndex; // Cache Array.Length, since it's a little slow.
internal SZArrayEnumerator(Array array)
{
Debug.Assert(array.Rank == 1 && array.GetLowerBound(0) == 0, "SZArrayEnumerator only works on single dimension arrays w/ a lower bound of zero.");
+
_array = array;
_index = -1;
_endIndex = array.Length;
@@ -2612,8 +2613,7 @@ namespace System
//! Warning: "this" is an array, not an SZArrayHelper. See comments above
//! or you may introduce a security hole!
T[] _this = JitHelpers.UnsafeCast<T[]>(this);
- int length = _this.Length;
- return length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(_this, length);
+ return _this.Length == 0 ? SZGenericArrayEnumerator<T>.Empty : new SZGenericArrayEnumerator<T>(_this);
}
// -----------------------------------------------------------
@@ -2724,54 +2724,53 @@ namespace System
//
private sealed class SZGenericArrayEnumerator<T> : IEnumerator<T>
{
- private T[] _array;
+ private readonly T[] _array;
private int _index;
- private int _endIndex; // cache array length, since it's a little slow.
- // Passing -1 for endIndex so that MoveNext always returns false without mutating _index
- internal static readonly SZGenericArrayEnumerator<T> Empty = new SZGenericArrayEnumerator<T>(null, -1);
+ // Array.Empty is intentionally omitted here, since we don't want to pay for generic instantiations that
+ // wouldn't have otherwise been used.
+ internal static readonly SZGenericArrayEnumerator<T> Empty = new SZGenericArrayEnumerator<T>(new T[0]);
- internal SZGenericArrayEnumerator(T[] array, int endIndex)
+ internal SZGenericArrayEnumerator(T[] array)
{
- // We allow passing null array in case of empty enumerator.
- Debug.Assert(array != null || endIndex == -1, "endIndex should be -1 in the case of a null array (for the empty enumerator).");
+ Debug.Assert(array != null);
+
_array = array;
_index = -1;
- _endIndex = endIndex;
}
public bool MoveNext()
{
- if (_index < _endIndex)
+ int index = _index + 1;
+ bool result = index < _array.Length;
+
+ if (result)
{
- _index++;
- return (_index < _endIndex);
+ _index = index;
}
- return false;
- }
+ return result;
+ }
+
public T Current
{
get
{
- if (_index < 0) ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumNotStarted();
- if (_index >= _endIndex) ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumEnded();
- return _array[_index];
- }
- }
+ int index = _index;
+ T[] array = _array;
- object IEnumerator.Current
- {
- get
- {
- return Current;
+ if ((uint)index >= (uint)array.Length)
+ {
+ ThrowHelper.ThrowInvalidOperationException_EnumCurrent(index);
+ }
+
+ return array[index];
}
}
+
+ object IEnumerator.Current => Current;
- void IEnumerator.Reset()
- {
- _index = -1;
- }
+ void IEnumerator.Reset() => _index = -1;
public void Dispose()
{
diff --git a/src/mscorlib/src/System/Buffer.cs b/src/mscorlib/src/System/Buffer.cs
index 92b938df8c..71859ec587 100644
--- a/src/mscorlib/src/System/Buffer.cs
+++ b/src/mscorlib/src/System/Buffer.cs
@@ -2,7 +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.
-#if AMD64 || (BIT32 && !ARM)
+#if AMD64 || ARM64 || (BIT32 && !ARM)
#define HAS_CUSTOM_BLOCKS
#endif
@@ -262,6 +262,17 @@ namespace System
{
#if AMD64 || (BIT32 && !ARM)
const nuint CopyThreshold = 2048;
+#elif ARM64
+#if PLATFORM_WINDOWS
+ // TODO-ARM64-WINDOWS-OPT determine optimal value for Windows
+ // https://github.com/dotnet/coreclr/issues/13843
+ const nuint CopyThreshold = 2048;
+#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 = UInt64.MaxValue;
+#endif // PLATFORM_WINDOWS
#else
const nuint CopyThreshold = 512;
#endif // AMD64 || (BIT32 && !ARM)
@@ -369,7 +380,6 @@ namespace System
{
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);
diff --git a/src/mscorlib/src/System/ByReference.cs b/src/mscorlib/src/System/ByReference.cs
index 833dab0d55..8079605bfb 100644
--- a/src/mscorlib/src/System/ByReference.cs
+++ b/src/mscorlib/src/System/ByReference.cs
@@ -3,12 +3,14 @@
// See the LICENSE file in the project root for more information.
using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
namespace System
{
// ByReference<T> is meant to be used to represent "ref T" fields. It is working
// around lack of first class support for byref fields in C# and IL. The JIT and
// type loader has special handling for it that turns it into a thin wrapper around ref T.
+ [NonVersionable]
internal struct ByReference<T>
{
private IntPtr _value;
diff --git a/src/mscorlib/src/System/Collections/Concurrent/ConcurrentDictionary.cs b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentDictionary.cs
index 436808c439..4111c5964e 100644
--- a/src/mscorlib/src/System/Collections/Concurrent/ConcurrentDictionary.cs
+++ b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentDictionary.cs
@@ -2,9 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-//
/*============================================================
**
+** Class: ConcurrentDictionary
**
**
** Purpose: A scalable dictionary for concurrent access
@@ -12,18 +12,13 @@
**
===========================================================*/
-using System;
-using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
-using System.Diagnostics.Contracts;
-using System.Runtime.InteropServices;
-using System.Runtime.Serialization;
-using System.Text;
+using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Threading;
-using System.Security;
namespace System.Collections.Concurrent
{
@@ -46,91 +41,78 @@ namespace System.Collections.Concurrent
/// Wrapping the three tables in a single object allows us to atomically
/// replace all tables at once.
/// </summary>
- private class Tables
+ private sealed class Tables
{
- internal readonly Node[] m_buckets; // A singly-linked list for each bucket.
- internal readonly object[] m_locks; // A set of locks, each guarding a section of the table.
- internal volatile int[] m_countPerLock; // The number of elements guarded by each lock.
- internal readonly IEqualityComparer<TKey> m_comparer; // Key equality comparer
+ internal readonly Node[] _buckets; // A singly-linked list for each bucket.
+ internal readonly object[] _locks; // A set of locks, each guarding a section of the table.
+ internal volatile int[] _countPerLock; // The number of elements guarded by each lock.
- internal Tables(Node[] buckets, object[] locks, int[] countPerLock, IEqualityComparer<TKey> comparer)
+ internal Tables(Node[] buckets, object[] locks, int[] countPerLock)
{
- m_buckets = buckets;
- m_locks = locks;
- m_countPerLock = countPerLock;
- m_comparer = comparer;
+ _buckets = buckets;
+ _locks = locks;
+ _countPerLock = countPerLock;
}
}
- private volatile Tables m_tables; // Internal tables of the dictionary
- // NOTE: this is only used for compat reasons to serialize the comparer.
- // This should not be accessed from anywhere else outside of the serialization methods.
- internal IEqualityComparer<TKey> m_comparer;
- private readonly bool m_growLockArray; // Whether to dynamically increase the size of the striped lock
-
- // How many times we resized becaused of collisions.
- // This is used to make sure we don't resize the dictionary because of multi-threaded Add() calls
- // that generate collisions. Whenever a GrowTable() should be the only place that changes this
- private int m_keyRehashCount;
-
- private int m_budget; // The maximum number of elements per lock before a resize operation is triggered
-
- // The default concurrency level is DEFAULT_CONCURRENCY_MULTIPLIER * #CPUs. The higher the
- // DEFAULT_CONCURRENCY_MULTIPLIER, the more concurrent writes can take place without interference
- // and blocking, but also the more expensive operations that require all locks become (e.g. table
- // resizing, ToArray, Count, etc). According to brief benchmarks that we ran, 4 seems like a good
- // compromise.
- private const int DEFAULT_CONCURRENCY_MULTIPLIER = 4;
+ private volatile Tables _tables; // Internal tables of the dictionary
+ private IEqualityComparer<TKey> _comparer; // Key equality comparer
+ private readonly bool _growLockArray; // Whether to dynamically increase the size of the striped lock
+ private int _budget; // The maximum number of elements per lock before a resize operation is triggered
// The default capacity, i.e. the initial # of buckets. When choosing this value, we are making
// a trade-off between the size of a very small dictionary, and the number of resizes when
// constructing a large dictionary. Also, the capacity should not be divisible by a small prime.
- private const int DEFAULT_CAPACITY = 31;
+ private const int DefaultCapacity = 31;
// The maximum size of the striped lock that will not be exceeded when locks are automatically
// added as the dictionary grows. However, the user is allowed to exceed this limit by passing
- // a concurrency level larger than MAX_LOCK_NUMBER into the constructor.
- private const int MAX_LOCK_NUMBER = 1024;
+ // a concurrency level larger than MaxLockNumber into the constructor.
+ private const int MaxLockNumber = 1024;
// Whether TValue is a type that can be written atomically (i.e., with no danger of torn reads)
private static readonly bool s_isValueWriteAtomic = IsValueWriteAtomic();
-
/// <summary>
/// Determines whether type TValue can be written atomically
/// </summary>
private static bool IsValueWriteAtomic()
{
- Type valueType = typeof(TValue);
- if (valueType.IsEnum)
- {
- valueType = Enum.GetUnderlyingType(valueType);
- }
-
//
// Section 12.6.6 of ECMA CLI explains which types can be read and written atomically without
// the risk of tearing.
//
// See http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf
//
- bool isAtomic =
- (valueType.IsClass)
- || valueType == typeof(Boolean)
- || valueType == typeof(Char)
- || valueType == typeof(Byte)
- || valueType == typeof(SByte)
- || valueType == typeof(Int16)
- || valueType == typeof(UInt16)
- || valueType == typeof(Int32)
- || valueType == typeof(UInt32)
- || valueType == typeof(Single);
-
- if (!isAtomic && IntPtr.Size == 8)
+ Type valueType = typeof(TValue);
+ if (!valueType.IsValueType)
+ {
+ return true;
+ }
+ if (valueType.IsEnum)
{
- isAtomic |= valueType == typeof(Double) || valueType == typeof(Int64);
+ valueType = Enum.GetUnderlyingType(valueType);
}
- return isAtomic;
+ switch (Type.GetTypeCode(valueType))
+ {
+ case TypeCode.Boolean:
+ case TypeCode.Byte:
+ case TypeCode.Char:
+ case TypeCode.Int16:
+ case TypeCode.Int32:
+ case TypeCode.SByte:
+ case TypeCode.Single:
+ case TypeCode.UInt16:
+ case TypeCode.UInt32:
+ return true;
+ case TypeCode.Int64:
+ case TypeCode.Double:
+ case TypeCode.UInt64:
+ return IntPtr.Size == 8;
+ default:
+ return false;
+ }
}
/// <summary>
@@ -139,7 +121,7 @@ namespace System.Collections.Concurrent
/// class that is empty, has the default concurrency level, has the default initial capacity, and
/// uses the default comparer for the key type.
/// </summary>
- public ConcurrentDictionary() : this(DefaultConcurrencyLevel, DEFAULT_CAPACITY, true, EqualityComparer<TKey>.Default) { }
+ public ConcurrentDictionary() : this(DefaultConcurrencyLevel, DefaultCapacity, true, null) { }
internal ConcurrentDictionary(int concurrencyLevel, int capacity, bool growLockArray, IEqualityComparer<TKey> comparer)
{
@@ -151,7 +133,6 @@ namespace System.Collections.Concurrent
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ConcurrentDictionary_CapacityMustNotBeNegative);
}
- if (comparer == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer);
// The capacity should be at least as large as the concurrency level. Otherwise, we would have locks that don't guard
// any buckets.
@@ -168,13 +149,13 @@ namespace System.Collections.Concurrent
int[] countPerLock = new int[locks.Length];
Node[] buckets = new Node[capacity];
- m_tables = new Tables(buckets, locks, countPerLock, comparer);
+ _tables = new Tables(buckets, locks, countPerLock);
- m_growLockArray = growLockArray;
- m_budget = buckets.Length / locks.Length;
+ _comparer = comparer ?? EqualityComparer<TKey>.Default;
+ _growLockArray = growLockArray;
+ _budget = buckets.Length / locks.Length;
}
-
/// <summary>
/// Attempts to add the specified key and value to the <see cref="ConcurrentDictionary{TKey,
/// TValue}"/>.
@@ -191,9 +172,9 @@ namespace System.Collections.Concurrent
/// contains too many elements.</exception>
public bool TryAdd(TKey key, TValue value)
{
- if (key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ if (key == null) ThrowKeyNullException();
TValue dummy;
- return TryAddInternal(key, value, false, true, out dummy);
+ return TryAddInternal(key, _comparer.GetHashCode(key), value, false, true, out dummy);
}
/// <summary>
@@ -208,14 +189,14 @@ namespace System.Collections.Concurrent
/// (Nothing in Visual Basic).</exception>
public bool ContainsKey(TKey key)
{
- if (key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ if (key == null) ThrowKeyNullException();
TValue throwAwayValue;
return TryGetValue(key, out throwAwayValue);
}
/// <summary>
- /// Attempts to remove and return the the value with the specified key from the
+ /// Attempts to remove and return the value with the specified key from the
/// <see cref="ConcurrentDictionary{TKey, TValue}"/>.
/// </summary>
/// <param name="key">The key of the element to remove and return.</param>
@@ -228,7 +209,7 @@ namespace System.Collections.Concurrent
/// (Nothing in Visual Basic).</exception>
public bool TryRemove(TKey key, out TValue value)
{
- if (key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ if (key == null) ThrowKeyNullException();
return TryRemoveInternal(key, out value, false, default(TValue));
}
@@ -246,34 +227,33 @@ namespace System.Collections.Concurrent
[SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
private bool TryRemoveInternal(TKey key, out TValue value, bool matchValue, TValue oldValue)
{
+ int hashcode = _comparer.GetHashCode(key);
while (true)
{
- Tables tables = m_tables;
-
- IEqualityComparer<TKey> comparer = tables.m_comparer;
+ Tables tables = _tables;
int bucketNo, lockNo;
- GetBucketAndLockNo(comparer.GetHashCode(key), out bucketNo, out lockNo, tables.m_buckets.Length, tables.m_locks.Length);
+ GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, tables._buckets.Length, tables._locks.Length);
- lock (tables.m_locks[lockNo])
+ lock (tables._locks[lockNo])
{
// If the table just got resized, we may not be holding the right lock, and must retry.
- // This should be a rare occurence.
- if (tables != m_tables)
+ // This should be a rare occurrence.
+ if (tables != _tables)
{
continue;
}
Node prev = null;
- for (Node curr = tables.m_buckets[bucketNo]; curr != null; curr = curr.m_next)
+ for (Node curr = tables._buckets[bucketNo]; curr != null; curr = curr._next)
{
- Assert((prev == null && curr == tables.m_buckets[bucketNo]) || prev.m_next == curr);
+ Debug.Assert((prev == null && curr == tables._buckets[bucketNo]) || prev._next == curr);
- if (comparer.Equals(curr.m_key, key))
+ if (hashcode == curr._hashcode && _comparer.Equals(curr._key, key))
{
if (matchValue)
{
- bool valuesMatch = EqualityComparer<TValue>.Default.Equals(oldValue, curr.m_value);
+ bool valuesMatch = EqualityComparer<TValue>.Default.Equals(oldValue, curr._value);
if (!valuesMatch)
{
value = default(TValue);
@@ -283,15 +263,15 @@ namespace System.Collections.Concurrent
if (prev == null)
{
- Volatile.Write<Node>(ref tables.m_buckets[bucketNo], curr.m_next);
+ Volatile.Write<Node>(ref tables._buckets[bucketNo], curr._next);
}
else
{
- prev.m_next = curr.m_next;
+ prev._next = curr._next;
}
- value = curr.m_value;
- tables.m_countPerLock[lockNo]--;
+ value = curr._value;
+ tables._countPerLock[lockNo]--;
return true;
}
prev = curr;
@@ -319,27 +299,32 @@ namespace System.Collections.Concurrent
[SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
public bool TryGetValue(TKey key, out TValue value)
{
- if (key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ if (key == null) ThrowKeyNullException();
+ return TryGetValueInternal(key, _comparer.GetHashCode(key), out value);
+ }
- int bucketNo, lockNoUnused;
+ private bool TryGetValueInternal(TKey key, int hashcode, out TValue value)
+ {
+ Debug.Assert(_comparer.GetHashCode(key) == hashcode);
+
+ // We must capture the _buckets field in a local variable. It is set to a new table on each table resize.
+ Tables tables = _tables;
- // We must capture the m_buckets field in a local variable. It is set to a new table on each table resize.
- Tables tables = m_tables;
- IEqualityComparer<TKey> comparer = tables.m_comparer;
- GetBucketAndLockNo(comparer.GetHashCode(key), out bucketNo, out lockNoUnused, tables.m_buckets.Length, tables.m_locks.Length);
+ int bucketNo = GetBucket(hashcode, tables._buckets.Length);
// We can get away w/out a lock here.
- // The Volatile.Read ensures that the load of the fields of 'n' doesn't move before the load from buckets[i].
- Node n = Volatile.Read<Node>(ref tables.m_buckets[bucketNo]);
+ // The Volatile.Read ensures that we have a copy of the reference to tables._buckets[bucketNo].
+ // This protects us from reading fields ('_hashcode', '_key', '_value' and '_next') of different instances.
+ Node n = Volatile.Read<Node>(ref tables._buckets[bucketNo]);
while (n != null)
{
- if (comparer.Equals(n.m_key, key))
+ if (hashcode == n._hashcode && _comparer.Equals(n._key, key))
{
- value = n.m_value;
+ value = n._value;
return true;
}
- n = n.m_next;
+ n = n._next;
}
value = default(TValue);
@@ -356,9 +341,9 @@ namespace System.Collections.Concurrent
{
AcquireAllLocks(ref locksAcquired);
- Tables newTables = new Tables(new Node[DEFAULT_CAPACITY], m_tables.m_locks, new int[m_tables.m_countPerLock.Length], m_tables.m_comparer);
- m_tables = newTables;
- m_budget = Math.Max(1, newTables.m_buckets.Length / newTables.m_locks.Length);
+ Tables newTables = new Tables(new Node[DefaultCapacity], _tables._locks, new int[_tables._countPerLock.Length]);
+ _tables = newTables;
+ _budget = Math.Max(1, newTables._buckets.Length / newTables._locks.Length);
}
finally
{
@@ -400,9 +385,9 @@ namespace System.Collections.Concurrent
int count = 0;
- for (int i = 0; i < m_tables.m_locks.Length && count >= 0; i++)
+ for (int i = 0; i < _tables._locks.Length && count >= 0; i++)
{
- count += m_tables.m_countPerLock[i];
+ count += _tables._countPerLock[i];
}
if (array.Length - count < index || count < 0) //"count" itself or "count + index" can overflow
@@ -434,14 +419,18 @@ namespace System.Collections.Concurrent
int count = 0;
checked
{
- for (int i = 0; i < m_tables.m_locks.Length; i++)
+ for (int i = 0; i < _tables._locks.Length; i++)
{
- count += m_tables.m_countPerLock[i];
+ count += _tables._countPerLock[i];
}
}
- KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[count];
+ if (count == 0)
+ {
+ return Array.Empty<KeyValuePair<TKey, TValue>>();
+ }
+ KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[count];
CopyToPairs(array, 0);
return array;
}
@@ -454,16 +443,16 @@ namespace System.Collections.Concurrent
/// <summary>
/// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo.
///
- /// Important: the caller must hold all locks in m_locks before calling CopyToPairs.
+ /// Important: the caller must hold all locks in _locks before calling CopyToPairs.
/// </summary>
private void CopyToPairs(KeyValuePair<TKey, TValue>[] array, int index)
{
- Node[] buckets = m_tables.m_buckets;
+ Node[] buckets = _tables._buckets;
for (int i = 0; i < buckets.Length; i++)
{
- for (Node current = buckets[i]; current != null; current = current.m_next)
+ for (Node current = buckets[i]; current != null; current = current._next)
{
- array[index] = new KeyValuePair<TKey, TValue>(current.m_key, current.m_value);
+ array[index] = new KeyValuePair<TKey, TValue>(current._key, current._value);
index++; //this should never flow, CopyToPairs is only called when there's no overflow risk
}
}
@@ -472,16 +461,16 @@ namespace System.Collections.Concurrent
/// <summary>
/// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo.
///
- /// Important: the caller must hold all locks in m_locks before calling CopyToEntries.
+ /// Important: the caller must hold all locks in _locks before calling CopyToEntries.
/// </summary>
private void CopyToEntries(DictionaryEntry[] array, int index)
{
- Node[] buckets = m_tables.m_buckets;
+ Node[] buckets = _tables._buckets;
for (int i = 0; i < buckets.Length; i++)
{
- for (Node current = buckets[i]; current != null; current = current.m_next)
+ for (Node current = buckets[i]; current != null; current = current._next)
{
- array[index] = new DictionaryEntry(current.m_key, current.m_value);
+ array[index] = new DictionaryEntry(current._key, current._value);
index++; //this should never flow, CopyToEntries is only called when there's no overflow risk
}
}
@@ -490,16 +479,16 @@ namespace System.Collections.Concurrent
/// <summary>
/// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo.
///
- /// Important: the caller must hold all locks in m_locks before calling CopyToObjects.
+ /// Important: the caller must hold all locks in _locks before calling CopyToObjects.
/// </summary>
private void CopyToObjects(object[] array, int index)
{
- Node[] buckets = m_tables.m_buckets;
+ Node[] buckets = _tables._buckets;
for (int i = 0; i < buckets.Length; i++)
{
- for (Node current = buckets[i]; current != null; current = current.m_next)
+ for (Node current = buckets[i]; current != null; current = current._next)
{
- array[index] = new KeyValuePair<TKey, TValue>(current.m_key, current.m_value);
+ array[index] = new KeyValuePair<TKey, TValue>(current._key, current._value);
index++; //this should never flow, CopyToObjects is only called when there's no overflow risk
}
}
@@ -516,17 +505,18 @@ namespace System.Collections.Concurrent
/// </remarks>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
- Node[] buckets = m_tables.m_buckets;
+ Node[] buckets = _tables._buckets;
for (int i = 0; i < buckets.Length; i++)
{
- // The Volatile.Read ensures that the load of the fields of 'current' doesn't move before the load from buckets[i].
+ // The Volatile.Read ensures that we have a copy of the reference to buckets[i].
+ // This protects us from reading fields ('_key', '_value' and '_next') of different instances.
Node current = Volatile.Read<Node>(ref buckets[i]);
while (current != null)
{
- yield return new KeyValuePair<TKey, TValue>(current.m_key, current.m_value);
- current = current.m_next;
+ yield return new KeyValuePair<TKey, TValue>(current._key, current._value);
+ current = current._next;
}
}
}
@@ -537,39 +527,37 @@ namespace System.Collections.Concurrent
/// If key doesn't exist, we always add value and return true;
/// </summary>
[SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
- private bool TryAddInternal(TKey key, TValue value, bool updateIfExists, bool acquireLock, out TValue resultingValue)
+ private bool TryAddInternal(TKey key, int hashcode, TValue value, bool updateIfExists, bool acquireLock, out TValue resultingValue)
{
+ Debug.Assert(_comparer.GetHashCode(key) == hashcode);
+
while (true)
{
int bucketNo, lockNo;
- int hashcode;
- Tables tables = m_tables;
- IEqualityComparer<TKey> comparer = tables.m_comparer;
- hashcode = comparer.GetHashCode(key);
- GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, tables.m_buckets.Length, tables.m_locks.Length);
+ Tables tables = _tables;
+ GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, tables._buckets.Length, tables._locks.Length);
bool resizeDesired = false;
bool lockTaken = false;
-
try
{
if (acquireLock)
- Monitor.Enter(tables.m_locks[lockNo], ref lockTaken);
+ Monitor.Enter(tables._locks[lockNo], ref lockTaken);
// If the table just got resized, we may not be holding the right lock, and must retry.
- // This should be a rare occurence.
- if (tables != m_tables)
+ // This should be a rare occurrence.
+ if (tables != _tables)
{
continue;
}
// Try to find this key in the bucket
Node prev = null;
- for (Node node = tables.m_buckets[bucketNo]; node != null; node = node.m_next)
+ for (Node node = tables._buckets[bucketNo]; node != null; node = node._next)
{
- Assert((prev == null && node == tables.m_buckets[bucketNo]) || prev.m_next == node);
- if (comparer.Equals(node.m_key, key))
+ Debug.Assert((prev == null && node == tables._buckets[bucketNo]) || prev._next == node);
+ if (hashcode == node._hashcode && _comparer.Equals(node._key, key))
{
// The key was found in the dictionary. If updates are allowed, update the value for that key.
// We need to create a new node for the update, in order to support TValue types that cannot
@@ -578,25 +566,25 @@ namespace System.Collections.Concurrent
{
if (s_isValueWriteAtomic)
{
- node.m_value = value;
+ node._value = value;
}
else
{
- Node newNode = new Node(node.m_key, value, hashcode, node.m_next);
+ Node newNode = new Node(node._key, value, hashcode, node._next);
if (prev == null)
{
- tables.m_buckets[bucketNo] = newNode;
+ Volatile.Write(ref tables._buckets[bucketNo], newNode);
}
else
{
- prev.m_next = newNode;
+ prev._next = newNode;
}
}
resultingValue = value;
}
else
{
- resultingValue = node.m_value;
+ resultingValue = node._value;
}
return false;
}
@@ -604,10 +592,10 @@ namespace System.Collections.Concurrent
}
// The key was not found in the bucket. Insert the key-value pair.
- Volatile.Write<Node>(ref tables.m_buckets[bucketNo], new Node(key, value, hashcode, tables.m_buckets[bucketNo]));
+ Volatile.Write<Node>(ref tables._buckets[bucketNo], new Node(key, value, hashcode, tables._buckets[bucketNo]));
checked
{
- tables.m_countPerLock[lockNo]++;
+ tables._countPerLock[lockNo]++;
}
//
@@ -615,7 +603,7 @@ namespace System.Collections.Concurrent
// It is also possible that GrowTable will increase the budget but won't resize the bucket table.
// That happens if the bucket table is found to be poorly utilized due to a bad hash function.
//
- if (tables.m_countPerLock[lockNo] > m_budget)
+ if (tables._countPerLock[lockNo] > _budget)
{
resizeDesired = true;
}
@@ -623,7 +611,7 @@ namespace System.Collections.Concurrent
finally
{
if (lockTaken)
- Monitor.Exit(tables.m_locks[lockNo]);
+ Monitor.Exit(tables._locks[lockNo]);
}
//
@@ -636,11 +624,7 @@ namespace System.Collections.Concurrent
//
if (resizeDesired)
{
-#if FEATURE_RANDOMIZED_STRING_HASHING
- GrowTable(tables, tables.m_comparer, false, m_keyRehashCount);
-#else
- GrowTable(tables, tables.m_comparer, false, m_keyRehashCount);
-#endif
+ GrowTable(tables);
}
resultingValue = value;
@@ -654,7 +638,7 @@ namespace System.Collections.Concurrent
/// <param name="key">The key of the value to get or set.</param>
/// <value>The value associated with the specified key. If the specified key is not found, a get
/// operation throws a
- /// <see cref="T:Sytem.Collections.Generic.KeyNotFoundException"/>, and a set operation creates a new
+ /// <see cref="T:System.Collections.Generic.KeyNotFoundException"/>, and a set operation creates a new
/// element with the specified key.</value>
/// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
/// (Nothing in Visual Basic).</exception>
@@ -668,25 +652,41 @@ namespace System.Collections.Concurrent
TValue value;
if (!TryGetValue(key, out value))
{
- ThrowHelper.ThrowKeyNotFoundException();
+ ThrowKeyNotFoundException();
}
return value;
}
set
{
- if (key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ if (key == null) ThrowKeyNullException();
TValue dummy;
- TryAddInternal(key, value, true, true, out dummy);
+ TryAddInternal(key, _comparer.GetHashCode(key), value, true, true, out dummy);
}
}
+ // These exception throwing sites have been extracted into their own NoInlining methods
+ // as these are uncommonly needed and when inlined are observed to prevent the inlining
+ // of important methods like TryGetValue and ContainsKey.
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowKeyNotFoundException()
+ {
+ throw new KeyNotFoundException();
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowKeyNullException()
+ {
+ throw new ArgumentNullException("key");
+ }
+
/// <summary>
/// Gets the number of key/value pairs contained in the <see
/// cref="ConcurrentDictionary{TKey,TValue}"/>.
/// </summary>
/// <exception cref="T:System.OverflowException">The dictionary contains too many
/// elements.</exception>
- /// <value>The number of key/value paris contained in the <see
+ /// <value>The number of key/value pairs contained in the <see
/// cref="ConcurrentDictionary{TKey,TValue}"/>.</value>
/// <remarks>Count has snapshot semantics and represents the number of items in the <see
/// cref="ConcurrentDictionary{TKey,TValue}"/>
@@ -696,31 +696,46 @@ namespace System.Collections.Concurrent
[SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "ConcurrencyCop just doesn't know about these locks")]
get
{
- int count = 0;
-
int acquiredLocks = 0;
try
{
// Acquire all locks
AcquireAllLocks(ref acquiredLocks);
- // Compute the count, we allow overflow
- for (int i = 0; i < m_tables.m_countPerLock.Length; i++)
- {
- count += m_tables.m_countPerLock[i];
- }
+ return GetCountInternal();
}
finally
{
// Release locks that have been acquired earlier
ReleaseLocks(0, acquiredLocks);
}
-
- return count;
}
}
+ /// <summary>
+ /// Gets the number of key/value pairs contained in the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>. Should only be used after all locks
+ /// have been acquired.
+ /// </summary>
+ /// <exception cref="T:System.OverflowException">The dictionary contains too many
+ /// elements.</exception>
+ /// <value>The number of key/value pairs contained in the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>.</value>
+ /// <remarks>Count has snapshot semantics and represents the number of items in the <see
+ /// cref="ConcurrentDictionary{TKey,TValue}"/>
+ /// at the moment when Count was accessed.</remarks>
+ private int GetCountInternal()
+ {
+ int count = 0;
+
+ // Compute the count, we allow overflow
+ for (int i = 0; i < _tables._countPerLock.Length; i++)
+ {
+ count += _tables._countPerLock[i];
+ }
+ return count;
+ }
/// <summary>
/// Gets a value that indicates whether the <see cref="ConcurrentDictionary{TKey,TValue}"/> is empty.
@@ -738,9 +753,9 @@ namespace System.Collections.Concurrent
// Acquire all locks
AcquireAllLocks(ref acquiredLocks);
- for (int i = 0; i < m_tables.m_countPerLock.Length; i++)
+ for (int i = 0; i < _tables._countPerLock.Length; i++)
{
- if (m_tables.m_countPerLock[i] != 0)
+ if (_tables._countPerLock[i] != 0)
{
return false;
}
@@ -775,7 +790,7 @@ namespace System.Collections.Concurrent
{
if (!TryAdd(key, value))
{
- ThrowHelper.ThrowArgumentException(ExceptionResource.ConcurrentDictionary_KeyAlreadyExisted);
+ ThrowHelper.ThrowArgumentException(ExceptionResource.ConcurrentDictionary_KeyAlreadyExisted, ExceptionArgument.key);
}
}
@@ -908,7 +923,8 @@ namespace System.Collections.Concurrent
/// name="keyValuePair"/> is a null reference (Nothing in Visual Basic).</exception>
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> keyValuePair)
{
- if (keyValuePair.Key == null) ThrowHelper.ThrowArgumentNullException(ExceptionResource.ConcurrentDictionary_ItemKeyIsNull);
+ if (keyValuePair.Key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keyValuePair, ExceptionResource.ConcurrentDictionary_ItemKeyIsNull);
+
TValue throwAwayValue;
return TryRemoveInternal(keyValuePair.Key, out throwAwayValue, true, keyValuePair.Value);
}
@@ -954,17 +970,18 @@ namespace System.Collections.Concurrent
/// </exception>
void IDictionary.Add(object key, object value)
{
- if (key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
- if (!(key is TKey)) ThrowHelper.ThrowArgumentException(ExceptionResource.ConcurrentDictionary_TypeOfKeyIncorrect);
+ if (key == null) ThrowKeyNullException();
+ if (!(key is TKey)) ThrowHelper.ThrowArgumentException(ExceptionResource.ConcurrentDictionary_TypeOfKeyIncorrect, ExceptionArgument.key);
- TValue typedValue = default(TValue);
+ TValue typedValue;
try
{
typedValue = (TValue)value;
}
catch (InvalidCastException)
{
- ThrowHelper.ThrowArgumentException(ExceptionResource.ConcurrentDictionary_TypeOfValueIncorrect);
+ ThrowHelper.ThrowArgumentException(ExceptionResource.ConcurrentDictionary_TypeOfValueIncorrect, ExceptionArgument.value);
+ return;
}
((IDictionary<TKey, TValue>)this).Add((TKey)key, typedValue);
@@ -982,9 +999,9 @@ namespace System.Collections.Concurrent
/// (Nothing in Visual Basic).</exception>
bool IDictionary.Contains(object key)
{
- if (key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ if (key == null) ThrowKeyNullException();
- return (key is TKey) && ((ConcurrentDictionary<TKey, TValue>)this).ContainsKey((TKey)key);
+ return (key is TKey) && this.ContainsKey((TKey)key);
}
/// <summary>Provides an <see cref="T:System.Collections.Generics.IDictionaryEnumerator"/> for the
@@ -1042,12 +1059,12 @@ namespace System.Collections.Concurrent
/// (Nothing in Visual Basic).</exception>
void IDictionary.Remove(object key)
{
- if (key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ if (key == null) ThrowKeyNullException();
TValue throwAwayValue;
if (key is TKey)
{
- this.TryRemove((TKey)key, out throwAwayValue);
+ TryRemove((TKey)key, out throwAwayValue);
}
}
@@ -1084,10 +1101,10 @@ namespace System.Collections.Concurrent
{
get
{
- if (key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ if (key == null) ThrowKeyNullException();
TValue value;
- if (key is TKey && this.TryGetValue((TKey)key, out value))
+ if (key is TKey && TryGetValue((TKey)key, out value))
{
return value;
}
@@ -1096,10 +1113,10 @@ namespace System.Collections.Concurrent
}
set
{
- if (key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
+ if (key == null) ThrowKeyNullException();
- if (!(key is TKey)) ThrowHelper.ThrowArgumentException(ExceptionResource.ConcurrentDictionary_TypeOfKeyIncorrect);
- if (!(value is TValue)) ThrowHelper.ThrowArgumentException(ExceptionResource.ConcurrentDictionary_TypeOfValueIncorrect);
+ if (!(key is TKey)) ThrowHelper.ThrowArgumentException(ExceptionResource.ConcurrentDictionary_TypeOfKeyIncorrect, ExceptionArgument.key);
+ if (!(value is TValue)) ThrowHelper.ThrowArgumentException(ExceptionResource.ConcurrentDictionary_TypeOfValueIncorrect, ExceptionArgument.value);
((ConcurrentDictionary<TKey, TValue>)this)[(TKey)key] = (TValue)value;
}
@@ -1137,13 +1154,13 @@ namespace System.Collections.Concurrent
try
{
AcquireAllLocks(ref locksAcquired);
- Tables tables = m_tables;
+ Tables tables = _tables;
int count = 0;
- for (int i = 0; i < tables.m_locks.Length && count >= 0; i++)
+ for (int i = 0; i < tables._locks.Length && count >= 0; i++)
{
- count += tables.m_countPerLock[i];
+ count += tables._countPerLock[i];
}
if (array.Length - count < index || count < 0) //"count" itself or "count + index" can overflow
@@ -1217,62 +1234,49 @@ namespace System.Collections.Concurrent
/// <summary>
/// Replaces the bucket table with a larger one. To prevent multiple threads from resizing the
- /// table as a result of a race condition, the Tables instance that holds the table of buckets deemed too
+ /// table as a result of races, the Tables instance that holds the table of buckets deemed too
/// small is passed in as an argument to GrowTable(). GrowTable() obtains a lock, and then checks
- /// the Tables instance has been replaced in the meantime or not.
- /// The <paramref name="rehashCount"/> will be used to ensure that we don't do two subsequent resizes
- /// because of a collision
+ /// the Tables instance has been replaced in the meantime or not.
/// </summary>
- private void GrowTable(Tables tables, IEqualityComparer<TKey> newComparer, bool regenerateHashKeys, int rehashCount)
+ private void GrowTable(Tables tables)
{
+ const int MaxArrayLength = 0X7FEFFFFF;
int locksAcquired = 0;
try
{
- // The thread that first obtains m_locks[0] will be the one doing the resize operation
+ // The thread that first obtains _locks[0] will be the one doing the resize operation
AcquireLocks(0, 1, ref locksAcquired);
- if (regenerateHashKeys && rehashCount == m_keyRehashCount)
+ // Make sure nobody resized the table while we were waiting for lock 0:
+ if (tables != _tables)
{
- // This method is called with regenerateHashKeys==true when we detected
- // more than HashHelpers.HashCollisionThreshold collisions when adding a new element.
- // In that case we are in the process of switching to another (randomized) comparer
- // and we have to re-hash all the keys in the table.
- // We are only going to do this if we did not just rehash the entire table while waiting for the lock
- tables = m_tables;
+ // We assume that since the table reference is different, it was already resized (or the budget
+ // was adjusted). If we ever decide to do table shrinking, or replace the table for other reasons,
+ // we will have to revisit this logic.
+ return;
}
- else
+
+ // Compute the (approx.) total size. Use an Int64 accumulation variable to avoid an overflow.
+ long approxCount = 0;
+ for (int i = 0; i < tables._countPerLock.Length; i++)
{
- // If we don't require a regeneration of hash keys we want to make sure we don't do work when
- // we don't have to
- if (tables != m_tables)
- {
- // We assume that since the table reference is different, it was already resized (or the budget
- // was adjusted). If we ever decide to do table shrinking, or replace the table for other reasons,
- // we will have to revisit this logic.
- return;
- }
+ approxCount += tables._countPerLock[i];
+ }
- // Compute the (approx.) total size. Use an Int64 accumulation variable to avoid an overflow.
- long approxCount = 0;
- for (int i = 0; i < tables.m_countPerLock.Length; i++)
+ //
+ // If the bucket array is too empty, double the budget instead of resizing the table
+ //
+ if (approxCount < tables._buckets.Length / 4)
+ {
+ _budget = 2 * _budget;
+ if (_budget < 0)
{
- approxCount += tables.m_countPerLock[i];
+ _budget = int.MaxValue;
}
+ return;
+ }
- //
- // If the bucket array is too empty, double the budget instead of resizing the table
- //
- if (approxCount < tables.m_buckets.Length / 4)
- {
- m_budget = 2 * m_budget;
- if (m_budget < 0)
- {
- m_budget = int.MaxValue;
- }
- return;
- }
- }
// Compute the new table size. We find the smallest integer larger than twice the previous table size, and not divisible by
// 2,3,5 or 7. We can consider a different table-sizing policy in the future.
int newLength = 0;
@@ -1282,7 +1286,7 @@ namespace System.Collections.Concurrent
checked
{
// Double the size of the buckets table and add one, so that we have an odd integer.
- newLength = tables.m_buckets.Length * 2 + 1;
+ newLength = tables._buckets.Length * 2 + 1;
// Now, we only need to check odd integers, and find the first that is not divisible
// by 3, 5 or 7.
@@ -1291,9 +1295,9 @@ namespace System.Collections.Concurrent
newLength += 2;
}
- Assert(newLength % 2 != 0);
+ Debug.Assert(newLength % 2 != 0);
- if (newLength > Array.MaxArrayLength)
+ if (newLength > MaxArrayLength)
{
maximizeTableSize = true;
}
@@ -1306,28 +1310,27 @@ namespace System.Collections.Concurrent
if (maximizeTableSize)
{
- newLength = Array.MaxArrayLength;
+ newLength = MaxArrayLength;
// We want to make sure that GrowTable will not be called again, since table is at the maximum size.
// To achieve that, we set the budget to int.MaxValue.
//
// (There is one special case that would allow GrowTable() to be called in the future:
// calling Clear() on the ConcurrentDictionary will shrink the table and lower the budget.)
- m_budget = int.MaxValue;
+ _budget = int.MaxValue;
}
// Now acquire all other locks for the table
- AcquireLocks(1, tables.m_locks.Length, ref locksAcquired);
+ AcquireLocks(1, tables._locks.Length, ref locksAcquired);
- object[] newLocks = tables.m_locks;
+ object[] newLocks = tables._locks;
// Add more locks
- if (m_growLockArray && tables.m_locks.Length < MAX_LOCK_NUMBER)
+ if (_growLockArray && tables._locks.Length < MaxLockNumber)
{
- newLocks = new object[tables.m_locks.Length * 2];
- Array.Copy(tables.m_locks, newLocks, tables.m_locks.Length);
-
- for (int i = tables.m_locks.Length; i < newLocks.Length; i++)
+ newLocks = new object[tables._locks.Length * 2];
+ Array.Copy(tables._locks, 0, newLocks, 0, tables._locks.Length);
+ for (int i = tables._locks.Length; i < newLocks.Length; i++)
{
newLocks[i] = new object();
}
@@ -1337,24 +1340,16 @@ namespace System.Collections.Concurrent
int[] newCountPerLock = new int[newLocks.Length];
// Copy all data into a new table, creating new nodes for all elements
- for (int i = 0; i < tables.m_buckets.Length; i++)
+ for (int i = 0; i < tables._buckets.Length; i++)
{
- Node current = tables.m_buckets[i];
+ Node current = tables._buckets[i];
while (current != null)
{
- Node next = current.m_next;
+ Node next = current._next;
int newBucketNo, newLockNo;
- int nodeHashCode = current.m_hashcode;
-
- if (regenerateHashKeys)
- {
- // Recompute the hash from the key
- nodeHashCode = newComparer.GetHashCode(current.m_key);
- }
-
- GetBucketAndLockNo(nodeHashCode, out newBucketNo, out newLockNo, newBuckets.Length, newLocks.Length);
+ GetBucketAndLockNo(current._hashcode, out newBucketNo, out newLockNo, newBuckets.Length, newLocks.Length);
- newBuckets[newBucketNo] = new Node(current.m_key, current.m_value, nodeHashCode, newBuckets[newBucketNo]);
+ newBuckets[newBucketNo] = new Node(current._key, current._value, current._hashcode, newBuckets[newBucketNo]);
checked
{
@@ -1365,22 +1360,11 @@ namespace System.Collections.Concurrent
}
}
- // If this resize regenerated the hashkeys, increment the count
- if (regenerateHashKeys)
- {
- // We use unchecked here because we don't want to throw an exception if
- // an overflow happens
- unchecked
- {
- m_keyRehashCount++;
- }
- }
-
// Adjust the budget
- m_budget = Math.Max(1, newBuckets.Length / newLocks.Length);
+ _budget = Math.Max(1, newBuckets.Length / newLocks.Length);
// Replace tables with the new versions
- m_tables = new Tables(newBuckets, newLocks, newCountPerLock, newComparer);
+ _tables = new Tables(newBuckets, newLocks, newCountPerLock);
}
finally
{
@@ -1390,16 +1374,25 @@ namespace System.Collections.Concurrent
}
/// <summary>
+ /// Computes the bucket for a particular key.
+ /// </summary>
+ private static int GetBucket(int hashcode, int bucketCount)
+ {
+ int bucketNo = (hashcode & 0x7fffffff) % bucketCount;
+ Debug.Assert(bucketNo >= 0 && bucketNo < bucketCount);
+ return bucketNo;
+ }
+
+ /// <summary>
/// Computes the bucket and lock number for a particular key.
/// </summary>
- private void GetBucketAndLockNo(
- int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount)
+ private static void GetBucketAndLockNo(int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount)
{
bucketNo = (hashcode & 0x7fffffff) % bucketCount;
lockNo = bucketNo % lockCount;
- Assert(bucketNo >= 0 && bucketNo < bucketCount);
- Assert(lockNo >= 0 && lockNo < lockCount);
+ Debug.Assert(bucketNo >= 0 && bucketNo < bucketCount);
+ Debug.Assert(lockNo >= 0 && lockNo < lockCount);
}
/// <summary>
@@ -1407,7 +1400,7 @@ namespace System.Collections.Concurrent
/// </summary>
private static int DefaultConcurrencyLevel
{
- get { return DEFAULT_CONCURRENCY_MULTIPLIER * PlatformHelper.ProcessorCount; }
+ get { return PlatformHelper.ProcessorCount; }
}
/// <summary>
@@ -1420,10 +1413,10 @@ namespace System.Collections.Concurrent
// First, acquire lock 0
AcquireLocks(0, 1, ref locksAcquired);
- // Now that we have lock 0, the m_locks array will not change (i.e., grow),
- // and so we can safely read m_locks.Length.
- AcquireLocks(1, m_tables.m_locks.Length, ref locksAcquired);
- Assert(locksAcquired == m_tables.m_locks.Length);
+ // Now that we have lock 0, the _locks array will not change (i.e., grow),
+ // and so we can safely read _locks.Length.
+ AcquireLocks(1, _tables._locks.Length, ref locksAcquired);
+ Debug.Assert(locksAcquired == _tables._locks.Length);
}
/// <summary>
@@ -1433,8 +1426,8 @@ namespace System.Collections.Concurrent
/// </summary>
private void AcquireLocks(int fromInclusive, int toExclusive, ref int locksAcquired)
{
- Assert(fromInclusive <= toExclusive);
- object[] locks = m_tables.m_locks;
+ Debug.Assert(fromInclusive <= toExclusive);
+ object[] locks = _tables._locks;
for (int i = fromInclusive; i < toExclusive; i++)
{
@@ -1459,11 +1452,11 @@ namespace System.Collections.Concurrent
[SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
private void ReleaseLocks(int fromInclusive, int toExclusive)
{
- Assert(fromInclusive <= toExclusive);
+ Debug.Assert(fromInclusive <= toExclusive);
for (int i = fromInclusive; i < toExclusive; i++)
{
- Monitor.Exit(m_tables.m_locks[i]);
+ Monitor.Exit(_tables._locks[i]);
}
}
@@ -1477,15 +1470,18 @@ namespace System.Collections.Concurrent
try
{
AcquireAllLocks(ref locksAcquired);
- List<TKey> keys = new List<TKey>();
- for (int i = 0; i < m_tables.m_buckets.Length; i++)
+ int count = GetCountInternal();
+ if (count < 0) ThrowHelper.ThrowOutOfMemoryException();
+
+ List<TKey> keys = new List<TKey>(count);
+ for (int i = 0; i < _tables._buckets.Length; i++)
{
- Node current = m_tables.m_buckets[i];
+ Node current = _tables._buckets[i];
while (current != null)
{
- keys.Add(current.m_key);
- current = current.m_next;
+ keys.Add(current._key);
+ current = current._next;
}
}
@@ -1507,15 +1503,18 @@ namespace System.Collections.Concurrent
try
{
AcquireAllLocks(ref locksAcquired);
- List<TValue> values = new List<TValue>();
- for (int i = 0; i < m_tables.m_buckets.Length; i++)
+ int count = GetCountInternal();
+ if (count < 0) ThrowHelper.ThrowOutOfMemoryException();
+
+ List<TValue> values = new List<TValue>(count);
+ for (int i = 0; i < _tables._buckets.Length; i++)
{
- Node current = m_tables.m_buckets[i];
+ Node current = _tables._buckets[i];
while (current != null)
{
- values.Add(current.m_value);
- current = current.m_next;
+ values.Add(current._value);
+ current = current._next;
}
}
@@ -1528,30 +1527,21 @@ namespace System.Collections.Concurrent
}
/// <summary>
- /// A helper method for asserts.
- /// </summary>
- [Conditional("DEBUG")]
- private void Assert(bool condition)
- {
- Debug.Assert(condition);
- }
-
- /// <summary>
/// A node in a singly-linked list representing a particular hash table bucket.
/// </summary>
- private class Node
+ private sealed class Node
{
- internal TKey m_key;
- internal TValue m_value;
- internal volatile Node m_next;
- internal int m_hashcode;
+ internal readonly TKey _key;
+ internal TValue _value;
+ internal volatile Node _next;
+ internal readonly int _hashcode;
internal Node(TKey key, TValue value, int hashcode, Node next)
{
- m_key = key;
- m_value = value;
- m_next = next;
- m_hashcode = hashcode;
+ _key = key;
+ _value = value;
+ _next = next;
+ _hashcode = hashcode;
}
}
@@ -1559,43 +1549,43 @@ namespace System.Collections.Concurrent
/// A private class to represent enumeration over the dictionary that implements the
/// IDictionaryEnumerator interface.
/// </summary>
- private class DictionaryEnumerator : IDictionaryEnumerator
+ private sealed class DictionaryEnumerator : IDictionaryEnumerator
{
- private IEnumerator<KeyValuePair<TKey, TValue>> m_enumerator; // Enumerator over the dictionary.
+ IEnumerator<KeyValuePair<TKey, TValue>> _enumerator; // Enumerator over the dictionary.
internal DictionaryEnumerator(ConcurrentDictionary<TKey, TValue> dictionary)
{
- m_enumerator = dictionary.GetEnumerator();
+ _enumerator = dictionary.GetEnumerator();
}
public DictionaryEntry Entry
{
- get { return new DictionaryEntry(m_enumerator.Current.Key, m_enumerator.Current.Value); }
+ get { return new DictionaryEntry(_enumerator.Current.Key, _enumerator.Current.Value); }
}
public object Key
{
- get { return m_enumerator.Current.Key; }
+ get { return _enumerator.Current.Key; }
}
public object Value
{
- get { return m_enumerator.Current.Value; }
+ get { return _enumerator.Current.Value; }
}
public object Current
{
- get { return this.Entry; }
+ get { return Entry; }
}
public bool MoveNext()
{
- return m_enumerator.MoveNext();
+ return _enumerator.MoveNext();
}
public void Reset()
{
- m_enumerator.Reset();
+ _enumerator.Reset();
}
}
}
diff --git a/src/mscorlib/src/System/Collections/Concurrent/ConcurrentQueue.cs b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentQueue.cs
index 632f0eecef..304bcd6b79 100644
--- a/src/mscorlib/src/System/Collections/Concurrent/ConcurrentQueue.cs
+++ b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentQueue.cs
@@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
-using System.Runtime.Serialization;
using System.Threading;
namespace System.Collections.Concurrent
@@ -53,16 +52,11 @@ namespace System.Collections.Concurrent
/// Lock used to protect cross-segment operations, including any updates to <see cref="_tail"/> or <see cref="_head"/>
/// and any operations that need to get a consistent view of them.
/// </summary>
- [NonSerialized]
private object _crossSegmentLock;
/// <summary>The current tail segment.</summary>
- [NonSerialized]
private volatile Segment _tail;
/// <summary>The current head segment.</summary>
- [NonSerialized]
private volatile Segment _head;
- /// <summary>Field used to temporarily store the contents of the queue for serialization.</summary>
- private T[] _serializationArray;
/// <summary>
/// Initializes a new instance of the <see cref="ConcurrentQueue{T}"/> class.
@@ -73,29 +67,6 @@ namespace System.Collections.Concurrent
_tail = _head = new Segment(InitialSegmentLength);
}
- /// <summary>Set the data array to be serialized.</summary>
- [OnSerializing]
- private void OnSerializing(StreamingContext context)
- {
- _serializationArray = ToArray();
- }
-
- /// <summary>Clear the data array that was serialized.</summary>
- [OnSerialized]
- private void OnSerialized(StreamingContext context)
- {
- _serializationArray = null;
- }
-
- /// <summary>Construct the queue from the deserialized <see cref="_serializationArray"/>.</summary>
- [OnDeserialized]
- private void OnDeserialized(StreamingContext context)
- {
- Debug.Assert(_serializationArray != null);
- InitializeFromCollection(_serializationArray);
- _serializationArray = null;
- }
-
/// <summary>
/// Initializes the contents of the queue from an existing collection.
/// </summary>
@@ -139,7 +110,7 @@ namespace System.Collections.Concurrent
{
if (collection == null)
{
- throw new ArgumentNullException(nameof(collection));
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
}
InitializeFromCollection(collection);
@@ -182,7 +153,7 @@ namespace System.Collections.Concurrent
// Validate arguments.
if (array == null)
{
- throw new ArgumentNullException(nameof(array));
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
// Otherwise, fall back to the slower path that first copies the contents
@@ -204,7 +175,7 @@ namespace System.Collections.Concurrent
/// cref="ICollection"/>. This property is not supported.
/// </summary>
/// <exception cref="NotSupportedException">The SyncRoot property is not supported.</exception>
- object ICollection.SyncRoot { get { throw new NotSupportedException(SR.ConcurrentCollection_SyncRoot_NotSupported); } }
+ object ICollection.SyncRoot { get { ThrowHelper.ThrowNotSupportedException(ExceptionResource.ConcurrentCollection_SyncRoot_NotSupported); return default(object); } }
/// <summary>Returns an enumerator that iterates through a collection.</summary>
/// <returns>An <see cref="IEnumerator"/> that can be used to iterate through the collection.</returns>
@@ -426,11 +397,11 @@ namespace System.Collections.Concurrent
{
if (array == null)
{
- throw new ArgumentNullException(nameof(array));
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
if (index < 0)
{
- throw new ArgumentOutOfRangeException(nameof(index));
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);
}
// Snap for enumeration
@@ -442,7 +413,7 @@ namespace System.Collections.Concurrent
long count = GetCount(head, headHead, tail, tailTail);
if (index > array.Length - count)
{
- throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall);
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
}
// Copy the items to the target array
@@ -737,7 +708,7 @@ namespace System.Collections.Concurrent
/// <summary>Attempts to retrieve the value for the first element in the queue.</summary>
/// <param name="result">The value of the first element, if found.</param>
- /// <param name="resultUsed">true if the result is neede; otherwise false if only the true/false outcome is needed.</param>
+ /// <param name="resultUsed">true if the result is needed; otherwise false if only the true/false outcome is needed.</param>
/// <returns>true if an element was found; otherwise, false.</returns>
private bool TryPeek(out T result, bool resultUsed)
{
@@ -826,7 +797,7 @@ namespace System.Collections.Concurrent
/// <summary>Mask for quickly accessing a position within the queue's array.</summary>
internal readonly int _slotsMask;
/// <summary>The head and tail positions, with padding to help avoid false sharing contention.</summary>
- /// <remarks>Dequeueing happens from the head, enqueueing happens at the tail.</remarks>
+ /// <remarks>Dequeuing happens from the head, enqueuing happens at the tail.</remarks>
internal PaddedHeadAndTail _headAndTail; // mutable struct: do not make this readonly
/// <summary>Indicates whether the segment has been marked such that dequeues don't overwrite the removed data.</summary>
@@ -855,7 +826,7 @@ namespace System.Collections.Concurrent
// allows dequeuers to know whether they can dequeue and enqueuers to know whether they can
// enqueue. An enqueuer at position N can enqueue when the sequence number is N, and a dequeuer
// for position N can dequeue when the sequence number is N + 1. When an enqueuer is done writing
- // at position N, it sets the sequence number to N so that a dequeuer will be able to dequeue,
+ // at position N, it sets the sequence number to N + 1 so that a dequeuer will be able to dequeue,
// and when a dequeuer is done dequeueing at position N, it sets the sequence number to N + _slots.Length,
// so that when an enqueuer loops around the slots, it'll find that the sequence number at
// position N is N. This also means that when an enqueuer finds that at position N the sequence
@@ -1111,10 +1082,10 @@ namespace System.Collections.Concurrent
/// <summary>Padded head and tail indices, to avoid false sharing between producers and consumers.</summary>
[DebuggerDisplay("Head = {Head}, Tail = {Tail}")]
- [StructLayout(LayoutKind.Explicit, Size = 192)] // padding before/between/after fields based on typical cache line size of 64
+ [StructLayout(LayoutKind.Explicit, Size = 3*Internal.PaddingHelpers.CACHE_LINE_SIZE)] // padding before/between/after fields
internal struct PaddedHeadAndTail
{
- [FieldOffset(64)] public int Head;
- [FieldOffset(128)] public int Tail;
+ [FieldOffset(1*Internal.PaddingHelpers.CACHE_LINE_SIZE)] public int Head;
+ [FieldOffset(2*Internal.PaddingHelpers.CACHE_LINE_SIZE)] public int Tail;
}
}
diff --git a/src/mscorlib/src/System/Collections/Concurrent/ConcurrentStack.cs b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentStack.cs
index 82bc4f9f5c..d1c2d42dce 100644
--- a/src/mscorlib/src/System/Collections/Concurrent/ConcurrentStack.cs
+++ b/src/mscorlib/src/System/Collections/Concurrent/ConcurrentStack.cs
@@ -1,25 +1,17 @@
// 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 warning disable 0420
-
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
-//
+// ConcurrentStack.cs
//
// A lock-free, concurrent stack primitive, and its associated debugger view type.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-using System;
-using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Diagnostics.Contracts;
-using System.Runtime.ConstrainedExecution;
-using System.Runtime.Serialization;
-using System.Security;
using System.Threading;
namespace System.Collections.Concurrent
@@ -51,8 +43,8 @@ namespace System.Collections.Concurrent
/// </summary>
private class Node
{
- internal readonly T m_value; // Value of the node.
- internal Node m_next; // Next pointer.
+ internal readonly T _value; // Value of the node.
+ internal Node _next; // Next pointer.
/// <summary>
/// Constructs a new node with the specified value and no next node.
@@ -60,13 +52,12 @@ namespace System.Collections.Concurrent
/// <param name="value">The value of the node.</param>
internal Node(T value)
{
- m_value = value;
- m_next = null;
+ _value = value;
+ _next = null;
}
}
- private volatile Node m_head; // The stack is a singly linked list, and only remembers the head.
-
+ private volatile Node _head; // The stack is a singly linked list, and only remembers the head.
private const int BACKOFF_MAX_YIELDS = 8; // Arbitrary number to cap backoff.
/// <summary>
@@ -101,7 +92,7 @@ namespace System.Collections.Concurrent
// they are being dequeued. If we ever changed this (e.g. to pool nodes somehow),
// we'd need to revisit this implementation.
- for (Node curr = m_head; curr != null; curr = curr.m_next)
+ for (Node curr = _head; curr != null; curr = curr._next)
{
count++; //we don't handle overflow, to be consistent with existing generic collection types in CLR
}
@@ -110,6 +101,7 @@ namespace System.Collections.Concurrent
}
}
+
/// <summary>
/// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"/> is
/// synchronized with the SyncRoot.
@@ -135,7 +127,8 @@ namespace System.Collections.Concurrent
{
get
{
- throw new NotSupportedException(SR.ConcurrentCollection_SyncRoot_NotSupported);
+ ThrowHelper.ThrowNotSupportedException(ExceptionResource.ConcurrentCollection_SyncRoot_NotSupported);
+ return default(object);
}
}
@@ -169,7 +162,7 @@ namespace System.Collections.Concurrent
// Validate arguments.
if (array == null)
{
- throw new ArgumentNullException(nameof(array));
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
// We must be careful not to corrupt the array, so we will first accumulate an
@@ -203,7 +196,7 @@ namespace System.Collections.Concurrent
{
if (array == null)
{
- throw new ArgumentNullException(nameof(array));
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
// We must be careful not to corrupt the array, so we will first accumulate an
@@ -213,7 +206,7 @@ namespace System.Collections.Concurrent
ToList().CopyTo(array, index);
}
-
+#pragma warning disable 0420 // No warning for Interlocked.xxx if compiled with new managed compiler (Roslyn)
/// <summary>
/// Inserts an object at the top of the <see cref="ConcurrentStack{T}"/>.
/// </summary>
@@ -228,8 +221,8 @@ namespace System.Collections.Concurrent
// contention at the head, and then go back around and retry.
Node newNode = new Node(item);
- newNode.m_next = m_head;
- if (Interlocked.CompareExchange(ref m_head, newNode, newNode.m_next) == newNode.m_next)
+ newNode._next = _head;
+ if (Interlocked.CompareExchange(ref _head, newNode, newNode._next) == newNode._next)
{
return;
}
@@ -249,15 +242,15 @@ namespace System.Collections.Concurrent
{
SpinWait spin = new SpinWait();
- // Keep trying to CAS the exising head with the new node until we succeed.
+ // Keep trying to CAS the existing head with the new node until we succeed.
do
{
spin.SpinOnce();
// Reread the head and link our new node.
- tail.m_next = m_head;
+ tail._next = _head;
}
while (Interlocked.CompareExchange(
- ref m_head, head, tail.m_next) != tail.m_next);
+ ref _head, head, tail._next) != tail._next);
}
/// <summary>
@@ -269,19 +262,19 @@ namespace System.Collections.Concurrent
/// </param>
/// <returns>true if an element was removed and returned from the top of the <see
/// cref="ConcurrentStack{T}"/>
- /// succesfully; otherwise, false.</returns>
+ /// successfully; otherwise, false.</returns>
public bool TryPop(out T result)
{
- Node head = m_head;
+ Node head = _head;
//stack is empty
if (head == null)
{
result = default(T);
return false;
}
- if (Interlocked.CompareExchange(ref m_head, head.m_next, head) == head)
+ if (Interlocked.CompareExchange(ref _head, head._next, head) == head)
{
- result = head.m_value;
+ result = head._value;
return true;
}
@@ -300,7 +293,7 @@ namespace System.Collections.Concurrent
if (TryPopCore(1, out poppedNode) == 1)
{
- result = poppedNode.m_value;
+ result = poppedNode._value;
return true;
}
@@ -317,7 +310,8 @@ namespace System.Collections.Concurrent
/// When this method returns, if the pop succeeded, contains the removed object. If no object was
/// available to be removed, the value is unspecified. This parameter is passed uninitialized.
/// </param>
- /// <returns>True if an element was removed and returned; otherwise, false.</returns>
+ /// <returns>The number of objects successfully popped from the top of
+ /// the <see cref="ConcurrentStack{T}"/>.</returns>
private int TryPopCore(int count, out Node poppedHead)
{
SpinWait spin = new SpinWait();
@@ -330,7 +324,7 @@ namespace System.Collections.Concurrent
Random r = null;
while (true)
{
- head = m_head;
+ head = _head;
// Is the stack empty?
if (head == null)
{
@@ -339,13 +333,13 @@ namespace System.Collections.Concurrent
}
next = head;
int nodesCount = 1;
- for (; nodesCount < count && next.m_next != null; nodesCount++)
+ for (; nodesCount < count && next._next != null; nodesCount++)
{
- next = next.m_next;
+ next = next._next;
}
// Try to swap the new head. If we succeed, break out of the loop.
- if (Interlocked.CompareExchange(ref m_head, next.m_next, head) == head)
+ if (Interlocked.CompareExchange(ref _head, next._next, head) == head)
{
// Return the popped Node.
poppedHead = head;
@@ -372,6 +366,7 @@ namespace System.Collections.Concurrent
}
}
}
+#pragma warning restore 0420
/// <summary>
/// Copies the items stored in the <see cref="ConcurrentStack{T}"/> to a new array.
@@ -380,23 +375,34 @@ namespace System.Collections.Concurrent
/// cref="ConcurrentStack{T}"/>.</returns>
public T[] ToArray()
{
- return ToList().ToArray();
+ Node curr = _head;
+ return curr == null ?
+ Array.Empty<T>() :
+ ToList(curr).ToArray();
}
/// <summary>
/// Returns an array containing a snapshot of the list's contents, using
/// the target list node as the head of a region in the list.
/// </summary>
- /// <returns>An array of the list's contents.</returns>
+ /// <returns>A list of the stack's contents.</returns>
private List<T> ToList()
{
+ return ToList(_head);
+ }
+
+ /// <summary>
+ /// Returns an array containing a snapshot of the list's contents starting at the specified node.
+ /// </summary>
+ /// <returns>A list of the stack's contents starting at the specified node.</returns>
+ private List<T> ToList(Node curr)
+ {
List<T> list = new List<T>();
- Node curr = m_head;
while (curr != null)
{
- list.Add(curr.m_value);
- curr = curr.m_next;
+ list.Add(curr._value);
+ curr = curr._next;
}
return list;
@@ -421,8 +427,8 @@ namespace System.Collections.Concurrent
//If we put yield-return here, the iterator will be lazily evaluated. As a result a snapshot of
//the stack is not taken when GetEnumerator is initialized but when MoveNext() is first called.
//This is inconsistent with existing generic collections. In order to prevent it, we capture the
- //value of m_head in a buffer and call out to a helper method
- return GetEnumerator(m_head);
+ //value of _head in a buffer and call out to a helper method
+ return GetEnumerator(_head);
}
private IEnumerator<T> GetEnumerator(Node head)
@@ -430,8 +436,8 @@ namespace System.Collections.Concurrent
Node current = head;
while (current != null)
{
- yield return current.m_value;
- current = current.m_next;
+ yield return current._value;
+ current = current._next;
}
}
diff --git a/src/mscorlib/src/System/Collections/Generic/Dictionary.cs b/src/mscorlib/src/System/Collections/Generic/Dictionary.cs
index 50724017dd..0a83734eb0 100644
--- a/src/mscorlib/src/System/Collections/Generic/Dictionary.cs
+++ b/src/mscorlib/src/System/Collections/Generic/Dictionary.cs
@@ -92,12 +92,10 @@ namespace System.Collections.Generic
if (capacity > 0) Initialize(capacity);
this.comparer = comparer ?? EqualityComparer<TKey>.Default;
-#if FEATURE_RANDOMIZED_STRING_HASHING
- if (HashHelpers.s_UseRandomizedStringHashing && this.comparer == EqualityComparer<string>.Default)
+ if (this.comparer == EqualityComparer<string>.Default)
{
this.comparer = (IEqualityComparer<TKey>)NonRandomizedStringEqualityComparer.Default;
}
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
}
public Dictionary(IDictionary<TKey, TValue> dictionary) : this(dictionary, null) { }
@@ -408,9 +406,7 @@ namespace System.Collections.Generic
if (buckets == null) Initialize(0);
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int targetBucket = hashCode % buckets.Length;
-#if FEATURE_RANDOMIZED_STRING_HASHING
int collisionCount = 0;
-#endif
for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next)
{
@@ -430,9 +426,8 @@ namespace System.Collections.Generic
return false;
}
-#if FEATURE_RANDOMIZED_STRING_HASHING
+
collisionCount++;
-#endif
}
int index;
if (freeCount > 0)
@@ -459,18 +454,14 @@ namespace System.Collections.Generic
buckets[targetBucket] = index;
version++;
-#if FEATURE_RANDOMIZED_STRING_HASHING
- // In case we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
- // in this case will be EqualityComparer<string>.Default.
- // Note, randomized string hashing is turned on by default on coreclr so EqualityComparer<string>.Default will
- // be using randomized string hashing
+ // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
+ // i.e. EqualityComparer<string>.Default.
if (collisionCount > HashHelpers.HashCollisionThreshold && comparer == NonRandomizedStringEqualityComparer.Default)
{
comparer = (IEqualityComparer<TKey>)EqualityComparer<string>.Default;
Resize(entries.Length, true);
}
-#endif
return true;
}
diff --git a/src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs b/src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs
index 760c9d10b2..4427682d38 100644
--- a/src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs
+++ b/src/mscorlib/src/System/Collections/Generic/EqualityComparer.cs
@@ -272,11 +272,8 @@ namespace System.Collections.Generic
GetType().GetHashCode();
}
- // NonRandomizedStringEqualityComparer is the comparer used by default with the Dictionary<string,...>
- // As the randomized string hashing is turned on by default on coreclr, we need to keep the performance not affected
- // as much as possible in the main stream scenarios like Dictionary<string,>
// We use NonRandomizedStringEqualityComparer as default comparer as it doesnt use the randomized string hashing which
- // keep the perofrmance not affected till we hit collision threshold and then we switch to the comparer which is using
+ // keeps the performance unaffected till we hit collision threshold and then we switch to the comparer which is using
// randomized string hashing GenericEqualityComparer<string>
[Serializable]
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
diff --git a/src/mscorlib/src/System/Collections/Hashtable.cs b/src/mscorlib/src/System/Collections/Hashtable.cs
index 0c89632828..fcfcc3bbd6 100644
--- a/src/mscorlib/src/System/Collections/Hashtable.cs
+++ b/src/mscorlib/src/System/Collections/Hashtable.cs
@@ -1392,10 +1392,7 @@ namespace System.Collections
[FriendAccessAllowed]
internal static class HashHelpers
{
-#if FEATURE_RANDOMIZED_STRING_HASHING
public const int HashCollisionThreshold = 100;
- public static bool s_UseRandomizedStringHashing = String.UseRandomizedHashing();
-#endif
// Table of prime numbers to use as hash table sizes.
// A typical resize algorithm would pick the smallest prime number in this array
diff --git a/src/mscorlib/src/System/Decimal.cs b/src/mscorlib/src/System/Decimal.cs
index df2d3a1c39..5bb0446784 100644
--- a/src/mscorlib/src/System/Decimal.cs
+++ b/src/mscorlib/src/System/Decimal.cs
@@ -510,34 +510,63 @@ namespace System
//
public static Decimal Parse(String s)
{
- return Number.ParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseDecimal(s.AsReadOnlySpan(), NumberStyles.Number, NumberFormatInfo.CurrentInfo);
}
public static Decimal Parse(String s, NumberStyles style)
{
NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
- return Number.ParseDecimal(s, style, NumberFormatInfo.CurrentInfo);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseDecimal(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo);
}
public static Decimal Parse(String s, IFormatProvider provider)
{
- return Number.ParseDecimal(s, NumberStyles.Number, NumberFormatInfo.GetInstance(provider));
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseDecimal(s.AsReadOnlySpan(), NumberStyles.Number, NumberFormatInfo.GetInstance(provider));
}
public static Decimal Parse(String s, NumberStyles style, IFormatProvider provider)
{
NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ return Number.ParseDecimal(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ public static decimal Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
return Number.ParseDecimal(s, style, NumberFormatInfo.GetInstance(provider));
}
public static Boolean TryParse(String s, out Decimal result)
{
- return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result);
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return Number.TryParseDecimal(s.AsReadOnlySpan(), NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result);
}
public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Decimal result)
{
NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+
+ if (s == null)
+ {
+ result = 0;
+ return false;
+ }
+
+ return Number.TryParseDecimal(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result);
+ }
+
+ public static bool TryParse(ReadOnlySpan<char> s, out decimal result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result);
}
diff --git a/src/mscorlib/src/System/Diagnostics/Contracts/Contracts.cs b/src/mscorlib/src/System/Diagnostics/Contracts/Contracts.cs
index 76b15197f2..32a417036f 100644
--- a/src/mscorlib/src/System/Diagnostics/Contracts/Contracts.cs
+++ b/src/mscorlib/src/System/Diagnostics/Contracts/Contracts.cs
@@ -877,7 +877,7 @@ namespace System.Runtime.CompilerServices
/// The method should not perform any failure (assert/throw) itself.
/// This method has 3 functions:
/// 1. Call any contract hooks (such as listeners to Contract failed events)
- /// 2. Determine if the listeneres deem the failure as handled (then resultFailureMessage should be set to null)
+ /// 2. Determine if the listeners deem the failure as handled (then resultFailureMessage should be set to null)
/// 3. Produce a localized resultFailureMessage used in advertising the failure subsequently.
/// </summary>
/// <param name="resultFailureMessage">Should really be out (or the return value), but partial methods are not flexible enough.
diff --git a/src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs b/src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs
index d29b860bd1..db6a40c54a 100644
--- a/src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs
+++ b/src/mscorlib/src/System/Diagnostics/Contracts/ContractsBCL.cs
@@ -281,7 +281,7 @@ namespace System.Runtime.CompilerServices
/// The method should not perform any failure (assert/throw) itself.
/// This method has 3 functions:
/// 1. Call any contract hooks (such as listeners to Contract failed events)
- /// 2. Determine if the listeneres deem the failure as handled (then resultFailureMessage should be set to null)
+ /// 2. Determine if the listeners deem the failure as handled (then resultFailureMessage should be set to null)
/// 3. Produce a localized resultFailureMessage used in advertising the failure subsequently.
/// </summary>
/// <param name="resultFailureMessage">Should really be out (or the return value), but partial methods are not flexible enough.
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs
index 2f6fdf62ef..d166bf8140 100644
--- a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipe.cs
@@ -158,7 +158,7 @@ namespace System.Diagnostics.Tracing
//
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
- internal static extern IntPtr CreateProvider(Guid providerID, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback callbackFunc);
+ internal static extern IntPtr CreateProvider(string providerName, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback callbackFunc);
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
@@ -171,5 +171,9 @@ namespace System.Diagnostics.Tracing
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
internal static extern unsafe void WriteEvent(IntPtr eventHandle, uint eventID, void* pData, uint length, Guid* activityId, Guid* relatedActivityId);
+
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ internal static extern unsafe void WriteEventData(IntPtr eventHandle, uint eventID, EventProvider.EventData** pEventData, uint dataCount, Guid* activityId, Guid* relatedActivityId);
}
}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs
index d5bc4c2889..a8789f5692 100644
--- a/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventPipeEventProvider.cs
@@ -18,13 +18,13 @@ namespace System.Diagnostics.Tracing
// Register an event provider.
unsafe uint IEventProvider.EventRegister(
- ref Guid providerId,
+ EventSource eventSource,
UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback,
void* callbackContext,
ref long registrationHandle)
{
uint returnStatus = 0;
- m_provHandle = EventPipeInternal.CreateProvider(providerId, enableCallback);
+ m_provHandle = EventPipeInternal.CreateProvider(eventSource.Name, enableCallback);
if(m_provHandle != IntPtr.Zero)
{
// Fixed registration handle because a new EventPipeEventProvider
@@ -62,28 +62,11 @@ namespace System.Diagnostics.Tracing
{
if (userDataCount == 0)
{
- EventPipeInternal.WriteEvent(eventHandle, eventID, null, 0, activityId, relatedActivityId);
+ EventPipeInternal.WriteEventData(eventHandle, eventID, null, 0, activityId, relatedActivityId);
return 0;
}
- uint length = 0;
- for (int i = 0; i < userDataCount; i++)
- {
- length += userData[i].Size;
- }
-
- byte[] data = new byte[length];
- fixed (byte *pData = data)
- {
- uint offset = 0;
- for (int i = 0; i < userDataCount; i++)
- {
- byte * singleUserDataPtr = (byte *)(userData[i].Ptr);
- uint singleUserDataSize = userData[i].Size;
- WriteToBuffer(pData, length, ref offset, singleUserDataPtr, singleUserDataSize);
- }
- EventPipeInternal.WriteEvent(eventHandle, eventID, pData, length, activityId, relatedActivityId);
- }
+ EventPipeInternal.WriteEventData(eventHandle, eventID, &userData, (uint) userDataCount, activityId, relatedActivityId);
}
return 0;
}
diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs
index 01aac72cf6..c5e2b20a80 100644
--- a/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs
+++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs
@@ -77,7 +77,7 @@ namespace System.Diagnostics.Tracing
/// This API marks the current thread as working on activity 'activityID'. It returns
/// whatever activity the thread was previously marked with. There is a convention that
/// callers can assume that callees restore this activity mark before the callee returns.
- /// To encourage this this API returns the old activity, so that it can be restored later.
+ /// To encourage this, this API returns the old activity, so that it can be restored later.
///
/// All events created with the EventSource on this thread are also tagged with the
/// activity ID of the thread.
diff --git a/src/mscorlib/src/System/Diagnostics/Stacktrace.cs b/src/mscorlib/src/System/Diagnostics/Stacktrace.cs
index 6a138de327..097ed44f85 100644
--- a/src/mscorlib/src/System/Diagnostics/Stacktrace.cs
+++ b/src/mscorlib/src/System/Diagnostics/Stacktrace.cs
@@ -247,9 +247,6 @@ namespace System.Diagnostics
// Class which represents a description of a stack trace
// There is no good reason for the methods of this class to be virtual.
- // In order to ensure trusted code can trust the data it gets from a
- // StackTrace, we use an InheritanceDemand to prevent partially-trusted
- // subclasses.
public class StackTrace
{
private StackFrame[] frames;
diff --git a/src/mscorlib/src/System/Enum.cs b/src/mscorlib/src/System/Enum.cs
index 25a4ff34d3..b5d00fe60e 100644
--- a/src/mscorlib/src/System/Enum.cs
+++ b/src/mscorlib/src/System/Enum.cs
@@ -12,6 +12,18 @@ using System.Runtime.Versioning;
using System.Diagnostics;
using System.Diagnostics.Contracts;
+// The code below includes partial support for float/double and
+// pointer sized enums.
+//
+// The type loader does not prohibit such enums, and older versions of
+// the ECMA spec include them as possible enum types.
+//
+// However there are many things broken throughout the stack for
+// float/double/intptr/uintptr enums. There was a conscious decision
+// made to not fix the whole stack to work well for them because of
+// the right behavior is often unclear, and it is hard to test and
+// very low value because of such enums cannot be expressed in C#.
+
namespace System
{
[Serializable]
@@ -425,7 +437,7 @@ namespace System
}
if (firstNonWhitespaceIndex == -1)
{
- parseResult.SetFailure(ParseFailureKind.Argument, "Arg_MustContainEnumInfo", null);
+ parseResult.SetFailure(ParseFailureKind.Argument, nameof(SR.Arg_MustContainEnumInfo), null);
return false;
}
@@ -506,7 +518,7 @@ namespace System
if (!success)
{
// Not found, throw an argument exception.
- parseResult.SetFailure(ParseFailureKind.ArgumentWithParameter, "Arg_EnumValueNotFound", value);
+ parseResult.SetFailure(ParseFailureKind.ArgumentWithParameter, nameof(SR.Arg_EnumValueNotFound), value);
return false;
}
@@ -961,6 +973,7 @@ namespace System
return ToString();
}
+ [Intrinsic]
public Boolean HasFlag(Enum flag)
{
if (flag == null)
diff --git a/src/mscorlib/src/System/Environment.cs b/src/mscorlib/src/System/Environment.cs
index e634341584..ea99a1e3f2 100644
--- a/src/mscorlib/src/System/Environment.cs
+++ b/src/mscorlib/src/System/Environment.cs
@@ -62,21 +62,6 @@ namespace System
return SR.GetResourceString(key);
}
- // Private object for locking instead of locking on a public type for SQL reliability work.
- private static Object s_InternalSyncObject;
- private static Object InternalSyncObject
- {
- get
- {
- if (s_InternalSyncObject == null)
- {
- Object o = new Object();
- Interlocked.CompareExchange<Object>(ref s_InternalSyncObject, o, null);
- }
- return s_InternalSyncObject;
- }
- }
-
/*==================================TickCount===================================
**Action: Gets the number of ticks since the system was started.
**Returns: The number of ticks since the system was started.
@@ -139,7 +124,7 @@ namespace System
StringBuilder sb = new StringBuilder(Path.MaxPath);
int r = Win32Native.GetSystemDirectory(sb, Path.MaxPath);
Debug.Assert(r < Path.MaxPath, "r < Path.MaxPath");
- if (r == 0) __Error.WinIOError();
+ if (r == 0) throw Win32Marshal.GetExceptionForLastWin32Error();
String path = sb.ToString();
return path;
@@ -256,44 +241,38 @@ namespace System
{
char[] block = null;
- // Make sure pStrings is not leaked with async exceptions
RuntimeHelpers.PrepareConstrainedRegions();
+
+ char* pStrings = null;
+
try
{
- }
- finally
- {
- char* pStrings = null;
-
- try
+ pStrings = Win32Native.GetEnvironmentStrings();
+ if (pStrings == null)
{
- pStrings = Win32Native.GetEnvironmentStrings();
- if (pStrings == null)
- {
- throw new OutOfMemoryException();
- }
+ throw new OutOfMemoryException();
+ }
- // Format for GetEnvironmentStrings is:
- // [=HiddenVar=value\0]* [Variable=value\0]* \0
- // See the description of Environment Blocks in MSDN's
- // CreateProcess page (null-terminated array of null-terminated strings).
+ // Format for GetEnvironmentStrings is:
+ // [=HiddenVar=value\0]* [Variable=value\0]* \0
+ // See the description of Environment Blocks in MSDN's
+ // CreateProcess page (null-terminated array of null-terminated strings).
- // Search for terminating \0\0 (two unicode \0's).
- char* p = pStrings;
- while (!(*p == '\0' && *(p + 1) == '\0'))
- p++;
+ // Search for terminating \0\0 (two unicode \0's).
+ char* p = pStrings;
+ while (!(*p == '\0' && *(p + 1) == '\0'))
+ p++;
- int len = (int)(p - pStrings + 1);
- block = new char[len];
+ int len = (int)(p - pStrings + 1);
+ block = new char[len];
- fixed (char* pBlock = block)
- string.wstrcpy(pBlock, pStrings, len);
- }
- finally
- {
- if (pStrings != null)
- Win32Native.FreeEnvironmentStrings(pStrings);
- }
+ fixed (char* pBlock = block)
+ string.wstrcpy(pBlock, pStrings, len);
+ }
+ finally
+ {
+ if (pStrings != null)
+ Win32Native.FreeEnvironmentStrings(pStrings);
}
return block;
@@ -735,7 +714,7 @@ namespace System
// which is not accurate.
throw new ArgumentException(SR.Format(SR.Argument_LongEnvVarValue));
default:
- throw new ArgumentException(Win32Native.GetMessage(errorCode));
+ throw new ArgumentException(Interop.Kernel32.GetMessage(errorCode));
}
}
}
@@ -803,8 +782,8 @@ namespace System
}
// send a WM_SETTINGCHANGE message to all windows
- IntPtr r = Win32Native.SendMessageTimeout(new IntPtr(Win32Native.HWND_BROADCAST),
- Win32Native.WM_SETTINGCHANGE, IntPtr.Zero, "Environment", 0, 1000, IntPtr.Zero);
+ IntPtr r = Interop.User32.SendMessageTimeout(new IntPtr(Interop.User32.HWND_BROADCAST),
+ Interop.User32.WM_SETTINGCHANGE, IntPtr.Zero, "Environment", 0, 1000, IntPtr.Zero);
if (r == IntPtr.Zero) Debug.Assert(false, "SetEnvironmentVariable failed: " + Marshal.GetLastWin32Error());
#endif // FEATURE_WIN32_REGISTRY
diff --git a/src/mscorlib/src/System/Exception.cs b/src/mscorlib/src/System/Exception.cs
index 725adac4a9..785637d1ba 100644
--- a/src/mscorlib/src/System/Exception.cs
+++ b/src/mscorlib/src/System/Exception.cs
@@ -36,7 +36,7 @@ namespace System
_message = null;
_stackTrace = null;
_dynamicMethods = null;
- HResult = __HResults.COR_E_EXCEPTION;
+ HResult = HResults.COR_E_EXCEPTION;
_xcode = _COMPlusExceptionCode;
_xptrs = (IntPtr)0;
@@ -477,7 +477,7 @@ namespace System
}
// This method will clear the _stackTrace of the exception object upon deserialization
- // to ensure that references from another AD/Process dont get accidently used.
+ // to ensure that references from another AD/Process dont get accidentally used.
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
diff --git a/src/mscorlib/src/System/Globalization/CompareInfo.Unix.cs b/src/mscorlib/src/System/Globalization/CompareInfo.Unix.cs
index edc9a7f575..92fd691c8b 100644
--- a/src/mscorlib/src/System/Globalization/CompareInfo.Unix.cs
+++ b/src/mscorlib/src/System/Globalization/CompareInfo.Unix.cs
@@ -137,14 +137,6 @@ namespace System.Globalization
return -1;
}
- private int GetHashCodeOfStringCore(string source, CompareOptions options)
- {
- Debug.Assert(source != null);
- Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);
-
- return GetHashCodeOfStringCore(source, options, forceRandomizedHashing: false, additionalEntropy: 0);
- }
-
private static unsafe int CompareStringOrdinalIgnoreCase(char* string1, int count1, char* string2, int count2)
{
Debug.Assert(!GlobalizationMode.Invariant);
@@ -357,7 +349,7 @@ namespace System.Globalization
// ---- PAL layer ends here ----
// -----------------------------
- internal unsafe int GetHashCodeOfStringCore(string source, CompareOptions options, bool forceRandomizedHashing, long additionalEntropy)
+ internal unsafe int GetHashCodeOfStringCore(string source, CompareOptions options)
{
Debug.Assert(!_invariantMode);
@@ -376,7 +368,7 @@ namespace System.Globalization
{
byte* pSortKey = stackalloc byte[sortKeyLength];
Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options);
- return InternalHashSortKey(pSortKey, sortKeyLength, false, additionalEntropy);
+ return InternalHashSortKey(pSortKey, sortKeyLength);
}
byte[] sortKey = new byte[sortKeyLength];
@@ -384,13 +376,13 @@ namespace System.Globalization
fixed (byte* pSortKey = sortKey)
{
Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options);
- return InternalHashSortKey(pSortKey, sortKeyLength, false, additionalEntropy);
+ return InternalHashSortKey(pSortKey, sortKeyLength);
}
}
[DllImport(JitHelpers.QCall)]
[SuppressUnmanagedCodeSecurity]
- private static unsafe extern int InternalHashSortKey(byte* sortKey, int sortKeyLength, [MarshalAs(UnmanagedType.Bool)] bool forceRandomizedHashing, long additionalEntropy);
+ private static unsafe extern int InternalHashSortKey(byte* sortKey, int sortKeyLength);
private static CompareOptions GetOrdinalCompareOptions(CompareOptions options)
{
@@ -428,7 +420,7 @@ namespace System.Globalization
{
Debug.Assert(!_invariantMode);
- int sortVersion = Interop.GlobalizationInterop.GetSortVersion();
+ int sortVersion = Interop.GlobalizationInterop.GetSortVersion(_sortHandle);
return new SortVersion(sortVersion, LCID, new Guid(sortVersion, 0, 0, 0, 0, 0, 0,
(byte) (LCID >> 24),
(byte) ((LCID & 0x00FF0000) >> 16),
diff --git a/src/mscorlib/src/System/Globalization/CompareInfo.Windows.cs b/src/mscorlib/src/System/Globalization/CompareInfo.Windows.cs
index 0df5463a2f..2bfe15b835 100644
--- a/src/mscorlib/src/System/Globalization/CompareInfo.Windows.cs
+++ b/src/mscorlib/src/System/Globalization/CompareInfo.Windows.cs
@@ -92,7 +92,7 @@ namespace System.Globalization
int flags = GetNativeCompareFlags(options);
int tmpHash = 0;
#if CORECLR
- tmpHash = InternalGetGlobalizedHashCode(_sortHandle, _sortName, source, source.Length, flags, 0);
+ tmpHash = InternalGetGlobalizedHashCode(_sortHandle, _sortName, source, source.Length, flags);
#else
fixed (char* pSource = source)
{
@@ -481,7 +481,7 @@ namespace System.Globalization
// Get a locale sensitive sort hash code from native code -- COMNlsInfo::InternalGetGlobalizedHashCode
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
- private static extern int InternalGetGlobalizedHashCode(IntPtr handle, string localeName, string source, int length, int dwFlags, long additionalEntropy);
+ private static extern int InternalGetGlobalizedHashCode(IntPtr handle, string localeName, string source, int length, int dwFlags);
#endif
}
}
diff --git a/src/mscorlib/src/System/Globalization/CultureData.Windows.cs b/src/mscorlib/src/System/Globalization/CultureData.Windows.cs
index c39327e9e7..aadf6bd716 100644
--- a/src/mscorlib/src/System/Globalization/CultureData.Windows.cs
+++ b/src/mscorlib/src/System/Globalization/CultureData.Windows.cs
@@ -326,20 +326,14 @@ namespace System.Globalization
#if ENABLE_WINRT
return WinRTInterop.Callbacks.GetRegionDisplayName(isoCountryCode);
#else
- // Usually the UI culture shouldn't be different than what we got from WinRT except
- // if DefaultThreadCurrentUICulture was set
- CultureInfo ci;
-
- if (CultureInfo.DefaultThreadCurrentUICulture != null &&
- ((ci = GetUserDefaultCulture()) != null) &&
- !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
- {
- return SNATIVECOUNTRY;
- }
- else
+ // If the current UI culture matching the OS UI language, we'll get the display name from the OS.
+ // otherwise, we use the native name as we don't carry resources for the region display names anyway.
+ if (CultureInfo.CurrentUICulture.Name.Equals(CultureInfo.UserDefaultUICulture.Name))
{
return GetLocaleInfo(LocaleStringData.LocalizedCountryName);
}
+
+ return SNATIVECOUNTRY;
#endif // ENABLE_WINRT
}
diff --git a/src/mscorlib/src/System/Globalization/CultureData.cs b/src/mscorlib/src/System/Globalization/CultureData.cs
index 0dcebf484b..d913a8929f 100644
--- a/src/mscorlib/src/System/Globalization/CultureData.cs
+++ b/src/mscorlib/src/System/Globalization/CultureData.cs
@@ -2424,77 +2424,77 @@ namespace System.Globalization
/// </remarks>
private enum LocaleStringData : uint
{
- /// <summary>localized name of locale, eg "German (Germany)" in UI language (coresponds to LOCALE_SLOCALIZEDDISPLAYNAME)</summary>
+ /// <summary>localized name of locale, eg "German (Germany)" in UI language (corresponds to LOCALE_SLOCALIZEDDISPLAYNAME)</summary>
LocalizedDisplayName = 0x00000002,
- /// <summary>Display name (language + country usually) in English, eg "German (Germany)" (coresponds to LOCALE_SENGLISHDISPLAYNAME)</summary>
+ /// <summary>Display name (language + country usually) in English, eg "German (Germany)" (corresponds to LOCALE_SENGLISHDISPLAYNAME)</summary>
EnglishDisplayName = 0x00000072,
- /// <summary>Display name in native locale language, eg "Deutsch (Deutschland) (coresponds to LOCALE_SNATIVEDISPLAYNAME)</summary>
+ /// <summary>Display name in native locale language, eg "Deutsch (Deutschland) (corresponds to LOCALE_SNATIVEDISPLAYNAME)</summary>
NativeDisplayName = 0x00000073,
- /// <summary>Language Display Name for a language, eg "German" in UI language (coresponds to LOCALE_SLOCALIZEDLANGUAGENAME)</summary>
+ /// <summary>Language Display Name for a language, eg "German" in UI language (corresponds to LOCALE_SLOCALIZEDLANGUAGENAME)</summary>
LocalizedLanguageName = 0x0000006f,
- /// <summary>English name of language, eg "German" (coresponds to LOCALE_SENGLISHLANGUAGENAME)</summary>
+ /// <summary>English name of language, eg "German" (corresponds to LOCALE_SENGLISHLANGUAGENAME)</summary>
EnglishLanguageName = 0x00001001,
- /// <summary>native name of language, eg "Deutsch" (coresponds to LOCALE_SNATIVELANGUAGENAME)</summary>
+ /// <summary>native name of language, eg "Deutsch" (corresponds to LOCALE_SNATIVELANGUAGENAME)</summary>
NativeLanguageName = 0x00000004,
- /// <summary>localized name of country, eg "Germany" in UI language (coresponds to LOCALE_SLOCALIZEDCOUNTRYNAME)</summary>
+ /// <summary>localized name of country, eg "Germany" in UI language (corresponds to LOCALE_SLOCALIZEDCOUNTRYNAME)</summary>
LocalizedCountryName = 0x00000006,
- /// <summary>English name of country, eg "Germany" (coresponds to LOCALE_SENGLISHCOUNTRYNAME)</summary>
+ /// <summary>English name of country, eg "Germany" (corresponds to LOCALE_SENGLISHCOUNTRYNAME)</summary>
EnglishCountryName = 0x00001002,
- /// <summary>native name of country, eg "Deutschland" (coresponds to LOCALE_SNATIVECOUNTRYNAME)</summary>
+ /// <summary>native name of country, eg "Deutschland" (corresponds to LOCALE_SNATIVECOUNTRYNAME)</summary>
NativeCountryName = 0x00000008,
- /// <summary>abbreviated language name (coresponds to LOCALE_SABBREVLANGNAME)</summary>
+ /// <summary>abbreviated language name (corresponds to LOCALE_SABBREVLANGNAME)</summary>
AbbreviatedWindowsLanguageName = 0x00000003,
- /// <summary>list item separator (coresponds to LOCALE_SLIST)</summary>
+ /// <summary>list item separator (corresponds to LOCALE_SLIST)</summary>
ListSeparator = 0x0000000C,
- /// <summary>decimal separator (coresponds to LOCALE_SDECIMAL)</summary>
+ /// <summary>decimal separator (corresponds to LOCALE_SDECIMAL)</summary>
DecimalSeparator = 0x0000000E,
- /// <summary>thousand separator (coresponds to LOCALE_STHOUSAND)</summary>
+ /// <summary>thousand separator (corresponds to LOCALE_STHOUSAND)</summary>
ThousandSeparator = 0x0000000F,
- /// <summary>digit grouping (coresponds to LOCALE_SGROUPING)</summary>
+ /// <summary>digit grouping (corresponds to LOCALE_SGROUPING)</summary>
Digits = 0x00000013,
- /// <summary>local monetary symbol (coresponds to LOCALE_SCURRENCY)</summary>
+ /// <summary>local monetary symbol (corresponds to LOCALE_SCURRENCY)</summary>
MonetarySymbol = 0x00000014,
- /// <summary>English currency name (coresponds to LOCALE_SENGCURRNAME)</summary>
+ /// <summary>English currency name (corresponds to LOCALE_SENGCURRNAME)</summary>
CurrencyEnglishName = 0x00001007,
- /// <summary>Native currency name (coresponds to LOCALE_SNATIVECURRNAME)</summary>
+ /// <summary>Native currency name (corresponds to LOCALE_SNATIVECURRNAME)</summary>
CurrencyNativeName = 0x00001008,
- /// <summary>uintl monetary symbol (coresponds to LOCALE_SINTLSYMBOL)</summary>
+ /// <summary>uintl monetary symbol (corresponds to LOCALE_SINTLSYMBOL)</summary>
Iso4217MonetarySymbol = 0x00000015,
- /// <summary>monetary decimal separator (coresponds to LOCALE_SMONDECIMALSEP)</summary>
+ /// <summary>monetary decimal separator (corresponds to LOCALE_SMONDECIMALSEP)</summary>
MonetaryDecimalSeparator = 0x00000016,
- /// <summary>monetary thousand separator (coresponds to LOCALE_SMONTHOUSANDSEP)</summary>
+ /// <summary>monetary thousand separator (corresponds to LOCALE_SMONTHOUSANDSEP)</summary>
MonetaryThousandSeparator = 0x00000017,
- /// <summary>AM designator (coresponds to LOCALE_S1159)</summary>
+ /// <summary>AM designator (corresponds to LOCALE_S1159)</summary>
AMDesignator = 0x00000028,
- /// <summary>PM designator (coresponds to LOCALE_S2359)</summary>
+ /// <summary>PM designator (corresponds to LOCALE_S2359)</summary>
PMDesignator = 0x00000029,
- /// <summary>positive sign (coresponds to LOCALE_SPOSITIVESIGN)</summary>
+ /// <summary>positive sign (corresponds to LOCALE_SPOSITIVESIGN)</summary>
PositiveSign = 0x00000050,
- /// <summary>negative sign (coresponds to LOCALE_SNEGATIVESIGN)</summary>
+ /// <summary>negative sign (corresponds to LOCALE_SNEGATIVESIGN)</summary>
NegativeSign = 0x00000051,
- /// <summary>ISO abbreviated language name (coresponds to LOCALE_SISO639LANGNAME)</summary>
+ /// <summary>ISO abbreviated language name (corresponds to LOCALE_SISO639LANGNAME)</summary>
Iso639LanguageTwoLetterName = 0x00000059,
- /// <summary>ISO abbreviated country name (coresponds to LOCALE_SISO639LANGNAME2)</summary>
+ /// <summary>ISO abbreviated country name (corresponds to LOCALE_SISO639LANGNAME2)</summary>
Iso639LanguageThreeLetterName = 0x00000067,
- /// <summary>ISO abbreviated language name (coresponds to LOCALE_SISO639LANGNAME)</summary>
+ /// <summary>ISO abbreviated language name (corresponds to LOCALE_SISO639LANGNAME)</summary>
Iso639LanguageName = 0x00000059,
- /// <summary>ISO abbreviated country name (coresponds to LOCALE_SISO3166CTRYNAME)</summary>
+ /// <summary>ISO abbreviated country name (corresponds to LOCALE_SISO3166CTRYNAME)</summary>
Iso3166CountryName = 0x0000005A,
- /// <summary>3 letter ISO country code (coresponds to LOCALE_SISO3166CTRYNAME2)</summary>
+ /// <summary>3 letter ISO country code (corresponds to LOCALE_SISO3166CTRYNAME2)</summary>
Iso3166CountryName2 = 0x00000068, // 3 character ISO country name
- /// <summary>Not a Number (coresponds to LOCALE_SNAN)</summary>
+ /// <summary>Not a Number (corresponds to LOCALE_SNAN)</summary>
NaNSymbol = 0x00000069,
- /// <summary>+ Infinity (coresponds to LOCALE_SPOSINFINITY)</summary>
+ /// <summary>+ Infinity (corresponds to LOCALE_SPOSINFINITY)</summary>
PositiveInfinitySymbol = 0x0000006a,
- /// <summary>- Infinity (coresponds to LOCALE_SNEGINFINITY)</summary>
+ /// <summary>- Infinity (corresponds to LOCALE_SNEGINFINITY)</summary>
NegativeInfinitySymbol = 0x0000006b,
- /// <summary>Fallback name for resources (coresponds to LOCALE_SPARENT)</summary>
+ /// <summary>Fallback name for resources (corresponds to LOCALE_SPARENT)</summary>
ParentName = 0x0000006d,
- /// <summary>Fallback name for within the console (coresponds to LOCALE_SCONSOLEFALLBACKNAME)</summary>
+ /// <summary>Fallback name for within the console (corresponds to LOCALE_SCONSOLEFALLBACKNAME)</summary>
ConsoleFallbackName = 0x0000006e,
- /// <summary>Returns the percent symbol (coresponds to LOCALE_SPERCENT)</summary>
+ /// <summary>Returns the percent symbol (corresponds to LOCALE_SPERCENT)</summary>
PercentSymbol = 0x00000076,
- /// <summary>Returns the permille (U+2030) symbol (coresponds to LOCALE_SPERMILLE)</summary>
+ /// <summary>Returns the permille (U+2030) symbol (corresponds to LOCALE_SPERMILLE)</summary>
PerMilleSymbol = 0x00000077
}
@@ -2504,9 +2504,9 @@ namespace System.Globalization
/// </remarks>
private enum LocaleGroupingData : uint
{
- /// <summary>digit grouping (coresponds to LOCALE_SGROUPING)</summary>
+ /// <summary>digit grouping (corresponds to LOCALE_SGROUPING)</summary>
Digit = 0x00000010,
- /// <summary>monetary grouping (coresponds to LOCALE_SMONGROUPING)</summary>
+ /// <summary>monetary grouping (corresponds to LOCALE_SMONGROUPING)</summary>
Monetary = 0x00000018,
}
@@ -2516,29 +2516,29 @@ namespace System.Globalization
/// </remarks>
private enum LocaleNumberData : uint
{
- /// <summary>language id (coresponds to LOCALE_ILANGUAGE)</summary>
+ /// <summary>language id (corresponds to LOCALE_ILANGUAGE)</summary>
LanguageId = 0x00000001,
- /// <summary>geographical location id, (coresponds to LOCALE_IGEOID)</summary>
+ /// <summary>geographical location id, (corresponds to LOCALE_IGEOID)</summary>
GeoId = 0x0000005B,
- /// <summary>0 = context, 1 = none, 2 = national (coresponds to LOCALE_IDIGITSUBSTITUTION)</summary>
+ /// <summary>0 = context, 1 = none, 2 = national (corresponds to LOCALE_IDIGITSUBSTITUTION)</summary>
DigitSubstitution = 0x00001014,
- /// <summary>0 = metric, 1 = US (coresponds to LOCALE_IMEASURE)</summary>
+ /// <summary>0 = metric, 1 = US (corresponds to LOCALE_IMEASURE)</summary>
MeasurementSystem = 0x0000000D,
- /// <summary>number of fractional digits (coresponds to LOCALE_IDIGITS)</summary>
+ /// <summary>number of fractional digits (corresponds to LOCALE_IDIGITS)</summary>
FractionalDigitsCount = 0x00000011,
- /// <summary>negative number mode (coresponds to LOCALE_INEGNUMBER)</summary>
+ /// <summary>negative number mode (corresponds to LOCALE_INEGNUMBER)</summary>
NegativeNumberFormat = 0x00001010,
- /// <summary># local monetary digits (coresponds to LOCALE_ICURRDIGITS)</summary>
+ /// <summary># local monetary digits (corresponds to LOCALE_ICURRDIGITS)</summary>
MonetaryFractionalDigitsCount = 0x00000019,
- /// <summary>positive currency mode (coresponds to LOCALE_ICURRENCY)</summary>
+ /// <summary>positive currency mode (corresponds to LOCALE_ICURRENCY)</summary>
PositiveMonetaryNumberFormat = 0x0000001B,
- /// <summary>negative currency mode (coresponds to LOCALE_INEGCURR)</summary>
+ /// <summary>negative currency mode (corresponds to LOCALE_INEGCURR)</summary>
NegativeMonetaryNumberFormat = 0x0000001C,
- /// <summary>type of calendar specifier (coresponds to LOCALE_ICALENDARTYPE)</summary>
+ /// <summary>type of calendar specifier (corresponds to LOCALE_ICALENDARTYPE)</summary>
CalendarType = 0x00001009,
- /// <summary>first day of week specifier (coresponds to LOCALE_IFIRSTDAYOFWEEK)</summary>
+ /// <summary>first day of week specifier (corresponds to LOCALE_IFIRSTDAYOFWEEK)</summary>
FirstDayOfWeek = 0x0000100C,
- /// <summary>first week of year specifier (coresponds to LOCALE_IFIRSTWEEKOFYEAR)</summary>
+ /// <summary>first week of year specifier (corresponds to LOCALE_IFIRSTWEEKOFYEAR)</summary>
FirstWeekOfYear = 0x0000100D,
/// <summary>
/// Returns one of the following 4 reading layout values:
@@ -2546,20 +2546,20 @@ namespace System.Globalization
/// 1 - Right to left (eg arabic locales)
/// 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
/// 3 - Vertical top to bottom with columns proceeding to the right
- /// (coresponds to LOCALE_IREADINGLAYOUT)
+ /// (corresponds to LOCALE_IREADINGLAYOUT)
/// </summary>
ReadingLayout = 0x00000070,
- /// <summary>Returns 0-11 for the negative percent format (coresponds to LOCALE_INEGATIVEPERCENT)</summary>
+ /// <summary>Returns 0-11 for the negative percent format (corresponds to LOCALE_INEGATIVEPERCENT)</summary>
NegativePercentFormat = 0x00000074,
- /// <summary>Returns 0-3 for the positive percent format (coresponds to LOCALE_IPOSITIVEPERCENT)</summary>
+ /// <summary>Returns 0-3 for the positive percent format (corresponds to LOCALE_IPOSITIVEPERCENT)</summary>
PositivePercentFormat = 0x00000075,
- /// <summary>default ansi code page (coresponds to LOCALE_IDEFAULTCODEPAGE)</summary>
+ /// <summary>default ansi code page (corresponds to LOCALE_IDEFAULTCODEPAGE)</summary>
OemCodePage = 0x0000000B,
- /// <summary>default ansi code page (coresponds to LOCALE_IDEFAULTANSICODEPAGE)</summary>
+ /// <summary>default ansi code page (corresponds to LOCALE_IDEFAULTANSICODEPAGE)</summary>
AnsiCodePage = 0x00001004,
- /// <summary>default mac code page (coresponds to LOCALE_IDEFAULTMACCODEPAGE)</summary>
+ /// <summary>default mac code page (corresponds to LOCALE_IDEFAULTMACCODEPAGE)</summary>
MacCodePage = 0x00001011,
- /// <summary>default ebcdic code page (coresponds to LOCALE_IDEFAULTEBCDICCODEPAGE)</summary>
+ /// <summary>default ebcdic code page (corresponds to LOCALE_IDEFAULTEBCDICCODEPAGE)</summary>
EbcdicCodePage = 0x00001012,
}
}
diff --git a/src/mscorlib/src/System/Globalization/CultureInfo.cs b/src/mscorlib/src/System/Globalization/CultureInfo.cs
index f176ea045d..fd879c709d 100644
--- a/src/mscorlib/src/System/Globalization/CultureInfo.cs
+++ b/src/mscorlib/src/System/Globalization/CultureInfo.cs
@@ -206,6 +206,25 @@ namespace System.Globalization
InitializeFromName(name, useUserOverride);
}
+ private CultureInfo(CultureData cultureData)
+ {
+ Debug.Assert(cultureData != null);
+ _cultureData = cultureData;
+ _name = cultureData.CultureName;
+ _isInherited = false;
+ }
+
+ private static CultureInfo CreateCultureInfoNoThrow(string name, bool useUserOverride)
+ {
+ Debug.Assert(name != null);
+ CultureData cultureData = CultureData.GetCultureData(name, useUserOverride);
+ if (cultureData == null)
+ {
+ return null;
+ }
+
+ return new CultureInfo(cultureData);
+ }
public CultureInfo(int culture) : this(culture, true)
{
}
@@ -439,15 +458,23 @@ namespace System.Globalization
return ci;
}
- // if s_userDefaultUICulture == null means CultureInfo statics didn't get initialized yet. this can happen if there early static
- // method get executed which eventually hit the cultureInfo code while CultureInfo statics didn’t get chance to initialize
- if (s_userDefaultUICulture == null)
+ return UserDefaultUICulture;
+ }
+
+ internal static CultureInfo UserDefaultUICulture
+ {
+ get
{
- Init();
- }
+ // if s_userDefaultUICulture == null means CultureInfo statics didn't get initialized yet. this can happen if there early static
+ // method get executed which eventually hit the cultureInfo code while CultureInfo statics didn’t get chance to initialize
+ if (s_userDefaultUICulture == null)
+ {
+ Init();
+ }
- Debug.Assert(s_userDefaultUICulture != null);
- return s_userDefaultUICulture;
+ Debug.Assert(s_userDefaultUICulture != null);
+ return s_userDefaultUICulture;
+ }
}
public static CultureInfo InstalledUICulture
@@ -533,25 +560,22 @@ namespace System.Globalization
{
if (null == _parent)
{
- try
- {
- string parentName = _cultureData.SPARENT;
+ string parentName = _cultureData.SPARENT;
- if (String.IsNullOrEmpty(parentName))
+ if (String.IsNullOrEmpty(parentName))
+ {
+ _parent = InvariantCulture;
+ }
+ else
+ {
+ _parent = CreateCultureInfoNoThrow(parentName, _cultureData.UseUserOverride);
+ if (_parent == null)
{
+ // For whatever reason our IPARENT or SPARENT wasn't correct, so use invariant
+ // We can't allow ourselves to fail. In case of custom cultures the parent of the
+ // current custom culture isn't installed.
_parent = InvariantCulture;
}
- else
- {
- _parent = new CultureInfo(parentName, _cultureData.UseUserOverride);
- }
- }
- catch (ArgumentException)
- {
- // For whatever reason our IPARENT or SPARENT wasn't correct, so use invariant
- // We can't allow ourselves to fail. In case of custom cultures the parent of the
- // current custom culture isn't installed.
- _parent = InvariantCulture;
}
}
return _parent;
diff --git a/src/mscorlib/src/System/Globalization/GlobalizationAssembly.cs b/src/mscorlib/src/System/Globalization/GlobalizationAssembly.cs
deleted file mode 100644
index 956543524a..0000000000
--- a/src/mscorlib/src/System/Globalization/GlobalizationAssembly.cs
+++ /dev/null
@@ -1,64 +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.
-
-
-using System;
-using System.Reflection;
-using System.Collections;
-using System.Collections.Generic;
-using System.Threading;
-using System.Security;
-using System.Runtime.CompilerServices;
-using System.Runtime.ConstrainedExecution;
-using System.Runtime.Versioning;
-using System.IO;
-using System.Diagnostics;
-using System.Diagnostics.Contracts;
-
-namespace System.Globalization
-{
- /*=================================GlobalizationAssembly==========================
- **
- ** This class provides the table loading wrapper that calls GetManifestResourceStream
- **
- ** It used to provide an idea for sort versioning, but that proved to not work
- **
- ============================================================================*/
- internal sealed class GlobalizationAssembly
- {
- // ----------------------------------------------------------------------------------------------------
- //
- // Instance data members and instance methods.
- //
- // ----------------------------------------------------------------------------------------------------
- internal unsafe static byte* GetGlobalizationResourceBytePtr(Assembly assembly, String tableName)
- {
- Debug.Assert(assembly != null, "assembly can not be null. This should be generally the " + System.CoreLib.Name + " assembly.");
- Debug.Assert(tableName != null, "table name can not be null");
-
- Stream stream = assembly.GetManifestResourceStream(tableName);
- UnmanagedMemoryStream bytesStream = stream as UnmanagedMemoryStream;
- if (bytesStream != null)
- {
- byte* bytes = bytesStream.PositionPointer;
- if (bytes != null)
- {
- return (bytes);
- }
- }
-
- Debug.Assert(
- false,
- String.Format(
- CultureInfo.CurrentCulture,
- "Didn't get the resource table {0} for System.Globalization from {1}",
- tableName,
- assembly));
-
- // We can not continue if we can't get the resource.
- throw new InvalidOperationException();
- }
- }
-}
-
diff --git a/src/mscorlib/src/System/Globalization/TextInfo.Unix.cs b/src/mscorlib/src/System/Globalization/TextInfo.Unix.cs
index f7f64c684a..9f80d73891 100644
--- a/src/mscorlib/src/System/Globalization/TextInfo.Unix.cs
+++ b/src/mscorlib/src/System/Globalization/TextInfo.Unix.cs
@@ -11,7 +11,6 @@ namespace System.Globalization
{
public partial class TextInfo
{
- [NonSerialized]
private Tristate _needsTurkishCasing = Tristate.NotInitialized;
private void FinishInitialization(string textInfoName)
diff --git a/src/mscorlib/src/System/Globalization/TextInfo.Windows.cs b/src/mscorlib/src/System/Globalization/TextInfo.Windows.cs
index cc7c4df1da..58dfe32239 100644
--- a/src/mscorlib/src/System/Globalization/TextInfo.Windows.cs
+++ b/src/mscorlib/src/System/Globalization/TextInfo.Windows.cs
@@ -18,9 +18,9 @@ namespace System.Globalization
const uint LCMAP_SORTHANDLE = 0x20000000;
- long handle;
+ IntPtr handle;
int ret = Interop.Kernel32.LCMapStringEx(_textInfoName, LCMAP_SORTHANDLE, null, 0, &handle, IntPtr.Size, null, null, IntPtr.Zero);
- _sortHandle = ret > 0 ? (IntPtr)handle : IntPtr.Zero;
+ _sortHandle = ret > 0 ? handle : IntPtr.Zero;
}
private unsafe string ChangeCase(string s, bool toUpper)
diff --git a/src/mscorlib/src/System/Globalization/TextInfo.cs b/src/mscorlib/src/System/Globalization/TextInfo.cs
index 9a628768f1..c5acbd4579 100644
--- a/src/mscorlib/src/System/Globalization/TextInfo.cs
+++ b/src/mscorlib/src/System/Globalization/TextInfo.cs
@@ -21,10 +21,6 @@ namespace System.Globalization
{
public partial class TextInfo : ICloneable, IDeserializationCallback
{
- ////--------------------------------------------------------------------//
- //// Internal Information //
- ////--------------------------------------------------------------------//
-
private enum Tristate : byte
{
NotInitialized,
@@ -32,36 +28,22 @@ namespace System.Globalization
False,
}
- ////
- //// Variables.
- ////
-
- [OptionalField(VersionAdded = 2)]
- private String _listSeparator;
- [OptionalField(VersionAdded = 2)]
+ private string _listSeparator;
private bool _isReadOnly = false;
- //// _cultureName is the name of the creating culture. Note that we consider this authoratative,
- //// if the culture's textinfo changes when deserializing, then behavior may change.
- //// (ala Whidbey behavior). This is the only string Arrowhead needs to serialize.
- //// _cultureData is the data that backs this class.
- //// _textInfoName is the actual name of the textInfo (from cultureData.STEXTINFO)
- //// this can be the same as _cultureName on Silverlight since the OS knows
- //// how to do the sorting. However in the desktop, when we call the sorting dll, it doesn't
- //// know how to resolve custom locle names to sort ids so we have to have alredy resolved this.
- ////
-
- [OptionalField(VersionAdded = 3)]
+ /* _cultureName is the name of the creating culture.
+ _cultureData is the data that backs this class.
+ _textInfoName is the actual name of the textInfo (from cultureData.STEXTINFO)
+ In the desktop, when we call the sorting dll, it doesn't
+ know how to resolve custom locle names to sort ids so we have to have already resolved this.
+ */
+
private String _cultureName; // Name of the culture that created this text info
- [NonSerialized]
private CultureData _cultureData; // Data record for the culture that made us, not for this textinfo
- [NonSerialized]
private String _textInfoName; // Name of the text info we're using (ie: _cultureData.STEXTINFO)
- [NonSerialized]
private Tristate _isAsciiCasingSameAsInvariant = Tristate.NotInitialized;
// _invariantMode is defined for the perf reason as accessing the instance field is faster than access the static property GlobalizationMode.Invariant
- [NonSerialized]
private readonly bool _invariantMode = GlobalizationMode.Invariant;
// Invariant text info
@@ -452,7 +434,7 @@ namespace System.Globalization
return ChangeCase(str, toUpper: true);
}
- private static Char ToUpperAsciiInvariant(Char c)
+ internal static Char ToUpperAsciiInvariant(Char c)
{
if ((uint)(c - 'a') <= (uint)('z' - 'a'))
{
diff --git a/src/mscorlib/src/System/Globalization/TimeSpanParse.cs b/src/mscorlib/src/System/Globalization/TimeSpanParse.cs
deleted file mode 100644
index bcfb2dafc5..0000000000
--- a/src/mscorlib/src/System/Globalization/TimeSpanParse.cs
+++ /dev/null
@@ -1,1810 +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.
-
-//
-////////////////////////////////////////////////////////////////////////////
-//
-//
-// Purpose: This class is called by TimeSpan to parse a time interval string.
-//
-// Standard Format:
-// -=-=-=-=-=-=-=-
-// "c": Constant format. [-][d'.']hh':'mm':'ss['.'fffffff]
-// Not culture sensitive. Default format (and null/empty format string) map to this format.
-//
-// "g": General format, short: [-][d':']h':'mm':'ss'.'FFFFFFF
-// Only print what's needed. Localized (if you want Invariant, pass in Invariant).
-// The fractional seconds separator is localized, equal to the culture's DecimalSeparator.
-//
-// "G": General format, long: [-]d':'hh':'mm':'ss'.'fffffff
-// Always print days and 7 fractional digits. Localized (if you want Invariant, pass in Invariant).
-// The fractional seconds separator is localized, equal to the culture's DecimalSeparator.
-//
-//
-// * "TryParseTimeSpan" is the main method for Parse/TryParse
-//
-// - TimeSpanTokenizer.GetNextToken() is used to split the input string into number and literal tokens.
-// - TimeSpanRawInfo.ProcessToken() adds the next token into the parsing intermediary state structure
-// - ProcessTerminalState() uses the fully initialized TimeSpanRawInfo to find a legal parse match.
-// The terminal states are attempted as follows:
-// foreach (+InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern) try
-// 1 number => d
-// 2 numbers => h:m
-// 3 numbers => h:m:s | d.h:m | h:m:.f
-// 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f
-// 5 numbers => d.h:m:s.f
-//
-// Custom Format:
-// -=-=-=-=-=-=-=
-//
-// * "TryParseExactTimeSpan" is the main method for ParseExact/TryParseExact methods
-// * "TryParseExactMultipleTimeSpan" is the main method for ParseExact/TryparseExact
-// methods that take a String[] of formats
-//
-// - For single-letter formats "TryParseTimeSpan" is called (see above)
-// - For multi-letter formats "TryParseByFormat" is called
-// - TryParseByFormat uses helper methods (ParseExactLiteral, ParseExactDigits, etc)
-// which drive the underlying TimeSpanTokenizer. However, unlike standard formatting which
-// operates on whole-tokens, ParseExact operates at the character-level. As such,
-// TimeSpanTokenizer.NextChar and TimeSpanTokenizer.BackOne() are called directly.
-//
-////////////////////////////////////////////////////////////////////////////
-
-using System.Text;
-using System;
-using System.Diagnostics;
-using System.Diagnostics.Contracts;
-using System.Globalization;
-
-namespace System.Globalization
-{
- internal static class TimeSpanParse
- {
- // ---- SECTION: members for internal support ---------*
- internal const int unlimitedDigits = -1;
- internal const int maxFractionDigits = 7;
-
- internal const int maxDays = 10675199;
- internal const int maxHours = 23;
- internal const int maxMinutes = 59;
- internal const int maxSeconds = 59;
- internal const int maxFraction = 9999999;
-
- #region InternalSupport
- private enum TimeSpanThrowStyle
- {
- None = 0,
- All = 1,
- }
-
- private enum ParseFailureKind
- {
- None = 0,
- ArgumentNull = 1,
- Format = 2,
- FormatWithParameter = 3,
- Overflow = 4,
- }
-
- [Flags]
- private enum TimeSpanStandardStyles
- { // Standard Format Styles
- None = 0x00000000,
- Invariant = 0x00000001, //Allow Invariant Culture
- Localized = 0x00000002, //Allow Localized Culture
- RequireFull = 0x00000004, //Require the input to be in DHMSF format
- Any = Invariant | Localized,
- }
-
- // TimeSpan Token Types
- private enum TTT
- {
- None = 0, // None of the TimeSpanToken fields are set
- End = 1, // '\0'
- Num = 2, // Number
- Sep = 3, // literal
- NumOverflow = 4, // Number that overflowed
- }
-
- private static readonly TimeSpanToken zero = new TimeSpanToken(0);
- private struct TimeSpanToken
- {
- internal TTT ttt;
- internal int num; // Store the number that we are parsing (if any)
- internal int zeroes; // Store the number of leading zeroes (if any)
- internal String sep; // Store the literal that we are parsing (if any)
-
- public TimeSpanToken(int number)
- {
- ttt = TTT.Num;
- num = number;
- zeroes = 0;
- sep = null;
- }
-
- public TimeSpanToken(int leadingZeroes, int number)
- {
- ttt = TTT.Num;
- num = number;
- zeroes = leadingZeroes;
- sep = null;
- }
-
- public bool IsInvalidNumber(int maxValue, int maxPrecision)
- {
- Debug.Assert(ttt == TTT.Num);
- Debug.Assert(num > -1);
- Debug.Assert(maxValue > 0);
- Debug.Assert(maxPrecision == maxFractionDigits || maxPrecision == unlimitedDigits);
-
- if (num > maxValue)
- return true;
- if (maxPrecision == unlimitedDigits)
- return false; // all validation past this point applies only to fields with precision limits
- if (zeroes > maxPrecision)
- return true;
- if (num == 0 || zeroes == 0)
- return false;
-
- // num > 0 && zeroes > 0 && num <= maxValue && zeroes <= maxPrecision
- return (num >= (maxValue / (long)Math.Pow(10, zeroes - 1)));
- }
- }
-
- //
- // TimeSpanTokenizer
- //
- // Actions: TimeSpanTokenizer.GetNextToken() returns the next token in the input string.
- //
- private struct TimeSpanTokenizer
- {
- private int m_pos;
- private String m_value;
-
- internal void Init(String input)
- {
- Init(input, 0);
- }
- internal void Init(String input, int startPosition)
- {
- m_pos = startPosition;
- m_value = input;
- }
- // used by the parsing routines that operate on standard-formats
- internal TimeSpanToken GetNextToken()
- {
- Debug.Assert(m_pos > -1);
-
- TimeSpanToken tok = new TimeSpanToken();
- char ch = CurrentChar;
-
- if (ch == (char)0)
- {
- tok.ttt = TTT.End;
- return tok;
- }
-
- if (ch >= '0' && ch <= '9')
- {
- tok.ttt = TTT.Num;
- tok.num = 0;
- tok.zeroes = 0;
- do
- {
- if ((tok.num & 0xF0000000) != 0)
- {
- tok.ttt = TTT.NumOverflow;
- return tok;
- }
- tok.num = tok.num * 10 + ch - '0';
- if (tok.num == 0) tok.zeroes++;
- if (tok.num < 0)
- {
- tok.ttt = TTT.NumOverflow;
- return tok;
- }
- ch = NextChar;
- } while (ch >= '0' && ch <= '9');
- return tok;
- }
- else
- {
- tok.ttt = TTT.Sep;
- int startIndex = m_pos;
- int length = 0;
-
- while (ch != (char)0 && (ch < '0' || '9' < ch))
- {
- ch = NextChar;
- length++;
- }
- tok.sep = m_value.Substring(startIndex, length);
- return tok;
- }
- }
-
- internal Boolean EOL
- {
- get
- {
- return m_pos >= (m_value.Length - 1);
- }
- }
- // BackOne, NextChar, CurrentChar - used by ParseExact (ParseByFormat) to operate
- // on custom-formats where exact character-by-character control is allowed
- internal void BackOne()
- {
- if (m_pos > 0) --m_pos;
- }
-
- internal char NextChar
- {
- get
- {
- m_pos++;
- return CurrentChar;
- }
- }
- internal char CurrentChar
- {
- get
- {
- if (m_pos > -1 && m_pos < m_value.Length)
- {
- return m_value[m_pos];
- }
- else
- {
- return (char)0;
- }
- }
- }
- }
-
-
-
- // This stores intermediary parsing state for the standard formats
- private struct TimeSpanRawInfo
- {
- internal TimeSpanFormat.FormatLiterals PositiveInvariant
- {
- get
- {
- return TimeSpanFormat.PositiveInvariantFormatLiterals;
- }
- }
- internal TimeSpanFormat.FormatLiterals NegativeInvariant
- {
- get
- {
- return TimeSpanFormat.NegativeInvariantFormatLiterals;
- }
- }
-
- internal TimeSpanFormat.FormatLiterals PositiveLocalized
- {
- get
- {
- if (!m_posLocInit)
- {
- m_posLoc = new TimeSpanFormat.FormatLiterals();
- m_posLoc.Init(m_fullPosPattern, false);
- m_posLocInit = true;
- }
- return m_posLoc;
- }
- }
- internal TimeSpanFormat.FormatLiterals NegativeLocalized
- {
- get
- {
- if (!m_negLocInit)
- {
- m_negLoc = new TimeSpanFormat.FormatLiterals();
- m_negLoc.Init(m_fullNegPattern, false);
- m_negLocInit = true;
- }
- return m_negLoc;
- }
- }
-
- internal Boolean FullAppCompatMatch(TimeSpanFormat.FormatLiterals pattern)
- {
- return SepCount == 5
- && NumCount == 4
- && pattern.Start == literals[0]
- && pattern.DayHourSep == literals[1]
- && pattern.HourMinuteSep == literals[2]
- && pattern.AppCompatLiteral == literals[3]
- && pattern.End == literals[4];
- }
-
- internal Boolean PartialAppCompatMatch(TimeSpanFormat.FormatLiterals pattern)
- {
- return SepCount == 4
- && NumCount == 3
- && pattern.Start == literals[0]
- && pattern.HourMinuteSep == literals[1]
- && pattern.AppCompatLiteral == literals[2]
- && pattern.End == literals[3];
- }
- // DHMSF (all values matched)
- internal Boolean FullMatch(TimeSpanFormat.FormatLiterals pattern)
- {
- return SepCount == MaxLiteralTokens
- && NumCount == MaxNumericTokens
- && pattern.Start == literals[0]
- && pattern.DayHourSep == literals[1]
- && pattern.HourMinuteSep == literals[2]
- && pattern.MinuteSecondSep == literals[3]
- && pattern.SecondFractionSep == literals[4]
- && pattern.End == literals[5];
- }
- // D (no hours, minutes, seconds, or fractions)
- internal Boolean FullDMatch(TimeSpanFormat.FormatLiterals pattern)
- {
- return SepCount == 2
- && NumCount == 1
- && pattern.Start == literals[0]
- && pattern.End == literals[1];
- }
- // HM (no days, seconds, or fractions)
- internal Boolean FullHMMatch(TimeSpanFormat.FormatLiterals pattern)
- {
- return SepCount == 3
- && NumCount == 2
- && pattern.Start == literals[0]
- && pattern.HourMinuteSep == literals[1]
- && pattern.End == literals[2];
- }
- // DHM (no seconds or fraction)
- internal Boolean FullDHMMatch(TimeSpanFormat.FormatLiterals pattern)
- {
- return SepCount == 4
- && NumCount == 3
- && pattern.Start == literals[0]
- && pattern.DayHourSep == literals[1]
- && pattern.HourMinuteSep == literals[2]
- && pattern.End == literals[3];
- }
- // HMS (no days or fraction)
- internal Boolean FullHMSMatch(TimeSpanFormat.FormatLiterals pattern)
- {
- return SepCount == 4
- && NumCount == 3
- && pattern.Start == literals[0]
- && pattern.HourMinuteSep == literals[1]
- && pattern.MinuteSecondSep == literals[2]
- && pattern.End == literals[3];
- }
- // DHMS (no fraction)
- internal Boolean FullDHMSMatch(TimeSpanFormat.FormatLiterals pattern)
- {
- return SepCount == 5
- && NumCount == 4
- && pattern.Start == literals[0]
- && pattern.DayHourSep == literals[1]
- && pattern.HourMinuteSep == literals[2]
- && pattern.MinuteSecondSep == literals[3]
- && pattern.End == literals[4];
- }
- // HMSF (no days)
- internal Boolean FullHMSFMatch(TimeSpanFormat.FormatLiterals pattern)
- {
- return SepCount == 5
- && NumCount == 4
- && pattern.Start == literals[0]
- && pattern.HourMinuteSep == literals[1]
- && pattern.MinuteSecondSep == literals[2]
- && pattern.SecondFractionSep == literals[3]
- && pattern.End == literals[4];
- }
-
- internal TTT lastSeenTTT;
- internal int tokenCount;
- internal int SepCount;
- internal int NumCount;
- internal String[] literals;
- internal TimeSpanToken[] numbers; // raw numbers
-
- private TimeSpanFormat.FormatLiterals m_posLoc;
- private TimeSpanFormat.FormatLiterals m_negLoc;
- private Boolean m_posLocInit;
- private Boolean m_negLocInit;
- private String m_fullPosPattern;
- private String m_fullNegPattern;
-
- private const int MaxTokens = 11;
- private const int MaxLiteralTokens = 6;
- private const int MaxNumericTokens = 5;
-
- internal void Init(DateTimeFormatInfo dtfi)
- {
- Debug.Assert(dtfi != null);
-
- lastSeenTTT = TTT.None;
- tokenCount = 0;
- SepCount = 0;
- NumCount = 0;
-
- literals = new String[MaxLiteralTokens];
- numbers = new TimeSpanToken[MaxNumericTokens];
-
- m_fullPosPattern = dtfi.FullTimeSpanPositivePattern;
- m_fullNegPattern = dtfi.FullTimeSpanNegativePattern;
- m_posLocInit = false;
- m_negLocInit = false;
- }
-
- internal Boolean ProcessToken(ref TimeSpanToken tok, ref TimeSpanResult result)
- {
- if (tok.ttt == TTT.NumOverflow)
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge", null);
- return false;
- }
- if (tok.ttt != TTT.Sep && tok.ttt != TTT.Num)
- {
- // Some unknown token or a repeat token type in the input
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan", null);
- return false;
- }
-
- switch (tok.ttt)
- {
- case TTT.Sep:
- if (!AddSep(tok.sep, ref result)) return false;
- break;
- case TTT.Num:
- if (tokenCount == 0)
- {
- if (!AddSep(String.Empty, ref result)) return false;
- }
- if (!AddNum(tok, ref result)) return false;
- break;
- default:
- break;
- }
-
- lastSeenTTT = tok.ttt;
- Debug.Assert(tokenCount == (SepCount + NumCount), "tokenCount == (SepCount + NumCount)");
- return true;
- }
-
- private bool AddSep(String sep, ref TimeSpanResult result)
- {
- if (SepCount >= MaxLiteralTokens || tokenCount >= MaxTokens)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan", null);
- return false;
- }
- literals[SepCount++] = sep;
- tokenCount++;
- return true;
- }
- private bool AddNum(TimeSpanToken num, ref TimeSpanResult result)
- {
- if (NumCount >= MaxNumericTokens || tokenCount >= MaxTokens)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan", null);
- return false;
- }
- numbers[NumCount++] = num;
- tokenCount++;
- return true;
- }
- }
-
- // This will store the result of the parsing. And it will eventually be used to construct a TimeSpan instance.
- private struct TimeSpanResult
- {
- internal TimeSpan parsedTimeSpan;
- internal TimeSpanThrowStyle throwStyle;
-
- internal ParseFailureKind m_failure;
- internal string m_failureMessageID;
- internal object m_failureMessageFormatArgument;
- internal string m_failureArgumentName;
-
- internal void Init(TimeSpanThrowStyle canThrow)
- {
- parsedTimeSpan = default(TimeSpan);
- throwStyle = canThrow;
- }
- internal void SetFailure(ParseFailureKind failure, string failureMessageID)
- {
- SetFailure(failure, failureMessageID, null, null);
- }
- internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument)
- {
- SetFailure(failure, failureMessageID, failureMessageFormatArgument, null);
- }
- internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument,
- string failureArgumentName)
- {
- m_failure = failure;
- m_failureMessageID = failureMessageID;
- m_failureMessageFormatArgument = failureMessageFormatArgument;
- m_failureArgumentName = failureArgumentName;
- if (throwStyle != TimeSpanThrowStyle.None)
- {
- throw GetTimeSpanParseException();
- }
- }
-
- internal Exception GetTimeSpanParseException()
- {
- switch (m_failure)
- {
- case ParseFailureKind.ArgumentNull:
- return new ArgumentNullException(m_failureArgumentName, SR.GetResourceString(m_failureMessageID));
-
- case ParseFailureKind.FormatWithParameter:
- return new FormatException(SR.Format(SR.GetResourceString(m_failureMessageID), m_failureMessageFormatArgument));
-
- case ParseFailureKind.Format:
- return new FormatException(SR.GetResourceString(m_failureMessageID));
-
- case ParseFailureKind.Overflow:
- return new OverflowException(SR.GetResourceString(m_failureMessageID));
-
- default:
- Debug.Assert(false, "Unknown TimeSpanParseFailure: " + m_failure);
- return new FormatException(SR.Format_InvalidString);
- }
- }
- }
-
- private static bool TryTimeToTicks(bool positive, TimeSpanToken days, TimeSpanToken hours, TimeSpanToken minutes, TimeSpanToken seconds, TimeSpanToken fraction, out long result)
- {
- if (days.IsInvalidNumber(maxDays, unlimitedDigits)
- || hours.IsInvalidNumber(maxHours, unlimitedDigits)
- || minutes.IsInvalidNumber(maxMinutes, unlimitedDigits)
- || seconds.IsInvalidNumber(maxSeconds, unlimitedDigits)
- || fraction.IsInvalidNumber(maxFraction, maxFractionDigits))
- {
- result = 0;
- return false;
- }
-
- Int64 ticks = ((Int64)days.num * 3600 * 24 + (Int64)hours.num * 3600 + (Int64)minutes.num * 60 + seconds.num) * 1000;
- if (ticks > TimeSpan.MaxMilliSeconds || ticks < TimeSpan.MinMilliSeconds)
- {
- result = 0;
- return false;
- }
-
- // Normalize the fraction component
- //
- // string representation => (zeroes,num) => resultant fraction ticks
- // --------------------- ------------ ------------------------
- // ".9999999" => (0,9999999) => 9,999,999 ticks (same as constant maxFraction)
- // ".1" => (0,1) => 1,000,000 ticks
- // ".01" => (1,1) => 100,000 ticks
- // ".001" => (2,1) => 10,000 ticks
- long f = fraction.num;
- if (f != 0)
- {
- long lowerLimit = TimeSpan.TicksPerTenthSecond;
- if (fraction.zeroes > 0)
- {
- long divisor = (long)Math.Pow(10, fraction.zeroes);
- lowerLimit = lowerLimit / divisor;
- }
- while (f < lowerLimit)
- {
- f *= 10;
- }
- }
- result = ((long)ticks * TimeSpan.TicksPerMillisecond) + f;
- if (positive && result < 0)
- {
- result = 0;
- return false;
- }
- return true;
- }
- #endregion
-
-
- // ---- SECTION: internal static methods called by System.TimeSpan ---------*
- //
- // [Try]Parse, [Try]ParseExact, and [Try]ParseExactMultiple
- //
- // Actions: Main methods called from TimeSpan.Parse
- #region ParseMethods
- internal static TimeSpan Parse(String input, IFormatProvider formatProvider)
- {
- TimeSpanResult parseResult = new TimeSpanResult();
- parseResult.Init(TimeSpanThrowStyle.All);
-
- if (TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult))
- {
- return parseResult.parsedTimeSpan;
- }
- else
- {
- throw parseResult.GetTimeSpanParseException();
- }
- }
- internal static Boolean TryParse(String input, IFormatProvider formatProvider, out TimeSpan result)
- {
- TimeSpanResult parseResult = new TimeSpanResult();
- parseResult.Init(TimeSpanThrowStyle.None);
-
- if (TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult))
- {
- result = parseResult.parsedTimeSpan;
- return true;
- }
- else
- {
- result = default(TimeSpan);
- return false;
- }
- }
- internal static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles)
- {
- TimeSpanResult parseResult = new TimeSpanResult();
- parseResult.Init(TimeSpanThrowStyle.All);
-
- if (TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult))
- {
- return parseResult.parsedTimeSpan;
- }
- else
- {
- throw parseResult.GetTimeSpanParseException();
- }
- }
- internal static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
- {
- TimeSpanResult parseResult = new TimeSpanResult();
- parseResult.Init(TimeSpanThrowStyle.None);
-
- if (TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult))
- {
- result = parseResult.parsedTimeSpan;
- return true;
- }
- else
- {
- result = default(TimeSpan);
- return false;
- }
- }
- internal static TimeSpan ParseExactMultiple(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles)
- {
- TimeSpanResult parseResult = new TimeSpanResult();
- parseResult.Init(TimeSpanThrowStyle.All);
-
- if (TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult))
- {
- return parseResult.parsedTimeSpan;
- }
- else
- {
- throw parseResult.GetTimeSpanParseException();
- }
- }
- internal static Boolean TryParseExactMultiple(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
- {
- TimeSpanResult parseResult = new TimeSpanResult();
- parseResult.Init(TimeSpanThrowStyle.None);
-
- if (TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult))
- {
- result = parseResult.parsedTimeSpan;
- return true;
- }
- else
- {
- result = default(TimeSpan);
- return false;
- }
- }
- #endregion
-
-
- // ---- SECTION: private static methods that do the actual work ---------*
- #region TryParseTimeSpan
- //
- // TryParseTimeSpan
- //
- // Actions: Common private Parse method called by both Parse and TryParse
- //
- private static Boolean TryParseTimeSpan(String input, TimeSpanStandardStyles style, IFormatProvider formatProvider, ref TimeSpanResult result)
- {
- if (input == null)
- {
- result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(input));
- return false;
- }
-
- input = input.Trim();
- if (input == String.Empty)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
-
- TimeSpanTokenizer tokenizer = new TimeSpanTokenizer();
- tokenizer.Init(input);
-
- TimeSpanRawInfo raw = new TimeSpanRawInfo();
- raw.Init(DateTimeFormatInfo.GetInstance(formatProvider));
-
- TimeSpanToken tok = tokenizer.GetNextToken();
-
- /* The following loop will break out when we reach the end of the str or
- * when we can determine that the input is invalid. */
- while (tok.ttt != TTT.End)
- {
- if (!raw.ProcessToken(ref tok, ref result))
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
- tok = tokenizer.GetNextToken();
- }
- if (!tokenizer.EOL)
- {
- // embedded nulls in the input string
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
- if (!ProcessTerminalState(ref raw, style, ref result))
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
- return true;
- }
-
-
-
- //
- // ProcessTerminalState
- //
- // Actions: Validate the terminal state of a standard format parse.
- // Sets result.parsedTimeSpan on success.
- //
- // Calculates the resultant TimeSpan from the TimeSpanRawInfo
- //
- // try => +InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern
- // 1) Verify Start matches
- // 2) Verify End matches
- // 3) 1 number => d
- // 2 numbers => h:m
- // 3 numbers => h:m:s | d.h:m | h:m:.f
- // 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f
- // 5 numbers => d.h:m:s.f
- private static Boolean ProcessTerminalState(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
- {
- if (raw.lastSeenTTT == TTT.Num)
- {
- TimeSpanToken tok = new TimeSpanToken();
- tok.ttt = TTT.Sep;
- tok.sep = String.Empty;
- if (!raw.ProcessToken(ref tok, ref result))
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
- }
-
- switch (raw.NumCount)
- {
- case 1:
- return ProcessTerminal_D(ref raw, style, ref result);
- case 2:
- return ProcessTerminal_HM(ref raw, style, ref result);
- case 3:
- return ProcessTerminal_HM_S_D(ref raw, style, ref result);
- case 4:
- return ProcessTerminal_HMS_F_D(ref raw, style, ref result);
- case 5:
- return ProcessTerminal_DHMSF(ref raw, style, ref result);
- default:
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
- }
-
- //
- // ProcessTerminal_DHMSF
- //
- // Actions: Validate the 5-number "Days.Hours:Minutes:Seconds.Fraction" terminal case.
- // Sets result.parsedTimeSpan on success.
- //
- private static Boolean ProcessTerminal_DHMSF(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
- {
- if (raw.SepCount != 6 || raw.NumCount != 5)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
-
- bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0);
- bool loc = ((style & TimeSpanStandardStyles.Localized) != 0);
-
- bool positive = false;
- bool match = false;
-
- if (inv)
- {
- if (raw.FullMatch(raw.PositiveInvariant))
- {
- match = true;
- positive = true;
- }
- if (!match && raw.FullMatch(raw.NegativeInvariant))
- {
- match = true;
- positive = false;
- }
- }
- if (loc)
- {
- if (!match && raw.FullMatch(raw.PositiveLocalized))
- {
- match = true;
- positive = true;
- }
- if (!match && raw.FullMatch(raw.NegativeLocalized))
- {
- match = true;
- positive = false;
- }
- }
- long ticks;
- if (match)
- {
- if (!TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], raw.numbers[4], out ticks))
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- if (!positive)
- {
- ticks = -ticks;
- if (ticks > 0)
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- }
- result.parsedTimeSpan._ticks = ticks;
- return true;
- }
-
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
-
- //
- // ProcessTerminal_HMS_F_D
- //
- // Actions: Validate the ambiguous 4-number "Hours:Minutes:Seconds.Fraction", "Days.Hours:Minutes:Seconds", or "Days.Hours:Minutes:.Fraction" terminal case.
- // Sets result.parsedTimeSpan on success.
- //
- private static Boolean ProcessTerminal_HMS_F_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
- {
- if (raw.SepCount != 5 || raw.NumCount != 4 || (style & TimeSpanStandardStyles.RequireFull) != 0)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
-
- bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0);
- bool loc = ((style & TimeSpanStandardStyles.Localized) != 0);
-
- long ticks = 0;
- bool positive = false;
- bool match = false;
- bool overflow = false;
-
- if (inv)
- {
- if (raw.FullHMSFMatch(raw.PositiveInvariant))
- {
- positive = true;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullDHMSMatch(raw.PositiveInvariant))
- {
- positive = true;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullAppCompatMatch(raw.PositiveInvariant))
- {
- positive = true;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, raw.numbers[3], out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullHMSFMatch(raw.NegativeInvariant))
- {
- positive = false;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullDHMSMatch(raw.NegativeInvariant))
- {
- positive = false;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullAppCompatMatch(raw.NegativeInvariant))
- {
- positive = false;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, raw.numbers[3], out ticks);
- overflow = overflow || !match;
- }
- }
- if (loc)
- {
- if (!match && raw.FullHMSFMatch(raw.PositiveLocalized))
- {
- positive = true;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullDHMSMatch(raw.PositiveLocalized))
- {
- positive = true;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullAppCompatMatch(raw.PositiveLocalized))
- {
- positive = true;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, raw.numbers[3], out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullHMSFMatch(raw.NegativeLocalized))
- {
- positive = false;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullDHMSMatch(raw.NegativeLocalized))
- {
- positive = false;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], raw.numbers[3], zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullAppCompatMatch(raw.NegativeLocalized))
- {
- positive = false;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, raw.numbers[3], out ticks);
- overflow = overflow || !match;
- }
- }
-
- if (match)
- {
- if (!positive)
- {
- ticks = -ticks;
- if (ticks > 0)
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- }
- result.parsedTimeSpan._ticks = ticks;
- return true;
- }
-
- if (overflow)
- {
- // we found at least one literal pattern match but the numbers just didn't fit
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- else
- {
- // we couldn't find a thing
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
- }
-
- //
- // ProcessTerminal_HM_S_D
- //
- // Actions: Validate the ambiguous 3-number "Hours:Minutes:Seconds", "Days.Hours:Minutes", or "Hours:Minutes:.Fraction" terminal case
- //
- private static Boolean ProcessTerminal_HM_S_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
- {
- if (raw.SepCount != 4 || raw.NumCount != 3 || (style & TimeSpanStandardStyles.RequireFull) != 0)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
-
- bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0);
- bool loc = ((style & TimeSpanStandardStyles.Localized) != 0);
-
- bool positive = false;
- bool match = false;
- bool overflow = false;
-
- long ticks = 0;
-
- if (inv)
- {
- if (raw.FullHMSMatch(raw.PositiveInvariant))
- {
- positive = true;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullDHMMatch(raw.PositiveInvariant))
- {
- positive = true;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.PartialAppCompatMatch(raw.PositiveInvariant))
- {
- positive = true;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], zero, raw.numbers[2], out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullHMSMatch(raw.NegativeInvariant))
- {
- positive = false;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullDHMMatch(raw.NegativeInvariant))
- {
- positive = false;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.PartialAppCompatMatch(raw.NegativeInvariant))
- {
- positive = false;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], zero, raw.numbers[2], out ticks);
- overflow = overflow || !match;
- }
- }
- if (loc)
- {
- if (!match && raw.FullHMSMatch(raw.PositiveLocalized))
- {
- positive = true;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullDHMMatch(raw.PositiveLocalized))
- {
- positive = true;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.PartialAppCompatMatch(raw.PositiveLocalized))
- {
- positive = true;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], zero, raw.numbers[2], out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullHMSMatch(raw.NegativeLocalized))
- {
- positive = false;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.FullDHMMatch(raw.NegativeLocalized))
- {
- positive = false;
- match = TryTimeToTicks(positive, raw.numbers[0], raw.numbers[1], raw.numbers[2], zero, zero, out ticks);
- overflow = overflow || !match;
- }
- if (!match && raw.PartialAppCompatMatch(raw.NegativeLocalized))
- {
- positive = false;
- match = TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], zero, raw.numbers[2], out ticks);
- overflow = overflow || !match;
- }
- }
-
- if (match)
- {
- if (!positive)
- {
- ticks = -ticks;
- if (ticks > 0)
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- }
- result.parsedTimeSpan._ticks = ticks;
- return true;
- }
-
- if (overflow)
- {
- // we found at least one literal pattern match but the numbers just didn't fit
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- else
- {
- // we couldn't find a thing
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
- }
-
- //
- // ProcessTerminal_HM
- //
- // Actions: Validate the 2-number "Hours:Minutes" terminal case
- //
- private static Boolean ProcessTerminal_HM(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
- {
- if (raw.SepCount != 3 || raw.NumCount != 2 || (style & TimeSpanStandardStyles.RequireFull) != 0)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
-
- bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0);
- bool loc = ((style & TimeSpanStandardStyles.Localized) != 0);
-
- bool positive = false;
- bool match = false;
-
- if (inv)
- {
- if (raw.FullHMMatch(raw.PositiveInvariant))
- {
- match = true;
- positive = true;
- }
- if (!match && raw.FullHMMatch(raw.NegativeInvariant))
- {
- match = true;
- positive = false;
- }
- }
- if (loc)
- {
- if (!match && raw.FullHMMatch(raw.PositiveLocalized))
- {
- match = true;
- positive = true;
- }
- if (!match && raw.FullHMMatch(raw.NegativeLocalized))
- {
- match = true;
- positive = false;
- }
- }
-
- long ticks = 0;
- if (match)
- {
- if (!TryTimeToTicks(positive, zero, raw.numbers[0], raw.numbers[1], zero, zero, out ticks))
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- if (!positive)
- {
- ticks = -ticks;
- if (ticks > 0)
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- }
- result.parsedTimeSpan._ticks = ticks;
- return true;
- }
-
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
-
-
- //
- // ProcessTerminal_D
- //
- // Actions: Validate the 1-number "Days" terminal case
- //
- private static Boolean ProcessTerminal_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result)
- {
- if (raw.SepCount != 2 || raw.NumCount != 1 || (style & TimeSpanStandardStyles.RequireFull) != 0)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
-
- bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0);
- bool loc = ((style & TimeSpanStandardStyles.Localized) != 0);
-
- bool positive = false;
- bool match = false;
-
- if (inv)
- {
- if (raw.FullDMatch(raw.PositiveInvariant))
- {
- match = true;
- positive = true;
- }
- if (!match && raw.FullDMatch(raw.NegativeInvariant))
- {
- match = true;
- positive = false;
- }
- }
- if (loc)
- {
- if (!match && raw.FullDMatch(raw.PositiveLocalized))
- {
- match = true;
- positive = true;
- }
- if (!match && raw.FullDMatch(raw.NegativeLocalized))
- {
- match = true;
- positive = false;
- }
- }
-
- long ticks = 0;
- if (match)
- {
- if (!TryTimeToTicks(positive, raw.numbers[0], zero, zero, zero, zero, out ticks))
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- if (!positive)
- {
- ticks = -ticks;
- if (ticks > 0)
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- }
- result.parsedTimeSpan._ticks = ticks;
- return true;
- }
-
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
- #endregion
-
- #region TryParseExactTimeSpan
- //
- // TryParseExactTimeSpan
- //
- // Actions: Common private ParseExact method called by both ParseExact and TryParseExact
- //
- private static Boolean TryParseExactTimeSpan(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result)
- {
- if (input == null)
- {
- result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(input));
- return false;
- }
- if (format == null)
- {
- result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(format));
- return false;
- }
- if (format.Length == 0)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier");
- return false;
- }
-
- if (format.Length == 1)
- {
- TimeSpanStandardStyles style = TimeSpanStandardStyles.None;
-
- if (format[0] == 'c' || format[0] == 't' || format[0] == 'T')
- {
- // fast path for legacy style TimeSpan formats.
- return TryParseTimeSpanConstant(input, ref result);
- }
- else if (format[0] == 'g')
- {
- style = TimeSpanStandardStyles.Localized;
- }
- else if (format[0] == 'G')
- {
- style = TimeSpanStandardStyles.Localized | TimeSpanStandardStyles.RequireFull;
- }
- else
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier");
- return false;
- }
- return TryParseTimeSpan(input, style, formatProvider, ref result);
- }
-
- return TryParseByFormat(input, format, styles, ref result);
- }
-
- //
- // TryParseByFormat
- //
- // Actions: Parse the TimeSpan instance using the specified format. Used by TryParseExactTimeSpan.
- //
- private static Boolean TryParseByFormat(String input, String format, TimeSpanStyles styles, ref TimeSpanResult result)
- {
- Debug.Assert(input != null, "input != null");
- Debug.Assert(format != null, "format != null");
-
- bool seenDD = false; // already processed days?
- bool seenHH = false; // already processed hours?
- bool seenMM = false; // already processed minutes?
- bool seenSS = false; // already processed seconds?
- bool seenFF = false; // already processed fraction?
- int dd = 0; // parsed days
- int hh = 0; // parsed hours
- int mm = 0; // parsed minutes
- int ss = 0; // parsed seconds
- int leadingZeroes = 0; // number of leading zeroes in the parsed fraction
- int ff = 0; // parsed fraction
- int i = 0; // format string position
- int tokenLen = 0; // length of current format token, used to update index 'i'
-
- TimeSpanTokenizer tokenizer = new TimeSpanTokenizer();
- tokenizer.Init(input, -1);
-
- while (i < format.Length)
- {
- char ch = format[i];
- int nextFormatChar;
- switch (ch)
- {
- case 'h':
- tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
- if (tokenLen > 2 || seenHH || !ParseExactDigits(ref tokenizer, tokenLen, out hh))
- {
- result.SetFailure(ParseFailureKind.Format, "Format_InvalidString");
- return false;
- }
- seenHH = true;
- break;
- case 'm':
- tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
- if (tokenLen > 2 || seenMM || !ParseExactDigits(ref tokenizer, tokenLen, out mm))
- {
- result.SetFailure(ParseFailureKind.Format, "Format_InvalidString");
- return false;
- }
- seenMM = true;
- break;
- case 's':
- tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
- if (tokenLen > 2 || seenSS || !ParseExactDigits(ref tokenizer, tokenLen, out ss))
- {
- result.SetFailure(ParseFailureKind.Format, "Format_InvalidString");
- return false;
- }
- seenSS = true;
- break;
- case 'f':
- tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
- if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF || !ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff))
- {
- result.SetFailure(ParseFailureKind.Format, "Format_InvalidString");
- return false;
- }
- seenFF = true;
- break;
- case 'F':
- tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
- if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_InvalidString");
- return false;
- }
- ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff);
- seenFF = true;
- break;
- case 'd':
- tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
- int tmp = 0;
- if (tokenLen > 8 || seenDD || !ParseExactDigits(ref tokenizer, (tokenLen < 2) ? 1 : tokenLen, (tokenLen < 2) ? 8 : tokenLen, out tmp, out dd))
- {
- result.SetFailure(ParseFailureKind.Format, "Format_InvalidString");
- return false;
- }
- seenDD = true;
- break;
- case '\'':
- case '\"':
- StringBuilder enquotedString = new StringBuilder();
- if (!DateTimeParse.TryParseQuoteString(format, i, enquotedString, out tokenLen))
- {
- result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadQuote", ch);
- return false;
- }
- if (!ParseExactLiteral(ref tokenizer, enquotedString))
- {
- result.SetFailure(ParseFailureKind.Format, "Format_InvalidString");
- return false;
- }
- break;
- case '%':
- // Optional format character.
- // For example, format string "%d" will print day
- // Most of the cases, "%" can be ignored.
- nextFormatChar = DateTimeFormat.ParseNextChar(format, i);
- // nextFormatChar will be -1 if we already reach the end of the format string.
- // Besides, we will not allow "%%" appear in the pattern.
- if (nextFormatChar >= 0 && nextFormatChar != (int)'%')
- {
- tokenLen = 1; // skip the '%' and process the format character
- break;
- }
- else
- {
- // This means that '%' is at the end of the format string or
- // "%%" appears in the format string.
- result.SetFailure(ParseFailureKind.Format, "Format_InvalidString");
- return false;
- }
- case '\\':
- // Escaped character. Can be used to insert character into the format string.
- // For example, "\d" will insert the character 'd' into the string.
- //
- nextFormatChar = DateTimeFormat.ParseNextChar(format, i);
- if (nextFormatChar >= 0 && tokenizer.NextChar == (char)nextFormatChar)
- {
- tokenLen = 2;
- }
- else
- {
- // This means that '\' is at the end of the format string or the literal match failed.
- result.SetFailure(ParseFailureKind.Format, "Format_InvalidString");
- return false;
- }
- break;
- default:
- result.SetFailure(ParseFailureKind.Format, "Format_InvalidString");
- return false;
- }
- i += tokenLen;
- }
-
-
- if (!tokenizer.EOL)
- {
- // the custom format didn't consume the entire input
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
-
- long ticks = 0;
- bool positive = (styles & TimeSpanStyles.AssumeNegative) == 0;
- if (TryTimeToTicks(positive, new TimeSpanToken(dd),
- new TimeSpanToken(hh),
- new TimeSpanToken(mm),
- new TimeSpanToken(ss),
- new TimeSpanToken(leadingZeroes, ff),
- out ticks))
- {
- if (!positive) ticks = -ticks;
- result.parsedTimeSpan._ticks = ticks;
- return true;
- }
- else
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- }
-
- private static Boolean ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, out int result)
- {
- result = 0;
- int zeroes = 0;
- int maxDigitLength = (minDigitLength == 1) ? 2 : minDigitLength;
- return ParseExactDigits(ref tokenizer, minDigitLength, maxDigitLength, out zeroes, out result);
- }
- private static Boolean ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, int maxDigitLength, out int zeroes, out int result)
- {
- result = 0;
- zeroes = 0;
-
- int tokenLength = 0;
- while (tokenLength < maxDigitLength)
- {
- char ch = tokenizer.NextChar;
- if (ch < '0' || ch > '9')
- {
- tokenizer.BackOne();
- break;
- }
- result = result * 10 + (ch - '0');
- if (result == 0) zeroes++;
- tokenLength++;
- }
- return (tokenLength >= minDigitLength);
- }
- private static Boolean ParseExactLiteral(ref TimeSpanTokenizer tokenizer, StringBuilder enquotedString)
- {
- for (int i = 0; i < enquotedString.Length; i++)
- {
- if (enquotedString[i] != tokenizer.NextChar)
- return false;
- }
- return true;
- }
- #endregion
-
- #region TryParseTimeSpanConstant
- //
- // TryParseTimeSpanConstant
- //
- // Actions: Parses the "c" (constant) format. This code is 100% identical to the non-globalized v1.0-v3.5 TimeSpan.Parse() routine
- // and exists for performance/appcompat with legacy callers who cannot move onto the globalized Parse overloads.
- //
- private static Boolean TryParseTimeSpanConstant(String input, ref TimeSpanResult result)
- {
- return (new StringParser().TryParse(input, ref result));
- }
-
- private struct StringParser
- {
- private String str;
- private char ch;
- private int pos;
- private int len;
-
- internal void NextChar()
- {
- if (pos < len) pos++;
- ch = pos < len ? str[pos] : (char)0;
- }
-
- internal char NextNonDigit()
- {
- int i = pos;
- while (i < len)
- {
- char ch = str[i];
- if (ch < '0' || ch > '9') return ch;
- i++;
- }
- return (char)0;
- }
-
- internal bool TryParse(String input, ref TimeSpanResult result)
- {
- result.parsedTimeSpan._ticks = 0;
-
- if (input == null)
- {
- result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(input));
- return false;
- }
- str = input;
- len = input.Length;
- pos = -1;
- NextChar();
- SkipBlanks();
- bool negative = false;
- if (ch == '-')
- {
- negative = true;
- NextChar();
- }
- long time;
- if (NextNonDigit() == ':')
- {
- if (!ParseTime(out time, ref result))
- {
- return false;
- };
- }
- else
- {
- int days;
- if (!ParseInt((int)(0x7FFFFFFFFFFFFFFFL / TimeSpan.TicksPerDay), out days, ref result))
- {
- return false;
- }
- time = days * TimeSpan.TicksPerDay;
- if (ch == '.')
- {
- NextChar();
- long remainingTime;
- if (!ParseTime(out remainingTime, ref result))
- {
- return false;
- };
- time += remainingTime;
- }
- }
- if (negative)
- {
- time = -time;
- // Allow -0 as well
- if (time > 0)
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- }
- else
- {
- if (time < 0)
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- }
- SkipBlanks();
- if (pos < len)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
- result.parsedTimeSpan._ticks = time;
- return true;
- }
-
- internal bool ParseInt(int max, out int i, ref TimeSpanResult result)
- {
- i = 0;
- int p = pos;
- while (ch >= '0' && ch <= '9')
- {
- if ((i & 0xF0000000) != 0)
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- i = i * 10 + ch - '0';
- if (i < 0)
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- NextChar();
- }
- if (p == pos)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
- if (i > max)
- {
- result.SetFailure(ParseFailureKind.Overflow, "Overflow_TimeSpanElementTooLarge");
- return false;
- }
- return true;
- }
-
- internal bool ParseTime(out long time, ref TimeSpanResult result)
- {
- time = 0;
- int unit;
- if (!ParseInt(23, out unit, ref result))
- {
- return false;
- }
- time = unit * TimeSpan.TicksPerHour;
- if (ch != ':')
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
- NextChar();
- if (!ParseInt(59, out unit, ref result))
- {
- return false;
- }
- time += unit * TimeSpan.TicksPerMinute;
- if (ch == ':')
- {
- NextChar();
- // allow seconds with the leading zero
- if (ch != '.')
- {
- if (!ParseInt(59, out unit, ref result))
- {
- return false;
- }
- time += unit * TimeSpan.TicksPerSecond;
- }
- if (ch == '.')
- {
- NextChar();
- int f = (int)TimeSpan.TicksPerSecond;
- while (f > 1 && ch >= '0' && ch <= '9')
- {
- f /= 10;
- time += (ch - '0') * f;
- NextChar();
- }
- }
- }
- return true;
- }
-
- internal void SkipBlanks()
- {
- while (ch == ' ' || ch == '\t') NextChar();
- }
- }
- #endregion
-
- #region TryParseExactMultipleTimeSpan
- //
- // TryParseExactMultipleTimeSpan
- //
- // Actions: Common private ParseExactMultiple method called by both ParseExactMultiple and TryParseExactMultiple
- //
- private static Boolean TryParseExactMultipleTimeSpan(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result)
- {
- if (input == null)
- {
- result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(input));
- return false;
- }
- if (formats == null)
- {
- result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(formats));
- return false;
- }
-
- if (input.Length == 0)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return false;
- }
-
- if (formats.Length == 0)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier");
- return false;
- }
-
- //
- // Do a loop through the provided formats and see if we can parse succesfully in
- // one of the formats.
- //
- for (int i = 0; i < formats.Length; i++)
- {
- if (formats[i] == null || formats[i].Length == 0)
- {
- result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier");
- return false;
- }
-
- // Create a new non-throwing result each time to ensure the runs are independent.
- TimeSpanResult innerResult = new TimeSpanResult();
- innerResult.Init(TimeSpanThrowStyle.None);
-
- if (TryParseExactTimeSpan(input, formats[i], formatProvider, styles, ref innerResult))
- {
- result.parsedTimeSpan = innerResult.parsedTimeSpan;
- return true;
- }
- }
-
- result.SetFailure(ParseFailureKind.Format, "Format_BadTimeSpan");
- return (false);
- }
- #endregion
- }
-}
diff --git a/src/mscorlib/src/System/Guid.CoreCLR.cs b/src/mscorlib/src/System/Guid.CoreCLR.cs
new file mode 100644
index 0000000000..f71d71b145
--- /dev/null
+++ b/src/mscorlib/src/System/Guid.CoreCLR.cs
@@ -0,0 +1,27 @@
+// 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 Microsoft.Win32;
+using System.Runtime.InteropServices;
+using System.Diagnostics.Contracts;
+
+namespace System
+{
+ partial struct Guid
+ {
+ // This will create a new guid. Since we've now decided that constructors should 0-init,
+ // we need a method that allows users to create a guid.
+ public static Guid NewGuid()
+ {
+ // CoCreateGuid should never return Guid.Empty, since it attempts to maintain some
+ // uniqueness guarantees. It should also never return a known GUID, but it's unclear
+ // how extensively it checks for known values.
+ Contract.Ensures(Contract.Result<Guid>() != Guid.Empty);
+
+ Guid guid;
+ Marshal.ThrowExceptionForHR(Win32Native.CoCreateGuid(out guid), new IntPtr(-1));
+ return guid;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/HResults.cs b/src/mscorlib/src/System/HResults.cs
deleted file mode 100644
index d733ad4b21..0000000000
--- a/src/mscorlib/src/System/HResults.cs
+++ /dev/null
@@ -1,235 +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.
-
-//=============================================================================
-//
-//
-// Purpose: Define HResult constants. Every exception has one of these.
-//
-//
-//===========================================================================*/
-
-using System;
-
-namespace System
-{
- // Note: FACILITY_URT is defined as 0x13 (0x8013xxxx). Within that
- // range, 0x1yyy is for Runtime errors (used for Security, Metadata, etc).
- // In that subrange, 0x15zz and 0x16zz have been allocated for classlib-type
- // HResults. Also note that some of our HResults have to map to certain
- // COM HR's, etc.
-
- // Another arbitrary decision... Feel free to change this, as long as you
- // renumber the HResults yourself (and update rexcep.h).
- // Reflection will use 0x1600 -> 0x161f. IO will use 0x1620 -> 0x163f.
- // Security will use 0x1640 -> 0x165f
-
- // There are HResults files in the IO, Remoting, Reflection &
- // Security/Util directories as well, so choose your HResults carefully.
- internal static class HResults
- {
- internal const int APPMODEL_ERROR_NO_PACKAGE = unchecked((int)0x80073D54);
- internal const int CLDB_E_FILE_CORRUPT = unchecked((int)0x8013110e);
- internal const int CLDB_E_FILE_OLDVER = unchecked((int)0x80131107);
- internal const int CLDB_E_INDEX_NOTFOUND = unchecked((int)0x80131124);
- internal const int CLR_E_BIND_ASSEMBLY_NOT_FOUND = unchecked((int)0x80132004);
- internal const int CLR_E_BIND_ASSEMBLY_PUBLIC_KEY_MISMATCH = unchecked((int)0x80132001);
- internal const int CLR_E_BIND_ASSEMBLY_VERSION_TOO_LOW = unchecked((int)0x80132000);
- internal const int CLR_E_BIND_TYPE_NOT_FOUND = unchecked((int)0x80132005);
- internal const int CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT = unchecked((int)0x80132003);
- internal const int COR_E_ABANDONEDMUTEX = unchecked((int)0x8013152D);
- internal const int COR_E_AMBIGUOUSMATCH = unchecked((int)0x8000211D);
- internal const int COR_E_APPDOMAINUNLOADED = unchecked((int)0x80131014);
- internal const int COR_E_APPLICATION = unchecked((int)0x80131600);
- internal const int COR_E_ARGUMENT = unchecked((int)0x80070057);
- internal const int COR_E_ARGUMENTOUTOFRANGE = unchecked((int)0x80131502);
- internal const int COR_E_ARITHMETIC = unchecked((int)0x80070216);
- internal const int COR_E_ARRAYTYPEMISMATCH = unchecked((int)0x80131503);
- internal const int COR_E_ASSEMBLYEXPECTED = unchecked((int)0x80131018);
- internal const int COR_E_BADIMAGEFORMAT = unchecked((int)0x8007000B);
- internal const int COR_E_CANNOTUNLOADAPPDOMAIN = unchecked((int)0x80131015);
- internal const int COR_E_CODECONTRACTFAILED = unchecked((int)0x80131542);
- internal const int COR_E_CONTEXTMARSHAL = unchecked((int)0x80131504);
- internal const int COR_E_CUSTOMATTRIBUTEFORMAT = unchecked((int)0x80131605);
- internal const int COR_E_DATAMISALIGNED = unchecked((int)0x80131541);
- internal const int COR_E_DIVIDEBYZERO = unchecked((int)0x80020012); // DISP_E_DIVBYZERO
- internal const int COR_E_DLLNOTFOUND = unchecked((int)0x80131524);
- internal const int COR_E_DUPLICATEWAITOBJECT = unchecked((int)0x80131529);
- internal const int COR_E_ENTRYPOINTNOTFOUND = unchecked((int)0x80131523);
- internal const int COR_E_EXCEPTION = unchecked((int)0x80131500);
- internal const int COR_E_EXECUTIONENGINE = unchecked((int)0x80131506);
- internal const int COR_E_FIELDACCESS = unchecked((int)0x80131507);
- internal const int COR_E_FIXUPSINEXE = unchecked((int)0x80131019);
- internal const int COR_E_FORMAT = unchecked((int)0x80131537);
- internal const int COR_E_INDEXOUTOFRANGE = unchecked((int)0x80131508);
- internal const int COR_E_INSUFFICIENTEXECUTIONSTACK = unchecked((int)0x80131578);
- internal const int COR_E_INVALIDCAST = unchecked((int)0x80004002);
- internal const int COR_E_INVALIDCOMOBJECT = unchecked((int)0x80131527);
- internal const int COR_E_INVALIDFILTERCRITERIA = unchecked((int)0x80131601);
- internal const int COR_E_INVALIDOLEVARIANTTYPE = unchecked((int)0x80131531);
- internal const int COR_E_INVALIDOPERATION = unchecked((int)0x80131509);
- internal const int COR_E_INVALIDPROGRAM = unchecked((int)0x8013153a);
- internal const int COR_E_KEYNOTFOUND = unchecked((int)0x80131577);
- internal const int COR_E_MARSHALDIRECTIVE = unchecked((int)0x80131535);
- internal const int COR_E_MEMBERACCESS = unchecked((int)0x8013151A);
- internal const int COR_E_METHODACCESS = unchecked((int)0x80131510);
- internal const int COR_E_MISSINGFIELD = unchecked((int)0x80131511);
- internal const int COR_E_MISSINGMANIFESTRESOURCE = unchecked((int)0x80131532);
- internal const int COR_E_MISSINGMEMBER = unchecked((int)0x80131512);
- internal const int COR_E_MISSINGMETHOD = unchecked((int)0x80131513);
- internal const int COR_E_MISSINGSATELLITEASSEMBLY = unchecked((int)0x80131536);
- internal const int COR_E_MODULE_HASH_CHECK_FAILED = unchecked((int)0x80131039);
- internal const int COR_E_MULTICASTNOTSUPPORTED = unchecked((int)0x80131514);
- internal const int COR_E_NEWER_RUNTIME = unchecked((int)0x8013101b);
- internal const int COR_E_NOTFINITENUMBER = unchecked((int)0x80131528);
- internal const int COR_E_NOTSUPPORTED = unchecked((int)0x80131515);
- internal const int COR_E_NULLREFERENCE = unchecked((int)0x80004003);
- internal const int COR_E_OBJECTDISPOSED = unchecked((int)0x80131622);
- internal const int COR_E_OPERATIONCANCELED = unchecked((int)0x8013153B);
- internal const int COR_E_OUTOFMEMORY = unchecked((int)0x8007000E);
- internal const int COR_E_OVERFLOW = unchecked((int)0x80131516);
- internal const int COR_E_PLATFORMNOTSUPPORTED = unchecked((int)0x80131539);
- internal const int COR_E_RANK = unchecked((int)0x80131517);
- internal const int COR_E_REFLECTIONTYPELOAD = unchecked((int)0x80131602);
- internal const int COR_E_REMOTING = unchecked((int)0x8013150b);
- internal const int COR_E_RUNTIMEWRAPPED = unchecked((int)0x8013153e);
- internal const int COR_E_SAFEARRAYRANKMISMATCH = unchecked((int)0x80131538);
- internal const int COR_E_SAFEARRAYTYPEMISMATCH = unchecked((int)0x80131533);
- internal const int COR_E_SECURITY = unchecked((int)0x8013150A);
- internal const int COR_E_SERIALIZATION = unchecked((int)0x8013150C);
- internal const int COR_E_SERVER = unchecked((int)0x8013150e);
- internal const int COR_E_STACKOVERFLOW = unchecked((int)0x800703E9);
- internal const int COR_E_SYNCHRONIZATIONLOCK = unchecked((int)0x80131518);
- internal const int COR_E_SYSTEM = unchecked((int)0x80131501);
- internal const int COR_E_TARGET = unchecked((int)0x80131603);
- internal const int COR_E_TARGETINVOCATION = unchecked((int)0x80131604);
- internal const int COR_E_TARGETPARAMCOUNT = unchecked((int)0x8002000e);
- internal const int COR_E_THREADABORTED = unchecked((int)0x80131530);
- internal const int COR_E_THREADINTERRUPTED = unchecked((int)0x80131519);
- internal const int COR_E_THREADSTART = unchecked((int)0x80131525);
- internal const int COR_E_THREADSTATE = unchecked((int)0x80131520);
- internal const int COR_E_TIMEOUT = unchecked((int)0x80131505);
- internal const int COR_E_TYPEACCESS = unchecked((int)0x80131543);
- internal const int COR_E_TYPEINITIALIZATION = unchecked((int)0x80131534);
- internal const int COR_E_TYPELOAD = unchecked((int)0x80131522);
- internal const int COR_E_TYPEUNLOADED = unchecked((int)0x80131013);
- internal const int COR_E_UNAUTHORIZEDACCESS = unchecked((int)0x80070005);
- internal const int COR_E_VERIFICATION = unchecked((int)0x8013150D);
- internal const int COR_E_WAITHANDLECANNOTBEOPENED = unchecked((int)0x8013152C);
- internal const int CORSEC_E_CRYPTO = unchecked((int)0x80131430);
- internal const int CORSEC_E_CRYPTO_UNEX_OPER = unchecked((int)0x80131431);
- internal const int CORSEC_E_INVALID_IMAGE_FORMAT = unchecked((int)0x8013141d);
- internal const int CORSEC_E_INVALID_PUBLICKEY = unchecked((int)0x8013141e);
- internal const int CORSEC_E_INVALID_STRONGNAME = unchecked((int)0x8013141a);
- internal const int CORSEC_E_MIN_GRANT_FAIL = unchecked((int)0x80131417);
- internal const int CORSEC_E_MISSING_STRONGNAME = unchecked((int)0x8013141b);
- internal const int CORSEC_E_NO_EXEC_PERM = unchecked((int)0x80131418);
- internal const int CORSEC_E_POLICY_EXCEPTION = unchecked((int)0x80131416);
- internal const int CORSEC_E_SIGNATURE_MISMATCH = unchecked((int)0x80131420);
- internal const int CORSEC_E_XMLSYNTAX = unchecked((int)0x80131419);
- internal const int CTL_E_DEVICEIOERROR = unchecked((int)0x800A0039);
- internal const int CTL_E_DIVISIONBYZERO = unchecked((int)0x800A000B);
- internal const int CTL_E_FILENOTFOUND = unchecked((int)0x800A0035);
- internal const int CTL_E_OUTOFMEMORY = unchecked((int)0x800A0007);
- internal const int CTL_E_OUTOFSTACKSPACE = unchecked((int)0x800A001C);
- internal const int CTL_E_OVERFLOW = unchecked((int)0x800A0006);
- internal const int CTL_E_PATHFILEACCESSERROR = unchecked((int)0x800A004B);
- internal const int CTL_E_PATHNOTFOUND = unchecked((int)0x800A004C);
- internal const int CTL_E_PERMISSIONDENIED = unchecked((int)0x800A0046);
- internal const int E_ELEMENTNOTAVAILABLE = unchecked((int)0x802B001F);
- internal const int E_ELEMENTNOTENABLED = unchecked((int)0x802B001E);
- internal const int E_FAIL = unchecked((int)0x80004005);
- internal const int E_HANDLE = unchecked((int)0x80070006);
- internal const int E_ILLEGAL_DELEGATE_ASSIGNMENT = unchecked((int)0x80000018);
- internal const int E_ILLEGAL_METHOD_CALL = unchecked((int)0x8000000E);
- internal const int E_ILLEGAL_STATE_CHANGE = unchecked((int)0x8000000D);
- internal const int E_INVALIDARG = unchecked((int)0x80070057);
- internal const int E_LAYOUTCYCLE = unchecked((int)0x802B0014);
- internal const int E_NOTIMPL = unchecked((int)0x80004001);
- internal const int E_OUTOFMEMORY = unchecked((int)0x8007000E);
- internal const int E_POINTER = unchecked((int)0x80004003L);
- internal const int E_XAMLPARSEFAILED = unchecked((int)0x802B000A);
- internal const int ERROR_BAD_EXE_FORMAT = unchecked((int)0x800700C1);
- internal const int ERROR_BAD_NET_NAME = unchecked((int)0x80070043);
- internal const int ERROR_BAD_NETPATH = unchecked((int)0x80070035);
- internal const int ERROR_DISK_CORRUPT = unchecked((int)0x80070571);
- internal const int ERROR_DLL_INIT_FAILED = unchecked((int)0x8007045A);
- internal const int ERROR_DLL_NOT_FOUND = unchecked((int)0x80070485);
- internal const int ERROR_EXE_MARKED_INVALID = unchecked((int)0x800700C0);
- internal const int ERROR_FILE_CORRUPT = unchecked((int)0x80070570);
- internal const int ERROR_FILE_INVALID = unchecked((int)0x800703EE);
- internal const int ERROR_FILE_NOT_FOUND = unchecked((int)0x80070002);
- internal const int ERROR_INVALID_DLL = unchecked((int)0x80070482);
- internal const int ERROR_INVALID_NAME = unchecked((int)0x8007007B);
- internal const int ERROR_INVALID_ORDINAL = unchecked((int)0x800700B6);
- internal const int ERROR_INVALID_PARAMETER = unchecked((int)0x80070057);
- internal const int ERROR_LOCK_VIOLATION = unchecked((int)0x80070021);
- internal const int ERROR_MOD_NOT_FOUND = unchecked((int)0x8007007E);
- internal const int ERROR_NO_UNICODE_TRANSLATION = unchecked((int)0x80070459);
- internal const int ERROR_NOACCESS = unchecked((int)0x800703E6);
- internal const int ERROR_NOT_READY = unchecked((int)0x80070015);
- internal const int ERROR_OPEN_FAILED = unchecked((int)0x8007006E);
- internal const int ERROR_PATH_NOT_FOUND = unchecked((int)0x80070003);
- internal const int ERROR_SHARING_VIOLATION = unchecked((int)0x80070020);
- internal const int ERROR_TOO_MANY_OPEN_FILES = unchecked((int)0x80070004);
- internal const int ERROR_UNRECOGNIZED_VOLUME = unchecked((int)0x800703ED);
- internal const int ERROR_WRONG_TARGET_NAME = unchecked((int)0x80070574);
- internal const int FUSION_E_ASM_MODULE_MISSING = unchecked((int)0x80131042);
- internal const int FUSION_E_CACHEFILE_FAILED = unchecked((int)0x80131052);
- internal const int FUSION_E_CODE_DOWNLOAD_DISABLED = unchecked((int)0x80131048);
- internal const int FUSION_E_HOST_GAC_ASM_MISMATCH = unchecked((int)0x80131050);
- internal const int FUSION_E_INVALID_NAME = unchecked((int)0x80131047);
- internal const int FUSION_E_INVALID_PRIVATE_ASM_LOCATION = unchecked((int)0x80131041);
- internal const int FUSION_E_LOADFROM_BLOCKED = unchecked((int)0x80131051);
- internal const int FUSION_E_PRIVATE_ASM_DISALLOWED = unchecked((int)0x80131044);
- internal const int FUSION_E_REF_DEF_MISMATCH = unchecked((int)0x80131040);
- internal const int FUSION_E_SIGNATURE_CHECK_FAILED = unchecked((int)0x80131045);
- internal const int INET_E_CANNOT_CONNECT = unchecked((int)0x800C0004);
- internal const int INET_E_CONNECTION_TIMEOUT = unchecked((int)0x800C000B);
- internal const int INET_E_DATA_NOT_AVAILABLE = unchecked((int)0x800C0007);
- internal const int INET_E_DOWNLOAD_FAILURE = unchecked((int)0x800C0008);
- internal const int INET_E_OBJECT_NOT_FOUND = unchecked((int)0x800C0006);
- internal const int INET_E_RESOURCE_NOT_FOUND = unchecked((int)0x800C0005);
- internal const int INET_E_UNKNOWN_PROTOCOL = unchecked((int)0x800C000D);
- internal const int ISS_E_ALLOC_TOO_LARGE = unchecked((int)0x80131484);
- internal const int ISS_E_BLOCK_SIZE_TOO_SMALL = unchecked((int)0x80131483);
- internal const int ISS_E_CALLER = unchecked((int)0x801314A1);
- internal const int ISS_E_CORRUPTED_STORE_FILE = unchecked((int)0x80131480);
- internal const int ISS_E_CREATE_DIR = unchecked((int)0x80131468);
- internal const int ISS_E_CREATE_MUTEX = unchecked((int)0x80131464);
- internal const int ISS_E_DEPRECATE = unchecked((int)0x801314A0);
- internal const int ISS_E_FILE_NOT_MAPPED = unchecked((int)0x80131482);
- internal const int ISS_E_FILE_WRITE = unchecked((int)0x80131466);
- internal const int ISS_E_GET_FILE_SIZE = unchecked((int)0x80131463);
- internal const int ISS_E_ISOSTORE = unchecked((int)0x80131450);
- internal const int ISS_E_LOCK_FAILED = unchecked((int)0x80131465);
- internal const int ISS_E_MACHINE = unchecked((int)0x801314A3);
- internal const int ISS_E_MACHINE_DACL = unchecked((int)0x801314A4);
- internal const int ISS_E_MAP_VIEW_OF_FILE = unchecked((int)0x80131462);
- internal const int ISS_E_OPEN_FILE_MAPPING = unchecked((int)0x80131461);
- internal const int ISS_E_OPEN_STORE_FILE = unchecked((int)0x80131460);
- internal const int ISS_E_PATH_LENGTH = unchecked((int)0x801314A2);
- internal const int ISS_E_SET_FILE_POINTER = unchecked((int)0x80131467);
- internal const int ISS_E_STORE_NOT_OPEN = unchecked((int)0x80131469);
- internal const int ISS_E_STORE_VERSION = unchecked((int)0x80131481);
- internal const int ISS_E_TABLE_ROW_NOT_FOUND = unchecked((int)0x80131486);
- internal const int ISS_E_USAGE_WILL_EXCEED_QUOTA = unchecked((int)0x80131485);
- internal const int META_E_BAD_SIGNATURE = unchecked((int)0x80131192);
- internal const int META_E_CA_FRIENDS_SN_REQUIRED = unchecked((int)0x801311e6);
- internal const int MSEE_E_ASSEMBLYLOADINPROGRESS = unchecked((int)0x80131016);
- internal const int RO_E_CLOSED = unchecked((int)0x80000013);
- internal const int E_BOUNDS = unchecked((int)0x8000000B);
- internal const int RO_E_METADATA_NAME_NOT_FOUND = unchecked((int)0x8000000F);
- internal const int SECURITY_E_INCOMPATIBLE_EVIDENCE = unchecked((int)0x80131403);
- internal const int SECURITY_E_INCOMPATIBLE_SHARE = unchecked((int)0x80131401);
- internal const int SECURITY_E_UNVERIFIABLE = unchecked((int)0x80131402);
- internal const int STG_E_PATHNOTFOUND = unchecked((int)0x80030003);
- public const int COR_E_DIRECTORYNOTFOUND = unchecked((int)0x80070003);
- public const int COR_E_ENDOFSTREAM = unchecked((int)0x80070026); // OS defined
- public const int COR_E_FILELOAD = unchecked((int)0x80131621);
- public const int COR_E_FILENOTFOUND = unchecked((int)0x80070002);
- public const int COR_E_IO = unchecked((int)0x80131620);
- public const int COR_E_PATHTOOLONG = unchecked((int)0x800700CE);
- }
-}
diff --git a/src/mscorlib/src/System/IO/BinaryReader.cs b/src/mscorlib/src/System/IO/BinaryReader.cs
index 2cc6074d75..83fc2806bf 100644
--- a/src/mscorlib/src/System/IO/BinaryReader.cs
+++ b/src/mscorlib/src/System/IO/BinaryReader.cs
@@ -354,17 +354,28 @@ namespace System.IO
__Error.FileNotOpen();
// SafeCritical: index and count have already been verified to be a valid range for the buffer
- return InternalReadChars(buffer, index, count);
+ return InternalReadChars(new Span<char>(buffer, index, count));
}
- private int InternalReadChars(char[] buffer, int index, int count)
+ public virtual int Read(Span<char> destination)
+ {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ Contract.Ensures(Contract.Result<int>() <= destination.Length);
+ Contract.EndContractBlock();
+
+ if (_stream == null)
+ __Error.FileNotOpen();
+
+ return InternalReadChars(destination);
+ }
+
+ private int InternalReadChars(Span<char> buffer)
{
- Contract.Requires(buffer != null);
- Contract.Requires(index >= 0 && count >= 0);
Debug.Assert(_stream != null);
int numBytes = 0;
- int charsRemaining = count;
+ int index = 0;
+ int charsRemaining = buffer.Length;
if (_charBytes == null)
{
@@ -410,7 +421,7 @@ namespace System.IO
if (numBytes == 0)
{
- return (count - charsRemaining);
+ return (buffer.Length - charsRemaining);
}
Debug.Assert(byteBuffer != null, "expected byteBuffer to be non-null");
@@ -428,7 +439,7 @@ namespace System.IO
unsafe
{
fixed (byte* pBytes = byteBuffer)
- fixed (char* pChars = buffer)
+ fixed (char* pChars = &buffer.DangerousGetPinnableReference())
{
charsRead = _decoder.GetChars(pBytes + position, numBytes, pChars + index, charsRemaining, flush: false);
}
@@ -444,7 +455,7 @@ namespace System.IO
// we may have read fewer than the number of characters requested if end of stream reached
// or if the encoding makes the char count too big for the buffer (e.g. fallback sequence)
- return (count - charsRemaining);
+ return (buffer.Length - charsRemaining);
}
private int InternalReadOneChar()
@@ -541,7 +552,7 @@ namespace System.IO
// SafeCritical: we own the chars buffer, and therefore can guarantee that the index and count are valid
char[] chars = new char[count];
- int n = InternalReadChars(chars, 0, count);
+ int n = InternalReadChars(new Span<char>(chars));
if (n != count)
{
char[] copy = new char[n];
@@ -570,6 +581,18 @@ namespace System.IO
return _stream.Read(buffer, index, count);
}
+ public virtual int Read(Span<byte> destination)
+ {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ Contract.Ensures(Contract.Result<int>() <= destination.Length);
+ Contract.EndContractBlock();
+
+ if (_stream == null)
+ __Error.FileNotOpen();
+
+ return _stream.Read(destination);
+ }
+
public virtual byte[] ReadBytes(int count)
{
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
diff --git a/src/mscorlib/src/System/IO/Directory.cs b/src/mscorlib/src/System/IO/Directory.cs
index 6541e44e7d..fcc43f73c4 100644
--- a/src/mscorlib/src/System/IO/Directory.cs
+++ b/src/mscorlib/src/System/IO/Directory.cs
@@ -84,12 +84,6 @@ namespace System.IO
includeFiles, includeDirs, searchOption, true);
}
#endif // PLATFORM_UNIX
-
- internal static String InternalGetDirectoryRoot(String path)
- {
- if (path == null) return null;
- return path.Substring(0, PathInternal.GetRootLength(path));
- }
}
}
diff --git a/src/mscorlib/src/System/IO/File.cs b/src/mscorlib/src/System/IO/File.cs
index da962f6feb..e34b7aedab 100644
--- a/src/mscorlib/src/System/IO/File.cs
+++ b/src/mscorlib/src/System/IO/File.cs
@@ -196,7 +196,7 @@ namespace System.IO
if (!error)
{
Debug.Assert(false, "File::FillAttributeInfo - FindClose failed!");
- __Error.WinIOError();
+ throw Win32Marshal.GetExceptionForLastWin32Error();
}
}
}
diff --git a/src/mscorlib/src/System/IO/FileLoadException.CoreCLR.cs b/src/mscorlib/src/System/IO/FileLoadException.CoreCLR.cs
index d4ce9a9ab9..bd3504580b 100644
--- a/src/mscorlib/src/System/IO/FileLoadException.CoreCLR.cs
+++ b/src/mscorlib/src/System/IO/FileLoadException.CoreCLR.cs
@@ -27,7 +27,7 @@ namespace System.IO
GetFileLoadExceptionMessage(hResult, JitHelpers.GetStringHandleOnStack(ref format));
string message = null;
- if (hResult == System.__HResults.COR_E_BADEXEFORMAT)
+ if (hResult == System.HResults.COR_E_BADEXEFORMAT)
message = SR.Arg_BadImageFormatException;
else
GetMessageForHR(hResult, JitHelpers.GetStringHandleOnStack(ref message));
diff --git a/src/mscorlib/src/System/IO/FileSystemEnumerable.cs b/src/mscorlib/src/System/IO/FileSystemEnumerable.cs
index ed482c76b5..a16aad25c1 100644
--- a/src/mscorlib/src/System/IO/FileSystemEnumerable.cs
+++ b/src/mscorlib/src/System/IO/FileSystemEnumerable.cs
@@ -433,7 +433,7 @@ namespace System.IO
private void HandleError(int hr, String path)
{
Dispose();
- __Error.WinIOError(hr, path);
+ throw Win32Marshal.GetExceptionForWin32Error(hr, path);
}
private void AddSearchableDirsToStack(Directory.SearchData localSearchData)
diff --git a/src/mscorlib/src/System/IO/IOException.cs b/src/mscorlib/src/System/IO/IOException.cs
deleted file mode 100644
index 65504de863..0000000000
--- a/src/mscorlib/src/System/IO/IOException.cs
+++ /dev/null
@@ -1,73 +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.
-
-/*============================================================
-**
-**
-**
-**
-**
-** Purpose: Exception for a generic IO error.
-**
-**
-===========================================================*/
-
-using System;
-using System.Runtime.Serialization;
-
-namespace System.IO
-{
- public class IOException : SystemException
- {
- // For debugging purposes, store the complete path in the IOException
- // if possible. Don't give it back to users due to security concerns.
- // Let the code that throws the exception worry about that. But we can
- // at least assist people attached to the process with a managed
- // debugger. Don't serialize it to avoid any security problems.
- // This information isn't guaranteed to be correct, but is our second
- // best effort at a file or directory involved, after the exception
- // message.
- [NonSerialized]
- private String _maybeFullPath; // For debuggers on partial trust code
-
- public IOException()
- : base(SR.Arg_IOException)
- {
- HResult = __HResults.COR_E_IO;
- }
-
- public IOException(String message)
- : base(message)
- {
- HResult = __HResults.COR_E_IO;
- }
-
- public IOException(String message, int hresult)
- : base(message)
- {
- HResult = hresult;
- }
-
- // Adding this for debuggers when looking at exceptions in partial
- // trust code that may not have interesting path information in
- // the exception message.
- internal IOException(String message, int hresult, String maybeFullPath)
- : base(message)
- {
- HResult = hresult;
- _maybeFullPath = maybeFullPath;
- }
-
- public IOException(String message, Exception innerException)
- : base(message, innerException)
- {
- HResult = __HResults.COR_E_IO;
- }
-
- protected IOException(SerializationInfo info, StreamingContext context) : base(info, context)
- {
- throw new PlatformNotSupportedException();
- }
- }
-}
diff --git a/src/mscorlib/src/System/IO/MemoryStream.cs b/src/mscorlib/src/System/IO/MemoryStream.cs
index daf09d1274..2ac7c07db4 100644
--- a/src/mscorlib/src/System/IO/MemoryStream.cs
+++ b/src/mscorlib/src/System/IO/MemoryStream.cs
@@ -49,7 +49,6 @@ namespace System.IO
private bool _exposable; // Whether the array can be returned to the user.
private bool _isOpen; // Is this stream open or closed?
- [NonSerialized]
private Task<int> _lastReadTask; // The last successful task returned from ReadAsync
private const int MemStreamMaxLength = Int32.MaxValue;
@@ -391,6 +390,37 @@ namespace System.IO
return n;
}
+ public override int Read(Span<byte> destination)
+ {
+ if (GetType() != typeof(MemoryStream))
+ {
+ // MemoryStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior
+ // to this Read(Span<byte>) overload being introduced. In that case, this Read(Span<byte>) overload
+ // should use the behavior of Read(byte[],int,int) overload.
+ return base.Read(destination);
+ }
+
+ if (!_isOpen)
+ {
+ __Error.StreamIsClosed();
+ }
+
+ int n = Math.Min(_length - _position, destination.Length);
+ if (n <= 0)
+ {
+ return 0;
+ }
+
+ // TODO https://github.com/dotnet/corefx/issues/22388:
+ // Read(byte[], int, int) has an n <= 8 optimization, presumably based
+ // on benchmarking. Determine if/where such a cut-off is here and add
+ // an equivalent optimization if necessary.
+ new Span<byte>(_buffer, _position, n).CopyTo(destination);
+
+ _position += n;
+ return n;
+ }
+
public override Task<int> ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer == null)
@@ -425,6 +455,27 @@ namespace System.IO
}
}
+ public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
+ }
+
+ try
+ {
+ return new ValueTask<int>(Read(destination.Span));
+ }
+ catch (OperationCanceledException oce)
+ {
+ return new ValueTask<int>(Task.FromCancellation<int>(oce));
+ }
+ catch (Exception exception)
+ {
+ return new ValueTask<int>(Task.FromException<int>(exception));
+ }
+ }
+
public override int ReadByte()
{
@@ -634,6 +685,52 @@ namespace System.IO
_position = i;
}
+ public override void Write(ReadOnlySpan<byte> source)
+ {
+ if (GetType() != typeof(MemoryStream))
+ {
+ // MemoryStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior
+ // to this Write(Span<byte>) overload being introduced. In that case, this Write(Span<byte>) overload
+ // should use the behavior of Write(byte[],int,int) overload.
+ base.Write(source);
+ return;
+ }
+
+ if (!_isOpen)
+ {
+ __Error.StreamIsClosed();
+ }
+ EnsureWriteable();
+
+ // Check for overflow
+ int i = _position + source.Length;
+ if (i < 0)
+ {
+ throw new IOException(SR.IO_StreamTooLong);
+ }
+
+ if (i > _length)
+ {
+ bool mustZero = _position > _length;
+ if (i > _capacity)
+ {
+ bool allocatedNewArray = EnsureCapacity(i);
+ if (allocatedNewArray)
+ {
+ mustZero = false;
+ }
+ }
+ if (mustZero)
+ {
+ Array.Clear(_buffer, _length, i - _length);
+ }
+ _length = i;
+ }
+
+ source.CopyTo(new Span<byte>(_buffer, _position, source.Length));
+ _position = i;
+ }
+
public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer == null)
@@ -665,6 +762,28 @@ namespace System.IO
}
}
+ public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+
+ try
+ {
+ Write(source.Span);
+ return Task.CompletedTask;
+ }
+ catch (OperationCanceledException oce)
+ {
+ return Task.FromCancellation<VoidTaskResult>(oce);
+ }
+ catch (Exception exception)
+ {
+ return Task.FromException(exception);
+ }
+ }
+
public override void WriteByte(byte value)
{
if (!_isOpen) __Error.StreamIsClosed();
diff --git a/src/mscorlib/src/System/IO/Stream.cs b/src/mscorlib/src/System/IO/Stream.cs
index 786dfedef9..82fad24c6d 100644
--- a/src/mscorlib/src/System/IO/Stream.cs
+++ b/src/mscorlib/src/System/IO/Stream.cs
@@ -41,9 +41,7 @@ namespace System.IO
// To implement Async IO operations on streams that don't support async IO
- [NonSerialized]
private ReadWriteTask _activeReadWriteTask;
- [NonSerialized]
private SemaphoreSlim _asyncActiveSemaphore;
internal SemaphoreSlim EnsureAsyncActiveSemaphoreInitialized()
@@ -416,6 +414,33 @@ namespace System.IO
: BeginEndReadAsync(buffer, offset, count);
}
+ public virtual ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (destination.TryGetArray(out ArraySegment<byte> array))
+ {
+ return new ValueTask<int>(ReadAsync(array.Array, array.Offset, array.Count, cancellationToken));
+ }
+ else
+ {
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(destination.Length);
+ return FinishReadAsync(ReadAsync(buffer, 0, destination.Length, cancellationToken), buffer, destination);
+
+ async ValueTask<int> FinishReadAsync(Task<int> readTask, byte[] localBuffer, Memory<byte> localDestination)
+ {
+ try
+ {
+ int result = await readTask.ConfigureAwait(false);
+ new Span<byte>(localBuffer, 0, result).CopyTo(localDestination.Span);
+ return result;
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(localBuffer);
+ }
+ }
+ }
+ }
+
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern bool HasOverriddenBeginEndRead();
@@ -694,8 +719,6 @@ namespace System.IO
return WriteAsync(buffer, offset, count, CancellationToken.None);
}
-
-
public virtual Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
// If cancellation was requested, bail early with an already completed task.
@@ -705,6 +728,32 @@ namespace System.IO
: BeginEndWriteAsync(buffer, offset, count);
}
+ public virtual Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (source.DangerousTryGetArray(out ArraySegment<byte> array))
+ {
+ return WriteAsync(array.Array, array.Offset, array.Count, cancellationToken);
+ }
+ else
+ {
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(source.Length);
+ source.Span.CopyTo(buffer);
+ return FinishWriteAsync(WriteAsync(buffer, 0, source.Length, cancellationToken), buffer);
+
+ async Task FinishWriteAsync(Task writeTask, byte[] localBuffer)
+ {
+ try
+ {
+ await writeTask.ConfigureAwait(false);
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(localBuffer);
+ }
+ }
+ }
+ }
+
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern bool HasOverriddenBeginEndWrite();
@@ -734,6 +783,22 @@ namespace System.IO
public abstract int Read([In, Out] byte[] buffer, int offset, int count);
+ public virtual int Read(Span<byte> destination)
+ {
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(destination.Length);
+ try
+ {
+ int numRead = Read(buffer, 0, destination.Length);
+ if ((uint)numRead > destination.Length)
+ {
+ throw new IOException(SR.IO_StreamTooLong);
+ }
+ new Span<byte>(buffer, 0, numRead).CopyTo(destination);
+ return numRead;
+ }
+ finally { ArrayPool<byte>.Shared.Return(buffer); }
+ }
+
// Reads one byte from the stream by calling Read(byte[], int, int).
// Will return an unsigned byte cast to an int or -1 on end of stream.
// This implementation does not perform well because it allocates a new
@@ -754,6 +819,17 @@ namespace System.IO
public abstract void Write(byte[] buffer, int offset, int count);
+ public virtual void Write(ReadOnlySpan<byte> source)
+ {
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(source.Length);
+ try
+ {
+ source.CopyTo(buffer);
+ Write(buffer, 0, source.Length);
+ }
+ finally { ArrayPool<byte>.Shared.Return(buffer); }
+ }
+
// Writes one byte from the stream by calling Write(byte[], int, int).
// This implementation does not perform well because it allocates a new
// byte[] each time you call it, and should be overridden by any
@@ -957,14 +1033,20 @@ namespace System.IO
return 0;
}
+ public override int Read(Span<byte> destination)
+ {
+ return 0;
+ }
+
public override Task<int> ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
- var nullReadTask = s_nullReadTask;
- if (nullReadTask == null)
- s_nullReadTask = nullReadTask = new Task<int>(false, 0, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, CancellationToken.None); // benign race condition
- return nullReadTask;
+ return AsyncTaskMethodBuilder<int>.s_defaultResultTask;
+ }
+
+ public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return new ValueTask<int>(0);
}
- private static Task<int> s_nullReadTask;
public override int ReadByte()
{
@@ -975,6 +1057,10 @@ namespace System.IO
{
}
+ public override void Write(ReadOnlySpan<byte> source)
+ {
+ }
+
public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return cancellationToken.IsCancellationRequested ?
@@ -982,6 +1068,13 @@ namespace System.IO
Task.CompletedTask;
}
+ public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return cancellationToken.IsCancellationRequested ?
+ Task.FromCanceled(cancellationToken) :
+ Task.CompletedTask;
+ }
+
public override void WriteByte(byte value)
{
}
@@ -1229,6 +1322,12 @@ namespace System.IO
return _stream.Read(bytes, offset, count);
}
+ public override int Read(Span<byte> destination)
+ {
+ lock (_stream)
+ return _stream.Read(destination);
+ }
+
public override int ReadByte()
{
lock (_stream)
@@ -1282,6 +1381,12 @@ namespace System.IO
_stream.Write(bytes, offset, count);
}
+ public override void Write(ReadOnlySpan<byte> source)
+ {
+ lock (_stream)
+ _stream.Write(source);
+ }
+
public override void WriteByte(byte b)
{
lock (_stream)
diff --git a/src/mscorlib/src/System/IO/StreamReader.cs b/src/mscorlib/src/System/IO/StreamReader.cs
index 1fc72bffd1..9ff9187663 100644
--- a/src/mscorlib/src/System/IO/StreamReader.cs
+++ b/src/mscorlib/src/System/IO/StreamReader.cs
@@ -58,7 +58,6 @@ namespace System.IO
// This is used only for preamble detection
private int bytePos;
- [NonSerialized]
private StringBuilder _builder;
// This is the maximum number of chars we can get from one call to
@@ -84,14 +83,12 @@ namespace System.IO
private bool _isBlocked;
// The intent of this field is to leave open the underlying stream when
- // disposing of this StreamReader. A name like _leaveOpen is better,
- // but this type is serializable, and this field's name was _closable.
- private bool _closable; // Whether to close the underlying stream.
+ // disposing of this StreamReader.
+ private bool _leaveOpen; // Whether to keep the underlying stream open.
// We don't guarantee thread safety on StreamReader, but we should at
// least prevent users from trying to read anything while an Async
// read from the same thread is in progress.
- [NonSerialized]
private volatile Task _asyncReadTask;
private void CheckAsyncTaskInProgress()
@@ -216,14 +213,14 @@ namespace System.IO
_checkPreamble = (_preamble.Length > 0);
_isBlocked = false;
- _closable = !leaveOpen;
+ _leaveOpen = leaveOpen;
}
// Init used by NullStreamReader, to delay load encoding
internal void Init(Stream stream)
{
this.stream = stream;
- _closable = true;
+ _leaveOpen = false;
}
public override void Close()
@@ -265,7 +262,7 @@ namespace System.IO
}
internal bool LeaveOpen {
- get { return !_closable; }
+ get { return _leaveOpen; }
}
// DiscardBufferedData tells StreamReader to throw away its internal
@@ -1145,7 +1142,6 @@ namespace System.IO
}
#endregion
- // No data, class doesn't need to be serializable.
// Note this class is threadsafe.
private class NullStreamReader : StreamReader
{
diff --git a/src/mscorlib/src/System/IO/__Error.cs b/src/mscorlib/src/System/IO/__Error.cs
index 785a79efe8..6af1fcfadc 100644
--- a/src/mscorlib/src/System/IO/__Error.cs
+++ b/src/mscorlib/src/System/IO/__Error.cs
@@ -75,72 +75,6 @@ namespace System.IO
throw new ArgumentException(SR.InvalidOperation_EndWriteCalledMultiple);
}
- internal static void WinIOError()
- {
- int errorCode = Marshal.GetLastWin32Error();
- WinIOError(errorCode, String.Empty);
- }
-
- // After calling GetLastWin32Error(), it clears the last error field,
- // so you must save the HResult and pass it to this method. This method
- // will determine the appropriate exception to throw dependent on your
- // error, and depending on the error, insert a string into the message
- // gotten from the ResourceManager.
- internal static void WinIOError(int errorCode, String str)
- {
- switch (errorCode)
- {
- case Win32Native.ERROR_FILE_NOT_FOUND:
- if (str.Length == 0)
- throw new FileNotFoundException(SR.IO_FileNotFound);
- else
- throw new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, str), str);
-
- case Win32Native.ERROR_PATH_NOT_FOUND:
- if (str.Length == 0)
- throw new DirectoryNotFoundException(SR.IO_PathNotFound_NoPathName);
- else
- throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, str));
-
- case Win32Native.ERROR_ACCESS_DENIED:
- if (str.Length == 0)
- throw new UnauthorizedAccessException(SR.UnauthorizedAccess_IODenied_NoPathName);
- else
- throw new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, str));
-
- case Win32Native.ERROR_ALREADY_EXISTS:
- if (str.Length == 0)
- goto default;
- throw new IOException(SR.Format(SR.IO_AlreadyExists_Name, str), Win32Native.MakeHRFromErrorCode(errorCode), str);
-
- case Win32Native.ERROR_FILENAME_EXCED_RANGE:
- throw new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, str));
-
- case Win32Native.ERROR_INVALID_DRIVE:
- throw new DriveNotFoundException(SR.Format(SR.IO_DriveNotFound_Drive, str));
-
- case Win32Native.ERROR_INVALID_PARAMETER:
- throw new IOException(Win32Native.GetMessage(errorCode), Win32Native.MakeHRFromErrorCode(errorCode), str);
-
- case Win32Native.ERROR_SHARING_VIOLATION:
- if (str.Length == 0)
- throw new IOException(SR.IO_SharingViolation_NoFileName, Win32Native.MakeHRFromErrorCode(errorCode), str);
- else
- throw new IOException(SR.Format(SR.IO_SharingViolation_File, str), Win32Native.MakeHRFromErrorCode(errorCode), str);
-
- case Win32Native.ERROR_FILE_EXISTS:
- if (str.Length == 0)
- goto default;
- throw new IOException(SR.Format(SR.IO_FileExists_Name, str), Win32Native.MakeHRFromErrorCode(errorCode), str);
-
- case Win32Native.ERROR_OPERATION_ABORTED:
- throw new OperationCanceledException();
-
- default:
- throw new IOException(Win32Native.GetMessage(errorCode), Win32Native.MakeHRFromErrorCode(errorCode), str);
- }
- }
-
internal static void WriteNotSupported()
{
throw new NotSupportedException(SR.NotSupported_UnwritableStream);
diff --git a/src/mscorlib/src/System/IO/__HResults.cs b/src/mscorlib/src/System/IO/__HResults.cs
deleted file mode 100644
index 633c3538c5..0000000000
--- a/src/mscorlib/src/System/IO/__HResults.cs
+++ /dev/null
@@ -1,31 +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.
-
-//=============================================================================
-//
-//
-//
-//
-// Purpose: Define HResult constants. Every exception has one of these.
-//
-//
-//===========================================================================*/
-
-using System;
-
-namespace System.IO
-{
- // Only static data no need to serialize
- internal static class __HResults
- {
- // These use an error code from WinError.h
- public const int COR_E_ENDOFSTREAM = unchecked((int)0x80070026); // OS defined
- public const int COR_E_FILELOAD = unchecked((int)0x80131621);
- public const int COR_E_FILENOTFOUND = unchecked((int)0x80070002);
- public const int COR_E_DIRECTORYNOTFOUND = unchecked((int)0x80070003);
- public const int COR_E_PATHTOOLONG = unchecked((int)0x800700CE);
-
- public const int COR_E_IO = unchecked((int)0x80131620);
- }
-}
diff --git a/src/mscorlib/src/System/InsufficientMemoryException.cs b/src/mscorlib/src/System/InsufficientMemoryException.cs
index 8f7aa96ff7..b4bc588f18 100644
--- a/src/mscorlib/src/System/InsufficientMemoryException.cs
+++ b/src/mscorlib/src/System/InsufficientMemoryException.cs
@@ -27,19 +27,19 @@ namespace System
public InsufficientMemoryException()
: base(GetMessageFromNativeResources(ExceptionMessageKind.OutOfMemory))
{
- HResult = __HResults.COR_E_INSUFFICIENTMEMORY;
+ HResult = HResults.COR_E_INSUFFICIENTMEMORY;
}
public InsufficientMemoryException(String message)
: base(message)
{
- HResult = __HResults.COR_E_INSUFFICIENTMEMORY;
+ HResult = HResults.COR_E_INSUFFICIENTMEMORY;
}
public InsufficientMemoryException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_INSUFFICIENTMEMORY;
+ HResult = HResults.COR_E_INSUFFICIENTMEMORY;
}
}
}
diff --git a/src/mscorlib/src/System/MissingFieldException.cs b/src/mscorlib/src/System/MissingFieldException.cs
index 668d5f2a31..31931bf91c 100644
--- a/src/mscorlib/src/System/MissingFieldException.cs
+++ b/src/mscorlib/src/System/MissingFieldException.cs
@@ -22,19 +22,19 @@ namespace System
public MissingFieldException()
: base(SR.Arg_MissingFieldException)
{
- HResult = __HResults.COR_E_MISSINGFIELD;
+ HResult = HResults.COR_E_MISSINGFIELD;
}
public MissingFieldException(String message)
: base(message)
{
- HResult = __HResults.COR_E_MISSINGFIELD;
+ HResult = HResults.COR_E_MISSINGFIELD;
}
public MissingFieldException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_MISSINGFIELD;
+ HResult = HResults.COR_E_MISSINGFIELD;
}
protected MissingFieldException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/src/System/MissingMemberException.cs b/src/mscorlib/src/System/MissingMemberException.cs
index 1fb0c55e40..def3c39824 100644
--- a/src/mscorlib/src/System/MissingMemberException.cs
+++ b/src/mscorlib/src/System/MissingMemberException.cs
@@ -26,19 +26,19 @@ namespace System
public MissingMemberException()
: base(SR.Arg_MissingMemberException)
{
- HResult = __HResults.COR_E_MISSINGMEMBER;
+ HResult = HResults.COR_E_MISSINGMEMBER;
}
public MissingMemberException(String message)
: base(message)
{
- HResult = __HResults.COR_E_MISSINGMEMBER;
+ HResult = HResults.COR_E_MISSINGMEMBER;
}
public MissingMemberException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_MISSINGMEMBER;
+ HResult = HResults.COR_E_MISSINGMEMBER;
}
protected MissingMemberException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/src/System/MulticastDelegate.cs b/src/mscorlib/src/System/MulticastDelegate.cs
index 988bf2bb60..fe94aec29b 100644
--- a/src/mscorlib/src/System/MulticastDelegate.cs
+++ b/src/mscorlib/src/System/MulticastDelegate.cs
@@ -452,6 +452,17 @@ namespace System
if (IsUnmanagedFunctionPtr())
return ValueType.GetHashCodeOfPtr(_methodPtr) ^ ValueType.GetHashCodeOfPtr(_methodPtrAux);
+ if (_invocationCount != (IntPtr)0)
+ {
+ var t = _invocationList as Delegate;
+
+ if (t != null)
+ {
+ // this is a secure/wrapper delegate so we need to unwrap and check the inner one
+ return t.GetHashCode();
+ }
+ }
+
Object[] invocationList = _invocationList as Object[];
if (invocationList == null)
{
@@ -584,54 +595,6 @@ namespace System
}
[System.Diagnostics.DebuggerNonUserCode]
- private void CtorSecureClosed(Object target, IntPtr methodPtr, IntPtr callThunk, IntPtr creatorMethod)
- {
- MulticastDelegate realDelegate = (MulticastDelegate)Delegate.InternalAllocLike(this);
- realDelegate.CtorClosed(target, methodPtr);
- _invocationList = realDelegate;
- this._target = this;
- this._methodPtr = callThunk;
- this._methodPtrAux = creatorMethod;
- _invocationCount = GetInvokeMethod();
- }
-
- [System.Diagnostics.DebuggerNonUserCode]
- private void CtorSecureClosedStatic(Object target, IntPtr methodPtr, IntPtr callThunk, IntPtr creatorMethod)
- {
- MulticastDelegate realDelegate = (MulticastDelegate)Delegate.InternalAllocLike(this);
- realDelegate.CtorClosedStatic(target, methodPtr);
- _invocationList = realDelegate;
- this._target = this;
- this._methodPtr = callThunk;
- this._methodPtrAux = creatorMethod;
- _invocationCount = GetInvokeMethod();
- }
-
- [System.Diagnostics.DebuggerNonUserCode]
- private void CtorSecureRTClosed(Object target, IntPtr methodPtr, IntPtr callThunk, IntPtr creatorMethod)
- {
- MulticastDelegate realDelegate = Delegate.InternalAllocLike(this);
- realDelegate.CtorRTClosed(target, methodPtr);
- _invocationList = realDelegate;
- this._target = this;
- this._methodPtr = callThunk;
- this._methodPtrAux = creatorMethod;
- _invocationCount = GetInvokeMethod();
- }
-
- [System.Diagnostics.DebuggerNonUserCode]
- private void CtorSecureOpened(Object target, IntPtr methodPtr, IntPtr shuffleThunk, IntPtr callThunk, IntPtr creatorMethod)
- {
- MulticastDelegate realDelegate = Delegate.InternalAllocLike(this);
- realDelegate.CtorOpened(target, methodPtr, shuffleThunk);
- _invocationList = realDelegate;
- this._target = this;
- this._methodPtr = callThunk;
- this._methodPtrAux = creatorMethod;
- _invocationCount = GetInvokeMethod();
- }
-
- [System.Diagnostics.DebuggerNonUserCode]
private void CtorVirtualDispatch(Object target, IntPtr methodPtr, IntPtr shuffleThunk)
{
this._target = this;
@@ -640,18 +603,6 @@ namespace System
}
[System.Diagnostics.DebuggerNonUserCode]
- private void CtorSecureVirtualDispatch(Object target, IntPtr methodPtr, IntPtr shuffleThunk, IntPtr callThunk, IntPtr creatorMethod)
- {
- MulticastDelegate realDelegate = Delegate.InternalAllocLike(this);
- realDelegate.CtorVirtualDispatch(target, methodPtr, shuffleThunk);
- _invocationList = realDelegate;
- this._target = this;
- this._methodPtr = callThunk;
- this._methodPtrAux = creatorMethod;
- _invocationCount = GetInvokeMethod();
- }
-
- [System.Diagnostics.DebuggerNonUserCode]
private void CtorCollectibleClosedStatic(Object target, IntPtr methodPtr, IntPtr gchandle)
{
this._target = target;
diff --git a/src/mscorlib/src/System/NonSerializedAttribute.cs b/src/mscorlib/src/System/NonSerializedAttribute.cs
deleted file mode 100644
index be05e113c6..0000000000
--- a/src/mscorlib/src/System/NonSerializedAttribute.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-** Purpose: Used to mark a member as being not-serialized
-**
-**
-============================================================*/
-
-using System.Reflection;
-
-namespace System
-{
- [AttributeUsage(AttributeTargets.Field, Inherited = false)]
- public sealed class NonSerializedAttribute : Attribute
- {
- internal static Attribute GetCustomAttribute(RuntimeFieldInfo field)
- {
- if ((field.Attributes & FieldAttributes.NotSerialized) == 0)
- return null;
-
- return new NonSerializedAttribute();
- }
-
- internal static bool IsDefined(RuntimeFieldInfo field)
- {
- return (field.Attributes & FieldAttributes.NotSerialized) != 0;
- }
-
- public NonSerializedAttribute() { }
- }
-}
diff --git a/src/mscorlib/src/System/Number.cs b/src/mscorlib/src/System/Number.cs
index b30d4e643f..0eaa0b2285 100644
--- a/src/mscorlib/src/System/Number.cs
+++ b/src/mscorlib/src/System/Number.cs
@@ -286,12 +286,8 @@ namespace System
//
//This class contains only static members and does not need to be serializable
[System.Runtime.CompilerServices.FriendAccessAllowed]
- internal class Number
+ internal static class Number
{
- private Number()
- {
- }
-
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern String FormatDecimal(Decimal value, String format, NumberFormatInfo info);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -666,7 +662,7 @@ namespace System
return null;
}
- internal unsafe static Decimal ParseDecimal(String value, NumberStyles options, NumberFormatInfo numfmt)
+ internal unsafe static Decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -681,13 +677,8 @@ namespace System
return result;
}
- internal unsafe static Double ParseDouble(String value, NumberStyles options, NumberFormatInfo numfmt)
+ internal unsafe static Double ParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
{
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
Double d = 0;
@@ -697,16 +688,16 @@ namespace System
//If we failed TryStringToNumber, it may be from one of our special strings.
//Check the three with which we're concerned and rethrow if it's not one of
//those strings.
- String sTrim = value.Trim();
- if (sTrim.Equals(numfmt.PositiveInfinitySymbol))
+ ReadOnlySpan<char> sTrim = value.Trim();
+ if (StringSpanHelpers.Equals(sTrim, numfmt.PositiveInfinitySymbol))
{
return Double.PositiveInfinity;
}
- if (sTrim.Equals(numfmt.NegativeInfinitySymbol))
+ if (StringSpanHelpers.Equals(sTrim, numfmt.NegativeInfinitySymbol))
{
return Double.NegativeInfinity;
}
- if (sTrim.Equals(numfmt.NaNSymbol))
+ if (StringSpanHelpers.Equals(sTrim, numfmt.NaNSymbol))
{
return Double.NaN;
}
@@ -721,7 +712,7 @@ namespace System
return d;
}
- internal unsafe static Int32 ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
+ internal unsafe static Int32 ParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -746,7 +737,7 @@ namespace System
return i;
}
- internal unsafe static Int64 ParseInt64(String value, NumberStyles options, NumberFormatInfo numfmt)
+ internal unsafe static Int64 ParseInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -985,13 +976,8 @@ namespace System
return false;
}
- internal unsafe static Single ParseSingle(String value, NumberStyles options, NumberFormatInfo numfmt)
+ internal unsafe static Single ParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
{
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
Double d = 0;
@@ -1001,16 +987,16 @@ namespace System
//If we failed TryStringToNumber, it may be from one of our special strings.
//Check the three with which we're concerned and rethrow if it's not one of
//those strings.
- String sTrim = value.Trim();
- if (sTrim.Equals(numfmt.PositiveInfinitySymbol))
+ ReadOnlySpan<char> sTrim = value.Trim();
+ if (StringSpanHelpers.Equals(sTrim, numfmt.PositiveInfinitySymbol))
{
return Single.PositiveInfinity;
}
- if (sTrim.Equals(numfmt.NegativeInfinitySymbol))
+ if (StringSpanHelpers.Equals(sTrim, numfmt.NegativeInfinitySymbol))
{
return Single.NegativeInfinity;
}
- if (sTrim.Equals(numfmt.NaNSymbol))
+ if (StringSpanHelpers.Equals(sTrim, numfmt.NaNSymbol))
{
return Single.NaN;
}
@@ -1029,7 +1015,7 @@ namespace System
return castSingle;
}
- internal unsafe static UInt32 ParseUInt32(String value, NumberStyles options, NumberFormatInfo numfmt)
+ internal unsafe static UInt32 ParseUInt32(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -1055,7 +1041,7 @@ namespace System
return i;
}
- internal unsafe static UInt64 ParseUInt64(String value, NumberStyles options, NumberFormatInfo numfmt)
+ internal unsafe static UInt64 ParseUInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -1079,15 +1065,10 @@ namespace System
return i;
}
- private unsafe static void StringToNumber(String str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, Boolean parseDecimal)
+ private unsafe static void StringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, Boolean parseDecimal)
{
- if (str == null)
- {
- throw new ArgumentNullException(nameof(String));
- }
- Contract.EndContractBlock();
Debug.Assert(info != null, "");
- fixed (char* stringPointer = str)
+ fixed (char* stringPointer = &str.DangerousGetPinnableReference())
{
char* p = stringPointer;
if (!ParseNumber(ref p, options, ref number, null, info, parseDecimal)
@@ -1098,7 +1079,7 @@ namespace System
}
}
- private static Boolean TrailingZeros(String s, Int32 index)
+ private static Boolean TrailingZeros(ReadOnlySpan<char> s, Int32 index)
{
// For compatibility, we need to allow trailing zeros at the end of a number string
for (int i = index; i < s.Length; i++)
@@ -1111,7 +1092,7 @@ namespace System
return true;
}
- internal unsafe static Boolean TryParseDecimal(String value, NumberStyles options, NumberFormatInfo numfmt, out Decimal result)
+ internal unsafe static Boolean TryParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out Decimal result)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -1129,7 +1110,7 @@ namespace System
return true;
}
- internal unsafe static Boolean TryParseDouble(String value, NumberStyles options, NumberFormatInfo numfmt, out Double result)
+ internal unsafe static Boolean TryParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out Double result)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -1147,7 +1128,7 @@ namespace System
return true;
}
- internal unsafe static Boolean TryParseInt32(String s, NumberStyles style, NumberFormatInfo info, out Int32 result)
+ internal unsafe static Boolean TryParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out Int32 result)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -1175,7 +1156,7 @@ namespace System
return true;
}
- internal unsafe static Boolean TryParseInt64(String s, NumberStyles style, NumberFormatInfo info, out Int64 result)
+ internal unsafe static Boolean TryParseInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out Int64 result)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -1203,7 +1184,7 @@ namespace System
return true;
}
- internal unsafe static Boolean TryParseSingle(String value, NumberStyles options, NumberFormatInfo numfmt, out Single result)
+ internal unsafe static Boolean TryParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out Single result)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -1228,7 +1209,7 @@ namespace System
return true;
}
- internal unsafe static Boolean TryParseUInt32(String s, NumberStyles style, NumberFormatInfo info, out UInt32 result)
+ internal unsafe static Boolean TryParseUInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out UInt32 result)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -1256,7 +1237,7 @@ namespace System
return true;
}
- internal unsafe static Boolean TryParseUInt64(String s, NumberStyles style, NumberFormatInfo info, out UInt64 result)
+ internal unsafe static Boolean TryParseUInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out UInt64 result)
{
Byte* numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
@@ -1284,24 +1265,19 @@ namespace System
return true;
}
- internal static Boolean TryStringToNumber(String str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, Boolean parseDecimal)
- {
- return TryStringToNumber(str, options, ref number, null, numfmt, parseDecimal);
- }
-
[System.Runtime.CompilerServices.FriendAccessAllowed]
- internal unsafe static Boolean TryStringToNumber(String str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, Boolean parseDecimal)
+ internal unsafe static Boolean TryStringToNumber(String str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, Boolean parseDecimal) =>
+ str != null ?
+ TryStringToNumber(str.AsReadOnlySpan(), options, ref number, numfmt, parseDecimal) :
+ false;
+
+ internal static unsafe Boolean TryStringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, Boolean parseDecimal)
{
- if (str == null)
- {
- return false;
- }
Debug.Assert(numfmt != null, "");
-
- fixed (char* stringPointer = str)
+ fixed (char* stringPointer = &str.DangerousGetPinnableReference())
{
char* p = stringPointer;
- if (!ParseNumber(ref p, options, ref number, sb, numfmt, parseDecimal)
+ if (!ParseNumber(ref p, options, ref number, null, numfmt, parseDecimal)
|| (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer))))
{
return false;
diff --git a/src/mscorlib/src/System/OutOfMemoryException.cs b/src/mscorlib/src/System/OutOfMemoryException.cs
index 3bec542077..8ef2d7b265 100644
--- a/src/mscorlib/src/System/OutOfMemoryException.cs
+++ b/src/mscorlib/src/System/OutOfMemoryException.cs
@@ -22,19 +22,19 @@ namespace System
public OutOfMemoryException()
: base(GetMessageFromNativeResources(ExceptionMessageKind.OutOfMemory))
{
- HResult = __HResults.COR_E_OUTOFMEMORY;
+ HResult = HResults.COR_E_OUTOFMEMORY;
}
public OutOfMemoryException(String message)
: base(message)
{
- HResult = __HResults.COR_E_OUTOFMEMORY;
+ HResult = HResults.COR_E_OUTOFMEMORY;
}
public OutOfMemoryException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_OUTOFMEMORY;
+ HResult = HResults.COR_E_OUTOFMEMORY;
}
protected OutOfMemoryException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs b/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs
index bbbd80b58b..d437e05e31 100644
--- a/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs
+++ b/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs
@@ -22,7 +22,11 @@ namespace System.Reflection
private static Assembly LoadFromResolveHandler(object sender, ResolveEventArgs args)
{
Assembly requestingAssembly = args.RequestingAssembly;
-
+ if (requestingAssembly == null)
+ {
+ return null;
+ }
+
// Requesting assembly for LoadFrom is always loaded in defaultContext - proceed only if that
// is the case.
if (AssemblyLoadContext.Default != AssemblyLoadContext.GetLoadContext(requestingAssembly))
diff --git a/src/mscorlib/src/System/Reflection/CustomAttribute.cs b/src/mscorlib/src/System/Reflection/CustomAttribute.cs
index 4bc5933d50..1dd88a23a5 100644
--- a/src/mscorlib/src/System/Reflection/CustomAttribute.cs
+++ b/src/mscorlib/src/System/Reflection/CustomAttribute.cs
@@ -67,7 +67,7 @@ namespace System.Reflection
IList<CustomAttributeData> cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken);
int pcaCount = 0;
- Attribute[] a = PseudoCustomAttribute.GetCustomAttributes((RuntimeType)target, typeof(object) as RuntimeType, true, out pcaCount);
+ Attribute[] a = PseudoCustomAttribute.GetCustomAttributes((RuntimeType)target, typeof(object) as RuntimeType, out pcaCount);
if (pcaCount == 0)
return cad;
@@ -111,7 +111,7 @@ namespace System.Reflection
IList<CustomAttributeData> cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken);
int pcaCount = 0;
- Attribute[] a = PseudoCustomAttribute.GetCustomAttributes((RuntimeMethodInfo)target, typeof(object) as RuntimeType, true, out pcaCount);
+ Attribute[] a = PseudoCustomAttribute.GetCustomAttributes((RuntimeMethodInfo)target, typeof(object) as RuntimeType, out pcaCount);
if (pcaCount == 0)
return cad;
@@ -164,7 +164,7 @@ namespace System.Reflection
IList<CustomAttributeData> cad = GetCustomAttributes((RuntimeModule)target.ManifestModule, RuntimeAssembly.GetToken(target.GetNativeHandle()));
int pcaCount = 0;
- Attribute[] a = PseudoCustomAttribute.GetCustomAttributes(target, typeof(object) as RuntimeType, false, out pcaCount);
+ Attribute[] a = PseudoCustomAttribute.GetCustomAttributes(target, typeof(object) as RuntimeType, out pcaCount);
if (pcaCount == 0)
return cad;
@@ -1276,7 +1276,7 @@ namespace System.Reflection
type = type.GetGenericTypeDefinition() as RuntimeType;
int pcaCount = 0;
- Attribute[] pca = PseudoCustomAttribute.GetCustomAttributes(type, caType, true, out pcaCount);
+ Attribute[] pca = PseudoCustomAttribute.GetCustomAttributes(type, caType, out pcaCount);
// if we are asked to go up the hierarchy chain we have to do it now and regardless of the
// attribute usage for the specific attribute because a derived attribute may override the usage...
@@ -1320,7 +1320,7 @@ namespace System.Reflection
method = method.GetGenericMethodDefinition() as RuntimeMethodInfo;
int pcaCount = 0;
- Attribute[] pca = PseudoCustomAttribute.GetCustomAttributes(method, caType, true, out pcaCount);
+ Attribute[] pca = PseudoCustomAttribute.GetCustomAttributes(method, caType, out pcaCount);
// if we are asked to go up the hierarchy chain we have to do it now and regardless of the
// attribute usage for the specific attribute because a derived attribute may override the usage...
@@ -1361,7 +1361,7 @@ namespace System.Reflection
Contract.Requires(caType != null);
int pcaCount = 0;
- Attribute[] pca = PseudoCustomAttribute.GetCustomAttributes(ctor, caType, true, out pcaCount);
+ Attribute[] pca = PseudoCustomAttribute.GetCustomAttributes(ctor, caType, out pcaCount);
object[] attributes = GetCustomAttributes(ctor.GetRuntimeModule(), ctor.MetadataToken, pcaCount, caType);
if (pcaCount > 0) Array.Copy(pca, 0, attributes, attributes.Length - pcaCount, pcaCount);
return attributes;
@@ -1422,7 +1422,7 @@ namespace System.Reflection
Contract.Requires(caType != null);
int pcaCount = 0;
- Attribute[] pca = PseudoCustomAttribute.GetCustomAttributes(assembly, caType, true, out pcaCount);
+ Attribute[] pca = PseudoCustomAttribute.GetCustomAttributes(assembly, caType, out pcaCount);
int assemblyToken = RuntimeAssembly.GetToken(assembly.GetNativeHandle());
object[] attributes = GetCustomAttributes(assembly.ManifestModule as RuntimeModule, assemblyToken, pcaCount, caType);
if (pcaCount > 0) Array.Copy(pca, 0, attributes, attributes.Length - pcaCount, pcaCount);
@@ -1890,13 +1890,6 @@ namespace System.Reflection
private static int s_pcasCount;
#endregion
- #region FCalls
- internal static void GetSecurityAttributes(RuntimeModule module, int token, bool assembly, out object[] securityAttributes)
- {
- securityAttributes = null;
- }
- #endregion
-
#region Static Constructor
static PseudoCustomAttribute()
{
@@ -1941,13 +1934,7 @@ namespace System.Reflection
#endregion
#region Internal Static
- internal static bool IsSecurityAttribute(RuntimeType type)
- {
- // TODO: Cleanup
- return false;
- }
-
- internal static Attribute[] GetCustomAttributes(RuntimeType type, RuntimeType caType, bool includeSecCa, out int count)
+ internal static Attribute[] GetCustomAttributes(RuntimeType type, RuntimeType caType, out int count)
{
Contract.Requires(type != null);
Contract.Requires(caType != null);
@@ -1955,36 +1942,20 @@ namespace System.Reflection
count = 0;
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && !s_pca.ContainsKey(caType) && !IsSecurityAttribute(caType))
+ if (!all && !s_pca.ContainsKey(caType))
return Array.Empty<Attribute>();
List<Attribute> pcas = new List<Attribute>();
- Attribute pca = null;
if (all || caType == (RuntimeType)typeof(SerializableAttribute))
{
- pca = SerializableAttribute.GetCustomAttribute(type);
- if (pca != null) pcas.Add(pca);
+ if ((type.Attributes & TypeAttributes.Serializable) != 0)
+ pcas.Add(new SerializableAttribute());
}
if (all || caType == (RuntimeType)typeof(ComImportAttribute))
{
- pca = ComImportAttribute.GetCustomAttribute(type);
- if (pca != null) pcas.Add(pca);
- }
- if (includeSecCa && (all || IsSecurityAttribute(caType)))
- {
- if (!type.IsGenericParameter && type.GetElementType() == null)
- {
- if (type.IsGenericType)
- type = (RuntimeType)type.GetGenericTypeDefinition();
-
- object[] securityAttributes;
- GetSecurityAttributes(type.Module.ModuleHandle.GetRuntimeModule(), type.MetadataToken, false, out securityAttributes);
- if (securityAttributes != null)
- foreach (object a in securityAttributes)
- if (caType == a.GetType() || a.GetType().IsSubclassOf(caType))
- pcas.Add((Attribute)a);
- }
+ if ((type.Attributes & TypeAttributes.Import) != 0)
+ pcas.Add(new ComImportAttribute());
}
count = pcas.Count;
@@ -1993,28 +1964,24 @@ namespace System.Reflection
internal static bool IsDefined(RuntimeType type, RuntimeType caType)
{
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && !s_pca.ContainsKey(caType) && !IsSecurityAttribute(caType))
+ if (!all && !s_pca.ContainsKey(caType))
return false;
if (all || caType == (RuntimeType)typeof(SerializableAttribute))
{
- if (SerializableAttribute.IsDefined(type)) return true;
+ if ((type.Attributes & TypeAttributes.Serializable) != 0)
+ return true;
}
if (all || caType == (RuntimeType)typeof(ComImportAttribute))
{
- if (ComImportAttribute.IsDefined(type)) return true;
- }
- if (all || IsSecurityAttribute(caType))
- {
- int count;
- if (GetCustomAttributes(type, caType, true, out count).Length != 0)
+ if ((type.Attributes & TypeAttributes.Import) != 0)
return true;
}
return false;
}
- internal static Attribute[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeType caType, bool includeSecCa, out int count)
+ internal static Attribute[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeType caType, out int count)
{
Contract.Requires(method != null);
Contract.Requires(caType != null);
@@ -2022,31 +1989,21 @@ namespace System.Reflection
count = 0;
bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
- if (!all && !s_pca.ContainsKey(caType) && !IsSecurityAttribute(caType))
+ if (!all && !s_pca.ContainsKey(caType))
return Array.Empty<Attribute>();
List<Attribute> pcas = new List<Attribute>();
- Attribute pca = null;
+ Attribute pca;
if (all || caType == (RuntimeType)typeof(DllImportAttribute))
{
- pca = DllImportAttribute.GetCustomAttribute(method);
- if (pca != null) pcas.Add(pca);
+ pca = GetDllImportCustomAttribute(method);
+ if (pca != null) pcas[count++] = pca;
}
if (all || caType == (RuntimeType)typeof(PreserveSigAttribute))
{
- pca = PreserveSigAttribute.GetCustomAttribute(method);
- if (pca != null) pcas.Add(pca);
- }
- if (includeSecCa && (all || IsSecurityAttribute(caType)))
- {
- object[] securityAttributes;
-
- GetSecurityAttributes(method.Module.ModuleHandle.GetRuntimeModule(), method.MetadataToken, false, out securityAttributes);
- if (securityAttributes != null)
- foreach (object a in securityAttributes)
- if (caType == a.GetType() || a.GetType().IsSubclassOf(caType))
- pcas.Add((Attribute)a);
+ if ((method.GetMethodImplementationFlags() & MethodImplAttributes.PreserveSig) != 0)
+ pcas.Add(new PreserveSigAttribute());
}
count = pcas.Count;
@@ -2060,17 +2017,12 @@ namespace System.Reflection
if (all || caType == (RuntimeType)typeof(DllImportAttribute))
{
- if (DllImportAttribute.IsDefined(method)) return true;
+ if ((method.Attributes & MethodAttributes.PinvokeImpl) != 0)
+ return true;
}
if (all || caType == (RuntimeType)typeof(PreserveSigAttribute))
{
- if (PreserveSigAttribute.IsDefined(method)) return true;
- }
- if (all || IsSecurityAttribute(caType))
- {
- int count;
-
- if (GetCustomAttributes(method, caType, true, out count).Length != 0)
+ if ((method.GetMethodImplementationFlags() & MethodImplAttributes.PreserveSig) != 0)
return true;
}
@@ -2089,26 +2041,26 @@ namespace System.Reflection
return null;
Attribute[] pcas = new Attribute[s_pcasCount];
- Attribute pca = null;
+ Attribute pca;
if (all || caType == (RuntimeType)typeof(InAttribute))
{
- pca = InAttribute.GetCustomAttribute(parameter);
- if (pca != null) pcas[count++] = pca;
+ if (parameter.IsIn)
+ pcas[count++] = new InAttribute();
}
if (all || caType == (RuntimeType)typeof(OutAttribute))
{
- pca = OutAttribute.GetCustomAttribute(parameter);
- if (pca != null) pcas[count++] = pca;
+ if (parameter.IsOut)
+ pcas[count++] = new OutAttribute();
}
if (all || caType == (RuntimeType)typeof(OptionalAttribute))
{
- pca = OptionalAttribute.GetCustomAttribute(parameter);
- if (pca != null) pcas[count++] = pca;
+ if (parameter.IsOptional)
+ pcas[count++] = new OptionalAttribute();
}
if (all || caType == (RuntimeType)typeof(MarshalAsAttribute))
{
- pca = MarshalAsAttribute.GetCustomAttribute(parameter);
+ pca = GetMarshalAsCustomAttribute(parameter);
if (pca != null) pcas[count++] = pca;
}
return pcas;
@@ -2122,62 +2074,32 @@ namespace System.Reflection
if (all || caType == (RuntimeType)typeof(InAttribute))
{
- if (InAttribute.IsDefined(parameter)) return true;
+ if (parameter.IsIn) return true;
}
if (all || caType == (RuntimeType)typeof(OutAttribute))
{
- if (OutAttribute.IsDefined(parameter)) return true;
+ if (parameter.IsOut) return true;
}
if (all || caType == (RuntimeType)typeof(OptionalAttribute))
{
- if (OptionalAttribute.IsDefined(parameter)) return true;
+ if (parameter.IsOptional) return true;
}
if (all || caType == (RuntimeType)typeof(MarshalAsAttribute))
{
- if (MarshalAsAttribute.IsDefined(parameter)) return true;
+ if (GetMarshalAsCustomAttribute(parameter) != null) return true;
}
return false;
}
- internal static Attribute[] GetCustomAttributes(RuntimeAssembly assembly, RuntimeType caType, bool includeSecCa, out int count)
+ internal static Attribute[] GetCustomAttributes(RuntimeAssembly assembly, RuntimeType caType, out int count)
{
count = 0;
-
- bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
-
- if (!all && !s_pca.ContainsKey(caType) && !IsSecurityAttribute(caType))
- return Array.Empty<Attribute>();
-
- List<Attribute> pcas = new List<Attribute>();
- if (includeSecCa && (all || IsSecurityAttribute(caType)))
- {
- object[] securityAttributes;
-
- GetSecurityAttributes(assembly.ManifestModule.ModuleHandle.GetRuntimeModule(), RuntimeAssembly.GetToken(assembly.GetNativeHandle()), true, out securityAttributes);
- if (securityAttributes != null)
- foreach (object a in securityAttributes)
- if (caType == a.GetType() || a.GetType().IsSubclassOf(caType))
- pcas.Add((Attribute)a);
- }
-
- //TypeForwardedToAttribute.GetCustomAttribute(assembly) throws FileNotFoundException if the forwarded-to
- //assemblies are not present. This breaks many V4 scenarios because some framework types are forwarded
- //to assemblies not included in the client SKU. Let's omit TypeForwardedTo for now until we have a better solution.
-
- //if (all || caType == (RuntimeType)typeof(TypeForwardedToAttribute))
- //{
- // TypeForwardedToAttribute[] attrs = TypeForwardedToAttribute.GetCustomAttribute(assembly);
- // pcas.AddRange(attrs);
- //}
-
- count = pcas.Count;
- return pcas.ToArray();
+ return null;
}
internal static bool IsDefined(RuntimeAssembly assembly, RuntimeType caType)
{
- int count;
- return GetCustomAttributes(assembly, caType, true, out count).Length > 0;
+ return false;
}
internal static Attribute[] GetCustomAttributes(RuntimeModule module, RuntimeType caType, out int count)
@@ -2202,22 +2124,22 @@ namespace System.Reflection
return null;
Attribute[] pcas = new Attribute[s_pcasCount];
- Attribute pca = null;
+ Attribute pca;
if (all || caType == (RuntimeType)typeof(MarshalAsAttribute))
{
- pca = MarshalAsAttribute.GetCustomAttribute(field);
+ pca = GetMarshalAsCustomAttribute(field);
if (pca != null) pcas[count++] = pca;
}
if (all || caType == (RuntimeType)typeof(FieldOffsetAttribute))
{
- pca = FieldOffsetAttribute.GetCustomAttribute(field);
+ pca = GetFieldOffsetCustomAttribute(field);
if (pca != null) pcas[count++] = pca;
}
if (all || caType == (RuntimeType)typeof(NonSerializedAttribute))
{
- pca = NonSerializedAttribute.GetCustomAttribute(field);
- if (pca != null) pcas[count++] = pca;
+ if ((field.Attributes & FieldAttributes.NotSerialized) != 0)
+ pcas[count++] = new NonSerializedAttribute();
}
return pcas;
}
@@ -2229,60 +2151,28 @@ namespace System.Reflection
if (all || caType == (RuntimeType)typeof(MarshalAsAttribute))
{
- if (MarshalAsAttribute.IsDefined(field)) return true;
+ if (GetMarshalAsCustomAttribute(field) != null) return true;
}
if (all || caType == (RuntimeType)typeof(FieldOffsetAttribute))
{
- if (FieldOffsetAttribute.IsDefined(field)) return true;
+ if (GetFieldOffsetCustomAttribute(field) != null) return true;
}
if (all || caType == (RuntimeType)typeof(NonSerializedAttribute))
{
- if (NonSerializedAttribute.IsDefined(field)) return true;
+ if ((field.Attributes & FieldAttributes.NotSerialized) != 0)
+ return true;
}
return false;
}
- internal static Attribute[] GetCustomAttributes(RuntimeConstructorInfo ctor, RuntimeType caType, bool includeSecCa, out int count)
+ internal static Attribute[] GetCustomAttributes(RuntimeConstructorInfo ctor, RuntimeType caType, out int count)
{
count = 0;
-
- bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
-
- if (!all && !s_pca.ContainsKey(caType) && !IsSecurityAttribute(caType))
- return Array.Empty<Attribute>();
-
- List<Attribute> pcas = new List<Attribute>();
-
- if (includeSecCa && (all || IsSecurityAttribute(caType)))
- {
- object[] securityAttributes;
-
- GetSecurityAttributes(ctor.Module.ModuleHandle.GetRuntimeModule(), ctor.MetadataToken, false, out securityAttributes);
- if (securityAttributes != null)
- foreach (object a in securityAttributes)
- if (caType == a.GetType() || a.GetType().IsSubclassOf(caType))
- pcas.Add((Attribute)a);
- }
-
- count = pcas.Count;
- return pcas.ToArray();
+ return null;
}
internal static bool IsDefined(RuntimeConstructorInfo ctor, RuntimeType caType)
{
- bool all = caType == (RuntimeType)typeof(object) || caType == (RuntimeType)typeof(Attribute);
-
- if (!all && !s_pca.ContainsKey(caType))
- return false;
-
- if (all || IsSecurityAttribute(caType))
- {
- int count;
-
- if (GetCustomAttributes(ctor, caType, true, out count).Length != 0)
- return true;
- }
-
return false;
}
@@ -2306,5 +2196,165 @@ namespace System.Reflection
return false;
}
#endregion
+
+ private static DllImportAttribute GetDllImportCustomAttribute(RuntimeMethodInfo method)
+ {
+ if ((method.Attributes & MethodAttributes.PinvokeImpl) == 0)
+ return null;
+
+ MetadataImport scope = ModuleHandle.GetMetadataImport(method.Module.ModuleHandle.GetRuntimeModule());
+ string entryPoint, dllName = null;
+ int token = method.MetadataToken;
+ PInvokeAttributes flags = 0;
+
+ scope.GetPInvokeMap(token, out flags, out entryPoint, out dllName);
+
+ CharSet charSet = CharSet.None;
+
+ switch (flags & PInvokeAttributes.CharSetMask)
+ {
+ case PInvokeAttributes.CharSetNotSpec: charSet = CharSet.None; break;
+ case PInvokeAttributes.CharSetAnsi: charSet = CharSet.Ansi; break;
+ case PInvokeAttributes.CharSetUnicode: charSet = CharSet.Unicode; break;
+ case PInvokeAttributes.CharSetAuto: charSet = CharSet.Auto; break;
+
+ // Invalid: default to CharSet.None
+ default: break;
+ }
+
+ CallingConvention callingConvention = CallingConvention.Cdecl;
+
+ switch (flags & PInvokeAttributes.CallConvMask)
+ {
+ case PInvokeAttributes.CallConvWinapi: callingConvention = CallingConvention.Winapi; break;
+ case PInvokeAttributes.CallConvCdecl: callingConvention = CallingConvention.Cdecl; break;
+ case PInvokeAttributes.CallConvStdcall: callingConvention = CallingConvention.StdCall; break;
+ case PInvokeAttributes.CallConvThiscall: callingConvention = CallingConvention.ThisCall; break;
+ case PInvokeAttributes.CallConvFastcall: callingConvention = CallingConvention.FastCall; break;
+
+ // Invalid: default to CallingConvention.Cdecl
+ default: break;
+ }
+
+ DllImportAttribute attribute = new DllImportAttribute(dllName);
+
+ attribute.EntryPoint = entryPoint;
+ attribute.CharSet = charSet;
+ attribute.SetLastError = (flags & PInvokeAttributes.SupportsLastError) != 0;
+ attribute.ExactSpelling = (flags & PInvokeAttributes.NoMangle) != 0;
+ attribute.PreserveSig = (method.GetMethodImplementationFlags() & MethodImplAttributes.PreserveSig) != 0;
+ attribute.CallingConvention = callingConvention;
+ attribute.BestFitMapping = (flags & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitEnabled;
+ attribute.ThrowOnUnmappableChar = (flags & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharEnabled;
+
+ return attribute;
+ }
+
+
+ private static MarshalAsAttribute GetMarshalAsCustomAttribute(RuntimeParameterInfo parameter)
+ {
+ return GetMarshalAsCustomAttribute(parameter.MetadataToken, parameter.GetRuntimeModule());
+ }
+ private static MarshalAsAttribute GetMarshalAsCustomAttribute(RuntimeFieldInfo field)
+ {
+ return GetMarshalAsCustomAttribute(field.MetadataToken, field.GetRuntimeModule());
+ }
+
+ private static MarshalAsAttribute GetMarshalAsCustomAttribute(int token, RuntimeModule scope)
+ {
+ UnmanagedType unmanagedType, arraySubType;
+ VarEnum safeArraySubType;
+ int sizeParamIndex = 0, sizeConst = 0;
+ string marshalTypeName = null, marshalCookie = null, safeArrayUserDefinedTypeName = null;
+ int iidParamIndex = 0;
+ ConstArray nativeType = ModuleHandle.GetMetadataImport(scope.GetNativeHandle()).GetFieldMarshal(token);
+
+ if (nativeType.Length == 0)
+ return null;
+
+ MetadataImport.GetMarshalAs(nativeType,
+ out unmanagedType, out safeArraySubType, out safeArrayUserDefinedTypeName, out arraySubType, out sizeParamIndex,
+ out sizeConst, out marshalTypeName, out marshalCookie, out iidParamIndex);
+
+ RuntimeType safeArrayUserDefinedType = safeArrayUserDefinedTypeName == null || safeArrayUserDefinedTypeName.Length == 0 ? null :
+ RuntimeTypeHandle.GetTypeByNameUsingCARules(safeArrayUserDefinedTypeName, scope);
+ RuntimeType marshalTypeRef = null;
+
+ try
+ {
+ marshalTypeRef = marshalTypeName == null ? null : RuntimeTypeHandle.GetTypeByNameUsingCARules(marshalTypeName, scope);
+ }
+ catch (System.TypeLoadException)
+ {
+ // The user may have supplied a bad type name string causing this TypeLoadException
+ // Regardless, we return the bad type name
+ Debug.Assert(marshalTypeName != null);
+ }
+
+ MarshalAsAttribute attribute = new MarshalAsAttribute(unmanagedType);
+
+ attribute.SafeArraySubType = safeArraySubType;
+ attribute.SafeArrayUserDefinedSubType = safeArrayUserDefinedType;
+ attribute.IidParameterIndex = iidParamIndex;
+ attribute.ArraySubType = arraySubType;
+ attribute.SizeParamIndex = (short)sizeParamIndex;
+ attribute.SizeConst = sizeConst;
+ attribute.MarshalType = marshalTypeName;
+ attribute.MarshalTypeRef = marshalTypeRef;
+ attribute.MarshalCookie = marshalCookie;
+
+ return attribute;
+ }
+
+ private static FieldOffsetAttribute GetFieldOffsetCustomAttribute(RuntimeFieldInfo field)
+ {
+ int fieldOffset;
+
+ if (field.DeclaringType != null &&
+ field.GetRuntimeModule().MetadataImport.GetFieldOffset(field.DeclaringType.MetadataToken, field.MetadataToken, out fieldOffset))
+ return new FieldOffsetAttribute(fieldOffset);
+
+ return null;
+ }
+
+ internal static StructLayoutAttribute GetStructLayoutCustomAttribute(RuntimeType type)
+ {
+ if (type.IsInterface || type.HasElementType || type.IsGenericParameter)
+ return null;
+
+ int pack = 0, size = 0;
+ LayoutKind layoutKind = LayoutKind.Auto;
+ switch (type.Attributes & TypeAttributes.LayoutMask)
+ {
+ case TypeAttributes.ExplicitLayout: layoutKind = LayoutKind.Explicit; break;
+ case TypeAttributes.AutoLayout: layoutKind = LayoutKind.Auto; break;
+ case TypeAttributes.SequentialLayout: layoutKind = LayoutKind.Sequential; break;
+ default: Contract.Assume(false); break;
+ }
+
+ CharSet charSet = CharSet.None;
+ switch (type.Attributes & TypeAttributes.StringFormatMask)
+ {
+ case TypeAttributes.AnsiClass: charSet = CharSet.Ansi; break;
+ case TypeAttributes.AutoClass: charSet = CharSet.Auto; break;
+ case TypeAttributes.UnicodeClass: charSet = CharSet.Unicode; break;
+ default: Contract.Assume(false); break;
+ }
+ type.GetRuntimeModule().MetadataImport.GetClassLayout(type.MetadataToken, out pack, out size);
+
+ // Metadata parameter checking should not have allowed 0 for packing size.
+ // The runtime later converts a packing size of 0 to 8 so do the same here
+ // because it's more useful from a user perspective.
+ if (pack == 0)
+ pack = 8; // DEFAULT_PACKING_SIZE
+
+ StructLayoutAttribute attribute = new StructLayoutAttribute(layoutKind);
+
+ attribute.Pack = pack;
+ attribute.Size = size;
+ attribute.CharSet = charSet;
+
+ return attribute;
+ }
}
}
diff --git a/src/mscorlib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/mscorlib/src/System/Reflection/Emit/AssemblyBuilder.cs
index 7b190df6c2..82f460138b 100644
--- a/src/mscorlib/src/System/Reflection/Emit/AssemblyBuilder.cs
+++ b/src/mscorlib/src/System/Reflection/Emit/AssemblyBuilder.cs
@@ -44,7 +44,7 @@ namespace System.Reflection.Emit
// This InternalAssemblyBuilder can be retrieved via a call to Assembly.GetAssemblies() by untrusted code.
// In the past, when InternalAssemblyBuilder was AssemblyBuilder, the untrusted user could down cast the
// Assembly to an AssemblyBuilder and emit code with the elevated permissions of the trusted code which
- // origionally created the AssemblyBuilder via DefineDynamicAssembly. Today, this can no longer happen
+ // originally created the AssemblyBuilder via DefineDynamicAssembly. Today, this can no longer happen
// because the Assembly returned via AssemblyGetAssemblies() will be an InternalAssemblyBuilder.
// Only the caller of DefineDynamicAssembly will get an AssemblyBuilder.
diff --git a/src/mscorlib/src/System/Reflection/Emit/DynamicMethod.cs b/src/mscorlib/src/System/Reflection/Emit/DynamicMethod.cs
index 15792d2d68..07d886fcca 100644
--- a/src/mscorlib/src/System/Reflection/Emit/DynamicMethod.cs
+++ b/src/mscorlib/src/System/Reflection/Emit/DynamicMethod.cs
@@ -487,18 +487,19 @@ namespace System.Reflection.Emit
throw new TargetParameterCountException(SR.Arg_ParmCnt);
// if we are here we passed all the previous checks. Time to look at the arguments
+ bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
Object retValue = null;
if (actualCount > 0)
{
Object[] arguments = CheckArguments(parameters, binder, invokeAttr, culture, sig);
- retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, false);
+ retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, false, wrapExceptions);
// copy out. This should be made only if ByRef are present.
for (int index = 0; index < arguments.Length; index++)
parameters[index] = arguments[index];
}
else
{
- retValue = RuntimeMethodHandle.InvokeMethod(null, null, sig, false);
+ retValue = RuntimeMethodHandle.InvokeMethod(null, null, sig, false, wrapExceptions);
}
GC.KeepAlive(this);
@@ -578,7 +579,7 @@ namespace System.Reflection.Emit
// This way the DynamicMethod creator is the only one responsible for DynamicMethod access,
// and can control exactly who gets access to it.
//
- internal class RTDynamicMethod : MethodInfo
+ internal sealed class RTDynamicMethod : MethodInfo
{
internal DynamicMethod m_owner;
private RuntimeParameterInfo[] m_parameters;
@@ -679,7 +680,7 @@ namespace System.Reflection.Emit
Contract.EndContractBlock();
if (attributeType.IsAssignableFrom(typeof(MethodImplAttribute)))
- return new Object[] { new MethodImplAttribute(GetMethodImplementationFlags()) };
+ return new Object[] { new MethodImplAttribute((MethodImplOptions)GetMethodImplementationFlags()) };
else
return Array.Empty<Object>();
}
@@ -687,7 +688,7 @@ namespace System.Reflection.Emit
public override Object[] GetCustomAttributes(bool inherit)
{
// support for MethodImplAttribute PCA
- return new Object[] { new MethodImplAttribute(GetMethodImplementationFlags()) };
+ return new Object[] { new MethodImplAttribute((MethodImplOptions)GetMethodImplementationFlags()) };
}
public override bool IsDefined(Type attributeType, bool inherit)
diff --git a/src/mscorlib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs b/src/mscorlib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs
index 75e4acc903..8256d0e6d5 100644
--- a/src/mscorlib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs
+++ b/src/mscorlib/src/System/Reflection/Emit/GenericTypeParameterBuilder.cs
@@ -21,7 +21,7 @@ namespace System.Reflection.Emit
return IsAssignableFrom(typeInfo.AsType());
}
- #region Private Data Mebers
+ #region Private Data Members
internal TypeBuilder m_type;
#endregion
diff --git a/src/mscorlib/src/System/Reflection/Emit/ILGenerator.cs b/src/mscorlib/src/System/Reflection/Emit/ILGenerator.cs
index fa31d66f6c..52e8b30e33 100644
--- a/src/mscorlib/src/System/Reflection/Emit/ILGenerator.cs
+++ b/src/mscorlib/src/System/Reflection/Emit/ILGenerator.cs
@@ -1622,7 +1622,7 @@ namespace System.Reflection.Emit
/***************************
*
- * Find the current active lexcial scope. For example, if we have
+ * Find the current active lexical scope. For example, if we have
* "Open Open Open Close",
* we will return 1 as the second BeginScope is currently active.
*
diff --git a/src/mscorlib/src/System/Reflection/Emit/MethodBuilderInstantiation.cs b/src/mscorlib/src/System/Reflection/Emit/MethodBuilderInstantiation.cs
index 2d0a9b41dd..be4bfd5f48 100644
--- a/src/mscorlib/src/System/Reflection/Emit/MethodBuilderInstantiation.cs
+++ b/src/mscorlib/src/System/Reflection/Emit/MethodBuilderInstantiation.cs
@@ -26,7 +26,7 @@ namespace System.Reflection.Emit
#endregion
- #region Private Data Mebers
+ #region Private Data Members
internal MethodInfo m_method;
private Type[] m_inst;
#endregion
diff --git a/src/mscorlib/src/System/Reflection/Emit/ModuleBuilder.cs b/src/mscorlib/src/System/Reflection/Emit/ModuleBuilder.cs
index 362b13657f..1c65bf91c2 100644
--- a/src/mscorlib/src/System/Reflection/Emit/ModuleBuilder.cs
+++ b/src/mscorlib/src/System/Reflection/Emit/ModuleBuilder.cs
@@ -87,7 +87,7 @@ namespace System.Reflection.Emit
#endregion
- #region Intenral Data Members
+ #region Internal Data Members
// m_TypeBuilder contains both TypeBuilder and EnumBuilder objects
private Dictionary<string, Type> m_TypeBuilderDict;
private ISymbolWriter m_iSymWriter;
diff --git a/src/mscorlib/src/System/Reflection/Emit/ModuleBuilderData.cs b/src/mscorlib/src/System/Reflection/Emit/ModuleBuilderData.cs
index 4a9b774d15..c597dbe6b4 100644
--- a/src/mscorlib/src/System/Reflection/Emit/ModuleBuilderData.cs
+++ b/src/mscorlib/src/System/Reflection/Emit/ModuleBuilderData.cs
@@ -2,10 +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.
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-//
-
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
@@ -55,9 +51,7 @@ namespace System.Reflection.Emit
internal String m_strFileName;
internal bool m_fGlobalBeenCreated;
internal bool m_fHasGlobal;
- [NonSerialized]
internal TypeBuilder m_globalTypeBuilder;
- [NonSerialized]
internal ModuleBuilder m_module;
private int m_tkFile;
@@ -65,5 +59,5 @@ namespace System.Reflection.Emit
internal const String MULTI_BYTE_VALUE_CLASS = "$ArrayType$";
internal String m_strResourceFileName;
internal byte[] m_resourceBytes;
- } // class ModuleBuilderData
+ }
}
diff --git a/src/mscorlib/src/System/Reflection/Emit/TypeBuilderInstantiation.cs b/src/mscorlib/src/System/Reflection/Emit/TypeBuilderInstantiation.cs
index 64a38b0995..ab5b7eeaa5 100644
--- a/src/mscorlib/src/System/Reflection/Emit/TypeBuilderInstantiation.cs
+++ b/src/mscorlib/src/System/Reflection/Emit/TypeBuilderInstantiation.cs
@@ -43,7 +43,7 @@ namespace System.Reflection.Emit
#endregion
- #region Private Data Mebers
+ #region Private Data Members
private Type m_type;
private Type[] m_inst;
private string m_strFullQualName;
diff --git a/src/mscorlib/src/System/Reflection/Emit/XXXOnTypeBuilderInstantiation.cs b/src/mscorlib/src/System/Reflection/Emit/XXXOnTypeBuilderInstantiation.cs
index 78238c02b7..3894d9115b 100644
--- a/src/mscorlib/src/System/Reflection/Emit/XXXOnTypeBuilderInstantiation.cs
+++ b/src/mscorlib/src/System/Reflection/Emit/XXXOnTypeBuilderInstantiation.cs
@@ -23,7 +23,7 @@ namespace System.Reflection.Emit
}
#endregion
- #region Private Data Mebers
+ #region Private Data Members
internal MethodInfo m_method;
private TypeBuilderInstantiation m_type;
#endregion
@@ -99,7 +99,7 @@ namespace System.Reflection.Emit
}
#endregion
- #region Private Data Mebers
+ #region Private Data Members
internal ConstructorInfo m_ctor;
private TypeBuilderInstantiation m_type;
#endregion
diff --git a/src/mscorlib/src/System/Reflection/ExceptionHandlingClause.cs b/src/mscorlib/src/System/Reflection/ExceptionHandlingClause.cs
index 9bb45aebb2..a61ed5e385 100644
--- a/src/mscorlib/src/System/Reflection/ExceptionHandlingClause.cs
+++ b/src/mscorlib/src/System/Reflection/ExceptionHandlingClause.cs
@@ -9,7 +9,7 @@ namespace System.Reflection
{
public class ExceptionHandlingClause
{
- #region costructor
+ #region constructor
// This class can only be created from inside the EE.
protected ExceptionHandlingClause() { }
#endregion
diff --git a/src/mscorlib/src/System/Reflection/INVOCATION_FLAGS.cs b/src/mscorlib/src/System/Reflection/INVOCATION_FLAGS.cs
index 6ffc3e968b..b097b8fa0f 100644
--- a/src/mscorlib/src/System/Reflection/INVOCATION_FLAGS.cs
+++ b/src/mscorlib/src/System/Reflection/INVOCATION_FLAGS.cs
@@ -15,7 +15,7 @@ namespace System.Reflection
INVOCATION_FLAGS_INITIALIZED = 0x00000001,
// it's used for both method and field to signify that no access is allowed
INVOCATION_FLAGS_NO_INVOKE = 0x00000002,
- INVOCATION_FLAGS_NEED_SECURITY = 0x00000004,
+ /* unused 0x00000004 */
// Set for static ctors and ctors on abstract types, which
// can be invoked only if the "this" object is provided (even if it's null).
INVOCATION_FLAGS_NO_CTOR_INVOKE = 0x00000008,
diff --git a/src/mscorlib/src/System/Reflection/MethodBody.cs b/src/mscorlib/src/System/Reflection/MethodBody.cs
index 4335177efb..e1d18bf375 100644
--- a/src/mscorlib/src/System/Reflection/MethodBody.cs
+++ b/src/mscorlib/src/System/Reflection/MethodBody.cs
@@ -8,7 +8,7 @@ namespace System.Reflection
{
public class MethodBody
{
- #region costructor
+ #region constructor
// This class can only be created from inside the EE.
protected MethodBody() { }
#endregion
diff --git a/src/mscorlib/src/System/Reflection/RtFieldInfo.cs b/src/mscorlib/src/System/Reflection/RtFieldInfo.cs
index ddfc56b2aa..856d254d47 100644
--- a/src/mscorlib/src/System/Reflection/RtFieldInfo.cs
+++ b/src/mscorlib/src/System/Reflection/RtFieldInfo.cs
@@ -13,11 +13,6 @@ namespace System.Reflection
{
internal unsafe sealed class RtFieldInfo : RuntimeFieldInfo, IRuntimeFieldInfo
{
- #region FCalls
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- static private extern void PerformVisibilityCheckOnField(IntPtr field, Object target, RuntimeType declaringType, FieldAttributes attr, uint invocationFlags);
- #endregion
-
#region Private Data Members
// agressive caching
private IntPtr m_fieldHandle;
@@ -58,13 +53,6 @@ namespace System.Reflection
if ((m_fieldAttributes & FieldAttributes.HasFieldRVA) != (FieldAttributes)0)
invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_SPECIAL_FIELD;
- // A public field is inaccesible to Transparent code if the field is Critical.
- bool needsTransparencySecurityCheck = IsSecurityCritical && !IsSecuritySafeCritical;
- bool needsVisibilitySecurityCheck = ((m_fieldAttributes & FieldAttributes.FieldAccessMask) != FieldAttributes.Public) ||
- (declaringType != null && declaringType.NeedsReflectionSecurityCheck);
- if (needsTransparencySecurityCheck || needsVisibilitySecurityCheck)
- invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_NEED_SECURITY;
-
// find out if the field type is one of the following: Primitive, Enum or Pointer
Type fieldType = FieldType;
if (fieldType.IsPointer || fieldType.IsEnum || fieldType.IsPrimitive)
@@ -158,11 +146,6 @@ namespace System.Reflection
RuntimeType fieldType = (RuntimeType)FieldType;
value = fieldType.CheckValue(value, binder, culture, invokeAttr);
- #region Security Check
- if ((invocationFlags & (INVOCATION_FLAGS.INVOCATION_FLAGS_SPECIAL_FIELD | INVOCATION_FLAGS.INVOCATION_FLAGS_NEED_SECURITY)) != 0)
- PerformVisibilityCheckOnField(m_fieldHandle, obj, m_declaringType, m_fieldAttributes, (uint)m_invocationFlags);
- #endregion
-
bool domainInitialized = false;
if (declaringType == null)
{
@@ -223,10 +206,6 @@ namespace System.Reflection
CheckConsistency(obj);
- RuntimeType fieldType = (RuntimeType)FieldType;
- if ((invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_NEED_SECURITY) != 0)
- PerformVisibilityCheckOnField(m_fieldHandle, obj, m_declaringType, m_fieldAttributes, (uint)(m_invocationFlags & ~INVOCATION_FLAGS.INVOCATION_FLAGS_SPECIAL_FIELD));
-
return UnsafeGetValue(obj);
}
diff --git a/src/mscorlib/src/System/Reflection/RuntimeAssembly.cs b/src/mscorlib/src/System/Reflection/RuntimeAssembly.cs
index edcb0d5cd5..cd9c715eec 100644
--- a/src/mscorlib/src/System/Reflection/RuntimeAssembly.cs
+++ b/src/mscorlib/src/System/Reflection/RuntimeAssembly.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
+using System.Diagnostics;
using CultureInfo = System.Globalization.CultureInfo;
using System.Security;
using System.IO;
@@ -800,5 +801,78 @@ namespace System.Reflection
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern int GetToken(RuntimeAssembly assembly);
+
+ public sealed override Type[] GetForwardedTypes()
+ {
+ List<Type> types = new List<Type>();
+ List<Exception> exceptions = new List<Exception>();
+
+ MetadataImport scope = GetManifestModule(GetNativeHandle()).MetadataImport;
+ scope.Enum(MetadataTokenType.ExportedType, 0, out MetadataEnumResult enumResult);
+ for (int i = 0; i < enumResult.Length; i++)
+ {
+ MetadataToken mdtExternalType = enumResult[i];
+ Type type = null;
+ Exception exception = null;
+ ObjectHandleOnStack pType = JitHelpers.GetObjectHandleOnStack(ref type);
+ try
+ {
+ GetForwardedType(this, mdtExternalType, pType);
+ if (type == null)
+ continue; // mdtExternalType was not a forwarder entry.
+ }
+ catch (Exception e)
+ {
+ type = null;
+ exception = e;
+ }
+
+ Debug.Assert((type != null) != (exception != null)); // Exactly one of these must be non-null.
+
+ if (type != null)
+ {
+ types.Add(type);
+ AddPublicNestedTypes(type, types, exceptions);
+ }
+ else
+ {
+ exceptions.Add(exception);
+ }
+ }
+
+ if (exceptions.Count != 0)
+ {
+ int numTypes = types.Count;
+ int numExceptions = exceptions.Count;
+ types.AddRange(new Type[numExceptions]); // add one null Type for each exception.
+ exceptions.InsertRange(0, new Exception[numTypes]); // align the Exceptions with the null Types.
+ throw new ReflectionTypeLoadException(types.ToArray(), exceptions.ToArray());
+ }
+
+ return types.ToArray();
+ }
+
+ private static void AddPublicNestedTypes(Type type, List<Type> types, List<Exception> exceptions)
+ {
+ Type[] nestedTypes;
+ try
+ {
+ nestedTypes = type.GetNestedTypes(BindingFlags.Public);
+ }
+ catch (Exception e)
+ {
+ exceptions.Add(e);
+ return;
+ }
+ foreach (Type nestedType in nestedTypes)
+ {
+ types.Add(nestedType);
+ AddPublicNestedTypes(nestedType, types, exceptions);
+ }
+ }
+
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern void GetForwardedType(RuntimeAssembly assembly, MetadataToken mdtExternalType, ObjectHandleOnStack type);
}
}
diff --git a/src/mscorlib/src/System/Reflection/RuntimeConstructorInfo.cs b/src/mscorlib/src/System/Reflection/RuntimeConstructorInfo.cs
index 7870e0b91e..9b11a858df 100644
--- a/src/mscorlib/src/System/Reflection/RuntimeConstructorInfo.cs
+++ b/src/mscorlib/src/System/Reflection/RuntimeConstructorInfo.cs
@@ -57,16 +57,7 @@ namespace System.Reflection
// this should be an invocable method, determine the other flags that participate in invocation
invocationFlags |= RuntimeMethodHandle.GetSecurityFlags(this);
- if ((invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_NEED_SECURITY) == 0 &&
- ((Attributes & MethodAttributes.MemberAccessMask) != MethodAttributes.Public ||
- (declaringType != null && declaringType.NeedsReflectionSecurityCheck)))
- {
- // If method is non-public, or declaring type is not visible
- invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_NEED_SECURITY;
- }
-
- // Check for attempt to create a delegate class, we demand unmanaged
- // code permission for this since it's hard to validate the target address.
+ // Check for attempt to create a delegate class.
if (typeof(Delegate).IsAssignableFrom(DeclaringType))
invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_IS_DELEGATE_CTOR;
}
@@ -363,13 +354,6 @@ namespace System.Reflection
// check basic method consistency. This call will throw if there are problems in the target/method relationship
CheckConsistency(obj);
- if (obj != null)
- {
- // For unverifiable code, we require the caller to be critical.
- // Adding the INVOCATION_FLAGS_NEED_SECURITY flag makes that check happen
- invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_NEED_SECURITY;
- }
-
Signature sig = Signature;
// get the signature
@@ -379,16 +363,17 @@ namespace System.Reflection
throw new TargetParameterCountException(SR.Arg_ParmCnt);
// if we are here we passed all the previous checks. Time to look at the arguments
+ bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
if (actualCount > 0)
{
Object[] arguments = CheckArguments(parameters, binder, invokeAttr, culture, sig);
- Object retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, sig, false);
+ Object retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, sig, false, wrapExceptions);
// copy out. This should be made only if ByRef are present.
for (int index = 0; index < arguments.Length; index++)
parameters[index] = arguments[index];
return retValue;
}
- return RuntimeMethodHandle.InvokeMethod(obj, null, sig, false);
+ return RuntimeMethodHandle.InvokeMethod(obj, null, sig, false, wrapExceptions);
}
public override MethodBody GetMethodBody()
@@ -449,16 +434,17 @@ namespace System.Reflection
// JIT/NGen will insert the call to .cctor in the instance ctor.
// if we are here we passed all the previous checks. Time to look at the arguments
+ bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
if (actualCount > 0)
{
Object[] arguments = CheckArguments(parameters, binder, invokeAttr, culture, sig);
- Object retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, true);
+ Object retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, true, wrapExceptions);
// copy out. This should be made only if ByRef are present.
for (int index = 0; index < arguments.Length; index++)
parameters[index] = arguments[index];
return retValue;
}
- return RuntimeMethodHandle.InvokeMethod(null, null, sig, true);
+ return RuntimeMethodHandle.InvokeMethod(null, null, sig, true, wrapExceptions);
}
#endregion
}
diff --git a/src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs b/src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs
index f05508de7b..6addf74718 100644
--- a/src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs
+++ b/src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs
@@ -54,29 +54,6 @@ namespace System.Reflection
{
// this should be an invocable method, determine the other flags that participate in invocation
invocationFlags = RuntimeMethodHandle.GetSecurityFlags(this);
-
- if ((invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_NEED_SECURITY) == 0)
- {
- if ((Attributes & MethodAttributes.MemberAccessMask) != MethodAttributes.Public ||
- (declaringType != null && declaringType.NeedsReflectionSecurityCheck))
- {
- // If method is non-public, or declaring type is not visible
- invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_NEED_SECURITY;
- }
- else if (IsGenericMethod)
- {
- Type[] genericArguments = GetGenericArguments();
-
- for (int i = 0; i < genericArguments.Length; i++)
- {
- if (genericArguments[i].NeedsReflectionSecurityCheck)
- {
- invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_NEED_SECURITY;
- break;
- }
- }
- }
- }
}
m_invocationFlags = invocationFlags | INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED;
@@ -207,6 +184,7 @@ namespace System.Reflection
return m_declaringType;
}
+ internal sealed override int GenericParameterCount => RuntimeMethodHandle.GetGenericParameterCount(this);
#endregion
#region Object Overrides
@@ -488,7 +466,7 @@ namespace System.Reflection
{
object[] arguments = InvokeArgumentsCheck(obj, invokeAttr, binder, parameters, culture);
- return UnsafeInvokeInternal(obj, parameters, arguments);
+ return UnsafeInvokeInternal(obj, invokeAttr, parameters, arguments);
}
[DebuggerStepThroughAttribute]
@@ -497,18 +475,19 @@ namespace System.Reflection
{
object[] arguments = InvokeArgumentsCheck(obj, invokeAttr, binder, parameters, culture);
- return UnsafeInvokeInternal(obj, parameters, arguments);
+ return UnsafeInvokeInternal(obj, invokeAttr, parameters, arguments);
}
[DebuggerStepThroughAttribute]
[Diagnostics.DebuggerHidden]
- private object UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
+ private object UnsafeInvokeInternal(Object obj, BindingFlags invokeAttr, Object[] parameters, Object[] arguments)
{
+ bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
if (arguments == null || arguments.Length == 0)
- return RuntimeMethodHandle.InvokeMethod(obj, null, Signature, false);
+ return RuntimeMethodHandle.InvokeMethod(obj, null, Signature, false, wrapExceptions);
else
{
- Object retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false);
+ Object retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false, wrapExceptions);
// copy out. This should be made only if ByRef are present.
for (int index = 0; index < arguments.Length; index++)
diff --git a/src/mscorlib/src/System/Reflection/RuntimeParameterInfo.cs b/src/mscorlib/src/System/Reflection/RuntimeParameterInfo.cs
index 8c07d8f397..8f070b6827 100644
--- a/src/mscorlib/src/System/Reflection/RuntimeParameterInfo.cs
+++ b/src/mscorlib/src/System/Reflection/RuntimeParameterInfo.cs
@@ -118,20 +118,12 @@ namespace System.Reflection
#endregion
#region Private Data Members
- // These are new in Whidbey, so we cannot serialize them directly or we break backwards compatibility.
- [NonSerialized]
private int m_tkParamDef;
- [NonSerialized]
private MetadataImport m_scope;
- [NonSerialized]
private Signature m_signature;
- [NonSerialized]
private volatile bool m_nameIsCached = false;
- [NonSerialized]
private readonly bool m_noMetadata = false;
- [NonSerialized]
private bool m_noDefaultValue = false;
- [NonSerialized]
private MethodBase m_originalMember = null;
#endregion
@@ -366,15 +358,15 @@ namespace System.Reflection
if (attrType == typeof(DateTimeConstantAttribute))
{
- defaultValue = DateTimeConstantAttribute.GetRawDateTimeConstant(attr);
+ defaultValue = GetRawDateTimeConstant(attr);
}
else if (attrType == typeof(DecimalConstantAttribute))
{
- defaultValue = DecimalConstantAttribute.GetRawDecimalConstant(attr);
+ defaultValue = GetRawDecimalConstant(attr);
}
else if (attrType.IsSubclassOf(s_CustomConstantAttributeType))
{
- defaultValue = CustomConstantAttribute.GetRawConstant(attr);
+ defaultValue = GetRawConstant(attr);
}
}
}
@@ -403,6 +395,80 @@ namespace System.Reflection
return defaultValue;
}
+ private static Decimal GetRawDecimalConstant(CustomAttributeData attr)
+ {
+ Contract.Requires(attr.Constructor.DeclaringType == typeof(DecimalConstantAttribute));
+
+ foreach (CustomAttributeNamedArgument namedArgument in attr.NamedArguments)
+ {
+ if (namedArgument.MemberInfo.Name.Equals("Value"))
+ {
+ // This is not possible because Decimal cannot be represented directly in the metadata.
+ Debug.Assert(false, "Decimal cannot be represented directly in the metadata.");
+ return (Decimal)namedArgument.TypedValue.Value;
+ }
+ }
+
+ ParameterInfo[] parameters = attr.Constructor.GetParameters();
+ Debug.Assert(parameters.Length == 5);
+
+ System.Collections.Generic.IList<CustomAttributeTypedArgument> args = attr.ConstructorArguments;
+ Debug.Assert(args.Count == 5);
+
+ if (parameters[2].ParameterType == typeof(uint))
+ {
+ // DecimalConstantAttribute(byte scale, byte sign, uint hi, uint mid, uint low)
+ int low = (int)(UInt32)args[4].Value;
+ int mid = (int)(UInt32)args[3].Value;
+ int hi = (int)(UInt32)args[2].Value;
+ byte sign = (byte)args[1].Value;
+ byte scale = (byte)args[0].Value;
+
+ return new System.Decimal(low, mid, hi, (sign != 0), scale);
+ }
+ else
+ {
+ // DecimalConstantAttribute(byte scale, byte sign, int hi, int mid, int low)
+ int low = (int)args[4].Value;
+ int mid = (int)args[3].Value;
+ int hi = (int)args[2].Value;
+ byte sign = (byte)args[1].Value;
+ byte scale = (byte)args[0].Value;
+
+ return new System.Decimal(low, mid, hi, (sign != 0), scale);
+ }
+ }
+
+ private static DateTime GetRawDateTimeConstant(CustomAttributeData attr)
+ {
+ Contract.Requires(attr.Constructor.DeclaringType == typeof(DateTimeConstantAttribute));
+ Contract.Requires(attr.ConstructorArguments.Count == 1);
+
+ foreach (CustomAttributeNamedArgument namedArgument in attr.NamedArguments)
+ {
+ if (namedArgument.MemberInfo.Name.Equals("Value"))
+ {
+ return new DateTime((long)namedArgument.TypedValue.Value);
+ }
+ }
+
+ // Look at the ctor argument if the "Value" property was not explicitly defined.
+ return new DateTime((long)attr.ConstructorArguments[0].Value);
+ }
+
+ private static object GetRawConstant(CustomAttributeData attr)
+ {
+ foreach (CustomAttributeNamedArgument namedArgument in attr.NamedArguments)
+ {
+ if (namedArgument.MemberInfo.Name.Equals("Value"))
+ return namedArgument.TypedValue.Value;
+ }
+
+ // Return DBNull to indicate that no default value is available.
+ // Not to be confused with a null return which indicates a null default value.
+ return DBNull.Value;
+ }
+
internal RuntimeModule GetRuntimeModule()
{
RuntimeMethodInfo method = Member as RuntimeMethodInfo;
diff --git a/src/mscorlib/src/System/Resources/FileBasedResourceGroveler.cs b/src/mscorlib/src/System/Resources/FileBasedResourceGroveler.cs
index e1bbd2814a..7d445deba2 100644
--- a/src/mscorlib/src/System/Resources/FileBasedResourceGroveler.cs
+++ b/src/mscorlib/src/System/Resources/FileBasedResourceGroveler.cs
@@ -118,18 +118,13 @@ namespace System.Resources
return null; // give up.
}
- // Constructs a new ResourceSet for a given file name. The logic in
- // here avoids a ReflectionPermission check for our RuntimeResourceSet
- // for perf and working set reasons.
+ // Constructs a new ResourceSet for a given file name.
private ResourceSet CreateResourceSet(String file)
{
Debug.Assert(file != null, "file shouldn't be null; check caller");
if (_mediator.UserResourceSet == null)
{
- // Explicitly avoid CreateInstance if possible, because it
- // requires ReflectionPermission to call private & protected
- // constructors.
return new RuntimeResourceSet(file);
}
else
diff --git a/src/mscorlib/src/System/Resources/ManifestBasedResourceGroveler.cs b/src/mscorlib/src/System/Resources/ManifestBasedResourceGroveler.cs
index 0e9666b2b1..a4e698276f 100644
--- a/src/mscorlib/src/System/Resources/ManifestBasedResourceGroveler.cs
+++ b/src/mscorlib/src/System/Resources/ManifestBasedResourceGroveler.cs
@@ -189,9 +189,7 @@ namespace System.Resources
}
}
- // Constructs a new ResourceSet for a given file name. The logic in
- // here avoids a ReflectionPermission check for our RuntimeResourceSet
- // for perf and working set reasons.
+ // Constructs a new ResourceSet for a given file name.
// Use the assembly to resolve assembly manifest resource references.
// Note that is can be null, but probably shouldn't be.
// This method could use some refactoring. One thing at a time.
@@ -248,13 +246,7 @@ namespace System.Resources
// the abbreviated ones emitted by InternalResGen.
if (CanUseDefaultResourceClasses(readerTypeName, resSetTypeName))
{
- RuntimeResourceSet rs;
-#if LOOSELY_LINKED_RESOURCE_REFERENCE
- rs = new RuntimeResourceSet(store, assembly);
-#else
- rs = new RuntimeResourceSet(store);
-#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
- return rs;
+ return new RuntimeResourceSet(store);
}
else
{
@@ -264,16 +256,9 @@ namespace System.Resources
args[0] = store;
IResourceReader reader = (IResourceReader)Activator.CreateInstance(readerType, args);
- Object[] resourceSetArgs =
-#if LOOSELY_LINKED_RESOURCE_REFERENCE
- new Object[2];
-#else
- new Object[1];
-#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+ Object[] resourceSetArgs = new Object[1];
resourceSetArgs[0] = reader;
-#if LOOSELY_LINKED_RESOURCE_REFERENCE
- resourceSetArgs[1] = assembly;
-#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+
Type resSetType;
if (_mediator.UserResourceSet == null)
{
@@ -299,14 +284,7 @@ namespace System.Resources
if (_mediator.UserResourceSet == null)
{
- // Explicitly avoid CreateInstance if possible, because it
- // requires ReflectionPermission to call private & protected
- // constructors.
-#if LOOSELY_LINKED_RESOURCE_REFERENCE
- return new RuntimeResourceSet(store, assembly);
-#else
return new RuntimeResourceSet(store);
-#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
}
else
{
@@ -327,9 +305,7 @@ namespace System.Resources
args = new Object[1];
args[0] = store;
rs = (ResourceSet)Activator.CreateInstance(_mediator.UserResourceSet, args);
-#if LOOSELY_LINKED_RESOURCE_REFERENCE
- rs.Assembly = assembly;
-#endif // LOOSELY_LINKED_RESOURCE_REFERENCE
+
return rs;
}
catch (MissingMethodException e)
@@ -440,7 +416,7 @@ namespace System.Resources
// denied back from the OS. This showed up for
// href-run exe's at one point.
int hr = fle._HResult;
- if (hr != Win32Native.MakeHRFromErrorCode(Win32Native.ERROR_ACCESS_DENIED))
+ if (hr != Win32Marshal.MakeHRFromErrorCode(Win32Native.ERROR_ACCESS_DENIED))
{
Debug.Assert(false, "[This assert catches satellite assembly build/deployment problems - report this message to your build lab & loc engineer]" + Environment.NewLine + "GetSatelliteAssembly failed for culture " + lookForCulture.Name + " and version " + (_mediator.SatelliteContractVersion == null ? _mediator.MainAssembly.GetVersion().ToString() : _mediator.SatelliteContractVersion.ToString()) + " of assembly " + _mediator.MainAssembly.GetSimpleName() + " with error code 0x" + hr.ToString("X", CultureInfo.InvariantCulture) + Environment.NewLine + "Exception: " + fle);
}
diff --git a/src/mscorlib/src/System/Resources/ResourceManager.cs b/src/mscorlib/src/System/Resources/ResourceManager.cs
index 92b935d931..7c565adbbb 100644
--- a/src/mscorlib/src/System/Resources/ResourceManager.cs
+++ b/src/mscorlib/src/System/Resources/ResourceManager.cs
@@ -525,17 +525,16 @@ namespace System.Resources
// such as ".ResX", or a completely different format for naming files.
protected virtual String GetResourceFileName(CultureInfo culture)
{
- StringBuilder sb = new StringBuilder(255);
- sb.Append(BaseNameField);
- // If this is the neutral culture, don't append culture name.
- if (!culture.HasInvariantCultureName)
+ // If this is the neutral culture, don't include the culture name.
+ if (culture.HasInvariantCultureName)
{
- CultureInfo.VerifyCultureName(culture.Name, true);
- sb.Append('.');
- sb.Append(culture.Name);
+ return BaseNameField + ResFileExtension;
+ }
+ else
+ {
+ CultureInfo.VerifyCultureName(culture.Name, throwException: true);
+ return BaseNameField + "." + culture.Name + ResFileExtension;
}
- sb.Append(ResFileExtension);
- return sb.ToString();
}
// WARNING: This function must be kept in sync with ResourceFallbackManager.GetEnumerator()
@@ -889,7 +888,7 @@ namespace System.Resources
// contains the PRI resources.
private bool ShouldUseSatelliteAssemblyResourceLookupUnderAppX(RuntimeAssembly resourcesAssembly)
{
- bool fUseSatelliteAssemblyResourceLookupUnderAppX = true; // TODO: https://github.com/dotnet/coreclr/issues/12178 once we fix our uap testhost
+ bool fUseSatelliteAssemblyResourceLookupUnderAppX = typeof(Object).Assembly == resourcesAssembly;
if (!fUseSatelliteAssemblyResourceLookupUnderAppX)
{
@@ -1035,9 +1034,14 @@ namespace System.Resources
// In this case _PRIExceptionInfo is now null and we will just throw the generic
// MissingManifestResource_NoPRIresources exception.
// See the implementation of GetString for more details.
- if (e.HResult != __HResults.ERROR_MRM_MAP_NOT_FOUND)
+ if (e.HResult != HResults.ERROR_MRM_MAP_NOT_FOUND)
throw; // Unexpected exception code. Bubble it up to the caller.
}
+
+ if (!_PRIonAppXInitialized)
+ {
+ _bUsingModernResourceManagement = false;
+ }
// Allow all other exception types to bubble up to the caller.
// Yes, this causes us to potentially throw exception types that are not documented.
@@ -1122,7 +1126,7 @@ namespace System.Resources
{
// When running inside AppX we want to ignore the languages list when trying to come up with our CurrentUICulture.
// This line behaves the same way as CultureInfo.CurrentUICulture would have in .NET 4
- culture = CultureInfo.GetCurrentUICultureNoAppX();
+ culture = CultureInfo.CurrentUICulture;
}
ResourceSet last = GetFirstResourceSet(culture);
diff --git a/src/mscorlib/src/System/Resources/ResourceSet.cs b/src/mscorlib/src/System/Resources/ResourceSet.cs
index b4029a7e9c..deb9763d5d 100644
--- a/src/mscorlib/src/System/Resources/ResourceSet.cs
+++ b/src/mscorlib/src/System/Resources/ResourceSet.cs
@@ -13,16 +13,10 @@
**
===========================================================*/
-using System;
using System.Collections;
using System.IO;
-using System.Globalization;
-using System.Runtime.InteropServices;
using System.Reflection;
-using System.Runtime.Serialization;
-using System.Runtime.Versioning;
using System.Diagnostics.Contracts;
-using System.Collections.Generic;
namespace System.Resources
{
@@ -34,7 +28,7 @@ namespace System.Resources
//
public class ResourceSet : IDisposable, IEnumerable
{
- [NonSerialized] protected IResourceReader Reader;
+ protected IResourceReader Reader;
internal Hashtable Table;
private Hashtable _caseInsensitiveTable; // For case-insensitive lookups.
diff --git a/src/mscorlib/src/System/Resources/__HResults.cs b/src/mscorlib/src/System/Resources/__HResults.cs
deleted file mode 100644
index 5817161665..0000000000
--- a/src/mscorlib/src/System/Resources/__HResults.cs
+++ /dev/null
@@ -1,24 +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.
-
-//=============================================================================
-//
-//
-//
-// Purpose: Define HResult constants returned by the Windows Modern Resource Manager
-// and consumed by System.Resources.ResourceManager.
-//
-//===========================================================================*/
-#if FEATURE_APPX
-namespace System.Resources
-{
- using System;
- // Only static data no need to serialize
- internal static class __HResults
- {
- // From WinError.h
- public const int ERROR_MRM_MAP_NOT_FOUND = unchecked((int)0x80073B1F);
- }
-}
-#endif
diff --git a/src/mscorlib/src/System/RtType.cs b/src/mscorlib/src/System/RtType.cs
index 871a39db4b..2d70354a48 100644
--- a/src/mscorlib/src/System/RtType.cs
+++ b/src/mscorlib/src/System/RtType.cs
@@ -458,8 +458,8 @@ namespace System
Volatile.Write(ref m_cacheComplete, true);
}
- else
- list = m_allMembers;
+
+ list = m_allMembers;
break;
default:
@@ -618,7 +618,11 @@ namespace System
while (RuntimeTypeHandle.IsGenericVariable(declaringType))
declaringType = declaringType.GetBaseType();
- bool* overrides = stackalloc bool[RuntimeTypeHandle.GetNumVirtuals(declaringType)];
+ int numVirtuals = RuntimeTypeHandle.GetNumVirtuals(declaringType);
+
+ bool* overrides = stackalloc bool[numVirtuals];
+ new Span<bool>(overrides, numVirtuals).Clear();
+
bool isValueType = declaringType.IsValueType;
do
@@ -2403,7 +2407,7 @@ namespace System
for (int i = 0; i < parameterInfos.Length; i++)
{
// a null argument type implies a null arg which is always a perfect match
- if ((object)argumentTypes[i] != null && !Object.ReferenceEquals(parameterInfos[i].ParameterType, argumentTypes[i]))
+ if ((object)argumentTypes[i] != null && !argumentTypes[i].MatchesParameterTypeExactly(parameterInfos[i]))
return false;
}
}
@@ -2485,8 +2489,11 @@ namespace System
#region Type Overrides
#region Get XXXInfo Candidates
+
+ private const int GenericParameterCountAny = -1;
+
private ListBuilder<MethodInfo> GetMethodCandidates(
- String name, BindingFlags bindingAttr, CallingConventions callConv,
+ String name, int genericParameterCount, BindingFlags bindingAttr, CallingConventions callConv,
Type[] types, bool allowPrefixLookup)
{
bool prefixLookup, ignoreCase;
@@ -2499,6 +2506,9 @@ namespace System
for (int i = 0; i < cache.Length; i++)
{
RuntimeMethodInfo methodInfo = cache[i];
+ if (genericParameterCount != GenericParameterCountAny && genericParameterCount != methodInfo.GenericParameterCount)
+ continue;
+
if (FilterApplyMethodInfo(methodInfo, bindingAttr, callConv, types) &&
(!prefixLookup || RuntimeType.FilterApplyPrefixLookup(methodInfo, name, ignoreCase)))
{
@@ -2636,7 +2646,7 @@ namespace System
#region Get All XXXInfos
public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
{
- return GetMethodCandidates(null, bindingAttr, CallingConventions.Any, null, false).ToArray();
+ return GetMethodCandidates(null, GenericParameterCountAny, bindingAttr, CallingConventions.Any, null, false).ToArray();
}
public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr)
@@ -2676,7 +2686,7 @@ namespace System
public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
{
- ListBuilder<MethodInfo> methods = GetMethodCandidates(null, bindingAttr, CallingConventions.Any, null, false);
+ ListBuilder<MethodInfo> methods = GetMethodCandidates(null, GenericParameterCountAny, bindingAttr, CallingConventions.Any, null, false);
ListBuilder<ConstructorInfo> constructors = GetConstructorCandidates(null, bindingAttr, CallingConventions.Any, null, false);
ListBuilder<PropertyInfo> properties = GetPropertyCandidates(null, bindingAttr, null, false);
ListBuilder<EventInfo> events = GetEventCandidates(null, bindingAttr, false);
@@ -2765,11 +2775,26 @@ namespace System
#endregion
#region Find XXXInfo
+
protected override MethodInfo GetMethodImpl(
String name, BindingFlags bindingAttr, Binder binder, CallingConventions callConv,
Type[] types, ParameterModifier[] modifiers)
{
- ListBuilder<MethodInfo> candidates = GetMethodCandidates(name, bindingAttr, callConv, types, false);
+ return GetMethodImplCommon(name, GenericParameterCountAny, bindingAttr, binder, callConv, types, modifiers);
+ }
+
+ protected override MethodInfo GetMethodImpl(
+ String name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConv,
+ Type[] types, ParameterModifier[] modifiers)
+ {
+ return GetMethodImplCommon(name, genericParameterCount, bindingAttr, binder, callConv, types, modifiers);
+ }
+
+ private MethodInfo GetMethodImplCommon(
+ String name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConv,
+ Type[] types, ParameterModifier[] modifiers)
+ {
+ ListBuilder<MethodInfo> candidates = GetMethodCandidates(name, genericParameterCount, bindingAttr, callConv, types, false);
if (candidates.Count == 0)
return null;
@@ -3029,7 +3054,7 @@ namespace System
// Methods
if ((type & MemberTypes.Method) != 0)
{
- methods = GetMethodCandidates(name, bindingAttr, CallingConventions.Any, null, true);
+ methods = GetMethodCandidates(name, GenericParameterCountAny, bindingAttr, CallingConventions.Any, null, true);
if (type == MemberTypes.Method)
return methods.ToArray();
totalCount += methods.Count;
@@ -3447,6 +3472,8 @@ namespace System
return RuntimeTypeHandle.IsComObject(this, false);
}
+ public sealed override bool IsByRefLike => RuntimeTypeHandle.IsByRefLike(this);
+
#if FEATURE_COMINTEROP
internal override bool IsWindowsRuntimeObjectImpl()
{
@@ -3687,6 +3714,8 @@ namespace System
if (GetGenericArguments().Length != instantiation.Length)
throw new ArgumentException(SR.Argument_GenericArgsCount, nameof(instantiation));
+ bool foundSigType = false;
+ bool foundNonRuntimeType = false;
for (int i = 0; i < instantiation.Length; i++)
{
Type instantiationElem = instantiation[i];
@@ -3697,16 +3726,24 @@ namespace System
if (rtInstantiationElem == null)
{
- Type[] instantiationCopy = new Type[instantiation.Length];
- for (int iCopy = 0; iCopy < instantiation.Length; iCopy++)
- instantiationCopy[iCopy] = instantiation[iCopy];
- instantiation = instantiationCopy;
- return System.Reflection.Emit.TypeBuilderInstantiation.MakeGenericType(this, instantiation);
+ foundNonRuntimeType = true;
+ if (instantiationElem.IsSignatureType)
+ {
+ foundSigType = true;
+ }
}
instantiationRuntimeType[i] = rtInstantiationElem;
}
+ if (foundNonRuntimeType)
+ {
+ if (foundSigType)
+ return new SignatureConstructedGenericType(this, instantiation);
+
+ return System.Reflection.Emit.TypeBuilderInstantiation.MakeGenericType(this, (Type[])(instantiation.Clone()));
+ }
+
RuntimeType[] genericParameters = GetGenericArgumentsInternal();
SanityCheckGenericArguments(instantiationRuntimeType, genericParameters);
@@ -3809,7 +3846,7 @@ namespace System
{
get
{
- return (StructLayoutAttribute)StructLayoutAttribute.GetCustomAttribute(this);
+ return PseudoCustomAttribute.GetStructLayoutCustomAttribute(this);
}
}
#endregion
@@ -4045,7 +4082,7 @@ namespace System
#endif // FEATURE_COMINTEROP && FEATURE_USE_LCID
#endregion
- #region Check that any named paramters are not null
+ #region Check that any named parameters are not null
if (namedParams != null && Array.IndexOf(namedParams, null) != -1)
// "Named parameter value must not be null."
throw new ArgumentException(SR.Arg_NamedParamNull, nameof(namedParams));
@@ -4592,7 +4629,7 @@ namespace System
}
internal Object CreateInstanceImpl(
- BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, ref StackCrawlMark stackMark)
+ BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
{
CreateInstanceCheckThis();
@@ -4609,10 +4646,12 @@ namespace System
// deal with the __COMObject case first. It is very special because from a reflection point of view it has no ctors
// so a call to GetMemberCons would fail
+ bool publicOnly = (bindingAttr & BindingFlags.NonPublic) == 0;
+ bool wrapExceptions = (bindingAttr & BindingFlags.DoNotWrapExceptions) == 0;
if (argCnt == 0 && (bindingAttr & BindingFlags.Public) != 0 && (bindingAttr & BindingFlags.Instance) != 0
&& (IsGenericCOMObjectImpl() || IsValueType))
{
- server = CreateInstanceDefaultCtor((bindingAttr & BindingFlags.NonPublic) == 0, false, true, ref stackMark);
+ server = CreateInstanceDefaultCtor(publicOnly, false, true, wrapExceptions);
}
else
{
@@ -4670,7 +4709,7 @@ namespace System
}
// fast path??
- server = Activator.CreateInstance(this, true);
+ server = Activator.CreateInstance(this, nonPublic: true, wrapExceptions: wrapExceptions);
}
else
{
@@ -4754,7 +4793,7 @@ namespace System
internal void SetEntry(ActivatorCacheEntry ace)
{
- // fill the the array backwards to hit the most recently filled entries first in GetEntry
+ // fill the array backwards to hit the most recently filled entries first in GetEntry
int index = (hash_counter - 1) & (ActivatorCache.CACHE_SIZE - 1);
hash_counter = index;
Volatile.Write(ref cache[index], ace);
@@ -4764,7 +4803,7 @@ namespace System
private static volatile ActivatorCache s_ActivatorCache;
// the slow path of CreateInstanceDefaultCtor
- internal Object CreateInstanceSlow(bool publicOnly, bool skipCheckThis, bool fillCache, ref StackCrawlMark stackMark)
+ internal Object CreateInstanceSlow(bool publicOnly, bool wrapExceptions, bool skipCheckThis, bool fillCache)
{
RuntimeMethodHandleInternal runtime_ctor = default(RuntimeMethodHandleInternal);
bool bCanBeCached = false;
@@ -4772,7 +4811,7 @@ namespace System
if (!skipCheckThis)
CreateInstanceCheckThis();
- Object instance = RuntimeTypeHandle.CreateInstance(this, publicOnly, ref bCanBeCached, ref runtime_ctor);
+ Object instance = RuntimeTypeHandle.CreateInstance(this, publicOnly, wrapExceptions, ref bCanBeCached, ref runtime_ctor);
if (bCanBeCached && fillCache)
{
@@ -4796,7 +4835,7 @@ namespace System
// fillCache is set in the SL2/3 compat mode or when called from Marshal.PtrToStructure.
[DebuggerStepThroughAttribute]
[Diagnostics.DebuggerHidden]
- internal Object CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, ref StackCrawlMark stackMark)
+ internal Object CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, bool wrapExceptions)
{
if (GetType() == typeof(ReflectionOnlyType))
throw new InvalidOperationException(SR.InvalidOperation_NotAllowedInReflectionOnly);
@@ -4829,7 +4868,7 @@ namespace System
{
ace.m_ctor(instance);
}
- catch (Exception e)
+ catch (Exception e) when (wrapExceptions)
{
throw new TargetInvocationException(e);
}
@@ -4837,7 +4876,7 @@ namespace System
return instance;
}
}
- return CreateInstanceSlow(publicOnly, skipCheckThis, fillCache, ref stackMark);
+ return CreateInstanceSlow(publicOnly, wrapExceptions, skipCheckThis, fillCache);
}
internal void InvalidateCachedNestedType()
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs b/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs
index fc7a7f01a3..eb198ba087 100644
--- a/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs
+++ b/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs
@@ -395,7 +395,7 @@ namespace System.Runtime.CompilerServices
/// </summary>
/// <remarks>
/// This property lazily instantiates the ID in a non-thread-safe manner.
- /// It must only be used by the debugger and tracing pruposes, and only in a single-threaded manner
+ /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner
/// when no other threads are in the middle of accessing this property or this.Task.
/// </remarks>
private object ObjectIdForDebugger { get { return this.Task; } }
@@ -431,8 +431,9 @@ namespace System.Runtime.CompilerServices
public static AsyncTaskMethodBuilder<TResult> Create()
{
return default(AsyncTaskMethodBuilder<TResult>);
- // NOTE: If this method is ever updated to perform more initialization,
- // ATMB.Create must also be updated to call this Create method.
+ // NOTE: If this method is ever updated to perform more initialization,
+ // other Create methods like AsyncTaskMethodBuilder.Create and
+ // AsyncValueTaskMethodBuilder.Create must be updated to call this.
}
/// <summary>Initiates the builder's execution with the associated state machine.</summary>
@@ -718,7 +719,7 @@ namespace System.Runtime.CompilerServices
/// <param name="result">The result for which we need a task.</param>
/// <returns>The completed task containing the result.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] // method looks long, but for a given TResult it results in a relatively small amount of asm
- private Task<TResult> GetTaskForResult(TResult result)
+ internal static Task<TResult> GetTaskForResult(TResult result)
{
Contract.Ensures(
EqualityComparer<TResult>.Default.Equals(result, Contract.Result<Task<TResult>>().Result),
@@ -1119,7 +1120,7 @@ namespace System.Runtime.CompilerServices
}
///<summary>
- /// Given an action, see if it is a contiunation wrapper and has a Task associated with it. If so return it (null otherwise)
+ /// Given an action, see if it is a continuation wrapper and has a Task associated with it. If so return it (null otherwise)
///</summary>
internal static Task TryGetContinuationTask(Action action)
{
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/CustomConstantAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/CustomConstantAttribute.cs
deleted file mode 100644
index 7393bb2bcd..0000000000
--- a/src/mscorlib/src/System/Runtime/CompilerServices/CustomConstantAttribute.cs
+++ /dev/null
@@ -1,29 +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.
-
-using System.Reflection;
-using System.Collections.Generic;
-
-namespace System.Runtime.CompilerServices
-{
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)]
- public abstract class CustomConstantAttribute : Attribute
- {
- public abstract Object Value { get; }
-
- internal static object GetRawConstant(CustomAttributeData attr)
- {
- foreach (CustomAttributeNamedArgument namedArgument in attr.NamedArguments)
- {
- if (namedArgument.MemberInfo.Name.Equals("Value"))
- return namedArgument.TypedValue.Value;
- }
-
- // Return DBNull to indicate that no default value is available.
- // Not to be confused with a null return which indicates a null default value.
- return DBNull.Value;
- }
- }
-}
-
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs
deleted file mode 100644
index 5155d0085d..0000000000
--- a/src/mscorlib/src/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs
+++ /dev/null
@@ -1,46 +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.
-
-using System.Reflection;
-using System.Diagnostics.Contracts;
-
-namespace System.Runtime.CompilerServices
-{
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)]
- public sealed class DateTimeConstantAttribute : CustomConstantAttribute
- {
- public DateTimeConstantAttribute(long ticks)
- {
- date = new System.DateTime(ticks);
- }
-
- public override Object Value
- {
- get
- {
- return date;
- }
- }
-
- internal static DateTime GetRawDateTimeConstant(CustomAttributeData attr)
- {
- Contract.Requires(attr.Constructor.DeclaringType == typeof(DateTimeConstantAttribute));
- Contract.Requires(attr.ConstructorArguments.Count == 1);
-
- foreach (CustomAttributeNamedArgument namedArgument in attr.NamedArguments)
- {
- if (namedArgument.MemberInfo.Name.Equals("Value"))
- {
- return new DateTime((long)namedArgument.TypedValue.Value);
- }
- }
-
- // Look at the ctor argument if the "Value" property was not explicitly defined.
- return new DateTime((long)attr.ConstructorArguments[0].Value);
- }
-
- private System.DateTime date;
- }
-}
-
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/DecimalConstantAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/DecimalConstantAttribute.cs
deleted file mode 100644
index f5fd5a21a3..0000000000
--- a/src/mscorlib/src/System/Runtime/CompilerServices/DecimalConstantAttribute.cs
+++ /dev/null
@@ -1,97 +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.
-
-
-// Note: If you add a new ctor overloads you need to update ParameterInfo.RawDefaultValue
-
-using System.Reflection;
-using System.Diagnostics;
-using System.Diagnostics.Contracts;
-using System.Collections.Generic;
-
-namespace System.Runtime.CompilerServices
-{
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)]
- public sealed class DecimalConstantAttribute : Attribute
- {
- [CLSCompliant(false)]
- public DecimalConstantAttribute(
- byte scale,
- byte sign,
- uint hi,
- uint mid,
- uint low
- )
- {
- dec = new System.Decimal((int)low, (int)mid, (int)hi, (sign != 0), scale);
- }
-
- public DecimalConstantAttribute(
- byte scale,
- byte sign,
- int hi,
- int mid,
- int low
- )
- {
- dec = new System.Decimal(low, mid, hi, (sign != 0), scale);
- }
-
-
- public System.Decimal Value
- {
- get
- {
- return dec;
- }
- }
-
- internal static Decimal GetRawDecimalConstant(CustomAttributeData attr)
- {
- Contract.Requires(attr.Constructor.DeclaringType == typeof(DecimalConstantAttribute));
-
- foreach (CustomAttributeNamedArgument namedArgument in attr.NamedArguments)
- {
- if (namedArgument.MemberInfo.Name.Equals("Value"))
- {
- // This is not possible because Decimal cannot be represented directly in the metadata.
- Debug.Assert(false, "Decimal cannot be represented directly in the metadata.");
- return (Decimal)namedArgument.TypedValue.Value;
- }
- }
-
- ParameterInfo[] parameters = attr.Constructor.GetParameters();
- Debug.Assert(parameters.Length == 5);
-
- System.Collections.Generic.IList<CustomAttributeTypedArgument> args = attr.ConstructorArguments;
- Debug.Assert(args.Count == 5);
-
- if (parameters[2].ParameterType == typeof(uint))
- {
- // DecimalConstantAttribute(byte scale, byte sign, uint hi, uint mid, uint low)
- int low = (int)(UInt32)args[4].Value;
- int mid = (int)(UInt32)args[3].Value;
- int hi = (int)(UInt32)args[2].Value;
- byte sign = (byte)args[1].Value;
- byte scale = (byte)args[0].Value;
-
- return new System.Decimal(low, mid, hi, (sign != 0), scale);
- }
- else
- {
- // DecimalConstantAttribute(byte scale, byte sign, int hi, int mid, int low)
- int low = (int)args[4].Value;
- int mid = (int)args[3].Value;
- int hi = (int)args[2].Value;
- byte sign = (byte)args[1].Value;
- byte scale = (byte)args[0].Value;
-
- return new System.Decimal(low, mid, hi, (sign != 0), scale);
- }
- }
-
- private System.Decimal dec;
- }
-}
-
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/MethodImplAttribute.cs b/src/mscorlib/src/System/Runtime/CompilerServices/MethodImplAttribute.cs
deleted file mode 100644
index 3c19ee8863..0000000000
--- a/src/mscorlib/src/System/Runtime/CompilerServices/MethodImplAttribute.cs
+++ /dev/null
@@ -1,47 +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.
-
-
-using System;
-using System.Reflection;
-
-namespace System.Runtime.CompilerServices
-{
- // This Enum matchs the miImpl flags defined in corhdr.h. It is used to specify
- // certain method properties.
-
- // Custom attribute to specify additional method properties.
- [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)]
- sealed public class MethodImplAttribute : Attribute
- {
- internal MethodImplOptions _val;
- public MethodCodeType MethodCodeType;
-
- internal MethodImplAttribute(MethodImplAttributes methodImplAttributes)
- {
- MethodImplOptions all =
- MethodImplOptions.Unmanaged | MethodImplOptions.ForwardRef | MethodImplOptions.PreserveSig |
- MethodImplOptions.InternalCall | MethodImplOptions.Synchronized |
- MethodImplOptions.NoInlining | MethodImplOptions.AggressiveInlining |
- MethodImplOptions.NoOptimization;
- _val = ((MethodImplOptions)methodImplAttributes) & all;
- }
-
- public MethodImplAttribute(MethodImplOptions methodImplOptions)
- {
- _val = methodImplOptions;
- }
-
- public MethodImplAttribute(short value)
- {
- _val = (MethodImplOptions)value;
- }
-
- public MethodImplAttribute()
- {
- }
-
- public MethodImplOptions Value { get { return _val; } }
- }
-}
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeWrappedException.cs b/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeWrappedException.cs
deleted file mode 100644
index df52f301e4..0000000000
--- a/src/mscorlib/src/System/Runtime/CompilerServices/RuntimeWrappedException.cs
+++ /dev/null
@@ -1,42 +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.
-
-/*=============================================================================
-**
-**
-**
-** Purpose: The exception class uses to wrap all non-CLS compliant exceptions.
-**
-**
-=============================================================================*/
-
-using System;
-using System.Runtime.Serialization;
-using System.Diagnostics.Contracts;
-
-namespace System.Runtime.CompilerServices
-{
- public sealed class RuntimeWrappedException : Exception
- {
- private RuntimeWrappedException(Object thrownObject)
- : base(SR.RuntimeWrappedException)
- {
- HResult = System.__HResults.COR_E_RUNTIMEWRAPPED;
- m_wrappedException = thrownObject;
- }
-
- public Object WrappedException
- {
- get { return m_wrappedException; }
- }
-
- private Object m_wrappedException;
-
- public override void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- base.GetObjectData(info, context);
- }
- }
-}
-
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/Unsafe.cs b/src/mscorlib/src/System/Runtime/CompilerServices/Unsafe.cs
index b184cd9fa8..df3c244b47 100644
--- a/src/mscorlib/src/System/Runtime/CompilerServices/Unsafe.cs
+++ b/src/mscorlib/src/System/Runtime/CompilerServices/Unsafe.cs
@@ -48,6 +48,18 @@ namespace System.Runtime.CompilerServices
}
/// <summary>
+ /// Casts the given object to the specified type.
+ /// </summary>
+ [NonVersionable]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T As<T>(object o) where T : class
+ {
+ // The body of this function will be replaced by the EE with unsafe code!!!
+ // See getILIntrinsicImplementationForUnsafe for how this happens.
+ throw new InvalidOperationException();
+ }
+
+ /// <summary>
/// Reinterprets the given reference as a reference to a value of type <typeparamref name="TTo"/>.
/// </summary>
[NonVersionable]
@@ -55,7 +67,7 @@ namespace System.Runtime.CompilerServices
public static ref TTo As<TFrom, TTo>(ref TFrom source)
{
// The body of this function will be replaced by the EE with unsafe code!!!
- // See getILIntrinsicImplementationForUnsafe for how this happens.
+ // See getILIntrinsicImplementationForUnsafe for how this happens.
throw new InvalidOperationException();
}
@@ -77,6 +89,19 @@ namespace System.Runtime.CompilerServices
/// </summary>
[NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void* Add<T>(void* source, int elementOffset)
+ {
+ // The body of this function will be replaced by the EE with unsafe code!!!
+ // See getILIntrinsicImplementationForUnsafe for how this happens.
+ typeof(T).ToString(); // Type token used by the actual method body
+ throw new InvalidOperationException();
+ }
+
+ /// <summary>
+ /// Adds an element offset to the given reference.
+ /// </summary>
+ [NonVersionable]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AddByteOffset<T>(ref T source, nuint byteOffset)
{
// The body of this function will be replaced by the EE with unsafe code!!!
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs b/src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs
index 668358995b..7b50c705b2 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/Attributes.cs
@@ -154,126 +154,9 @@ namespace System.Runtime.InteropServices
public String Value { get { return _val; } }
}
- [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.ReturnValue, Inherited = false)]
- public unsafe sealed class MarshalAsAttribute : Attribute
- {
- internal static Attribute GetCustomAttribute(RuntimeParameterInfo parameter)
- {
- return GetCustomAttribute(parameter.MetadataToken, parameter.GetRuntimeModule());
- }
-
- internal static bool IsDefined(RuntimeParameterInfo parameter)
- {
- return GetCustomAttribute(parameter) != null;
- }
-
- internal static Attribute GetCustomAttribute(RuntimeFieldInfo field)
- {
- return GetCustomAttribute(field.MetadataToken, field.GetRuntimeModule()); ;
- }
-
- internal static bool IsDefined(RuntimeFieldInfo field)
- {
- return GetCustomAttribute(field) != null;
- }
-
- internal static Attribute GetCustomAttribute(int token, RuntimeModule scope)
- {
- UnmanagedType unmanagedType, arraySubType;
- VarEnum safeArraySubType;
- int sizeParamIndex = 0, sizeConst = 0;
- string marshalTypeName = null, marshalCookie = null, safeArrayUserDefinedTypeName = null;
- int iidParamIndex = 0;
- ConstArray nativeType = ModuleHandle.GetMetadataImport(scope.GetNativeHandle()).GetFieldMarshal(token);
-
- if (nativeType.Length == 0)
- return null;
-
- MetadataImport.GetMarshalAs(nativeType,
- out unmanagedType, out safeArraySubType, out safeArrayUserDefinedTypeName, out arraySubType, out sizeParamIndex,
- out sizeConst, out marshalTypeName, out marshalCookie, out iidParamIndex);
-
- RuntimeType safeArrayUserDefinedType = safeArrayUserDefinedTypeName == null || safeArrayUserDefinedTypeName.Length == 0 ? null :
- RuntimeTypeHandle.GetTypeByNameUsingCARules(safeArrayUserDefinedTypeName, scope);
- RuntimeType marshalTypeRef = null;
-
- try
- {
- marshalTypeRef = marshalTypeName == null ? null : RuntimeTypeHandle.GetTypeByNameUsingCARules(marshalTypeName, scope);
- }
- catch (System.TypeLoadException)
- {
- // The user may have supplied a bad type name string causing this TypeLoadException
- // Regardless, we return the bad type name
- Debug.Assert(marshalTypeName != null);
- }
-
- return new MarshalAsAttribute(
- unmanagedType, safeArraySubType, safeArrayUserDefinedType, arraySubType,
- (short)sizeParamIndex, sizeConst, marshalTypeName, marshalTypeRef, marshalCookie, iidParamIndex);
- }
-
- internal MarshalAsAttribute(UnmanagedType val, VarEnum safeArraySubType, RuntimeType safeArrayUserDefinedSubType, UnmanagedType arraySubType,
- short sizeParamIndex, int sizeConst, string marshalType, RuntimeType marshalTypeRef, string marshalCookie, int iidParamIndex)
- {
- _val = val;
- SafeArraySubType = safeArraySubType;
- SafeArrayUserDefinedSubType = safeArrayUserDefinedSubType;
- IidParameterIndex = iidParamIndex;
- ArraySubType = arraySubType;
- SizeParamIndex = sizeParamIndex;
- SizeConst = sizeConst;
- MarshalType = marshalType;
- MarshalTypeRef = marshalTypeRef;
- MarshalCookie = marshalCookie;
- }
-
- internal UnmanagedType _val;
- public MarshalAsAttribute(UnmanagedType unmanagedType)
- {
- _val = unmanagedType;
- }
- public MarshalAsAttribute(short unmanagedType)
- {
- _val = (UnmanagedType)unmanagedType;
- }
- public UnmanagedType Value { get { return _val; } }
-
- // Fields used with SubType = SafeArray.
- public VarEnum SafeArraySubType;
- public Type SafeArrayUserDefinedSubType;
-
- // Field used with iid_is attribute (interface pointers).
- public int IidParameterIndex;
-
- // Fields used with SubType = ByValArray and LPArray.
- // Array size = parameter(PI) * PM + C
- public UnmanagedType ArraySubType;
- public short SizeParamIndex; // param index PI
- public int SizeConst; // constant C
-
- // Fields used with SubType = CustomMarshaler
- public String MarshalType; // Name of marshaler class
- public Type MarshalTypeRef; // Type of marshaler class
- public String MarshalCookie; // cookie to pass to marshaler
- }
-
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false)]
public sealed class ComImportAttribute : Attribute
{
- internal static Attribute GetCustomAttribute(RuntimeType type)
- {
- if ((type.Attributes & TypeAttributes.Import) == 0)
- return null;
-
- return new ComImportAttribute();
- }
-
- internal static bool IsDefined(RuntimeType type)
- {
- return (type.Attributes & TypeAttributes.Import) != 0;
- }
-
public ComImportAttribute()
{
}
@@ -290,292 +173,6 @@ namespace System.Runtime.InteropServices
public String Value { get { return _val; } }
}
- [AttributeUsage(AttributeTargets.Method, Inherited = false)]
- public sealed class PreserveSigAttribute : Attribute
- {
- internal static Attribute GetCustomAttribute(RuntimeMethodInfo method)
- {
- if ((method.GetMethodImplementationFlags() & MethodImplAttributes.PreserveSig) == 0)
- return null;
-
- return new PreserveSigAttribute();
- }
-
- internal static bool IsDefined(RuntimeMethodInfo method)
- {
- return (method.GetMethodImplementationFlags() & MethodImplAttributes.PreserveSig) != 0;
- }
-
- public PreserveSigAttribute()
- {
- }
- }
-
- [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
- public sealed class InAttribute : Attribute
- {
- internal static Attribute GetCustomAttribute(RuntimeParameterInfo parameter)
- {
- return parameter.IsIn ? new InAttribute() : null;
- }
- internal static bool IsDefined(RuntimeParameterInfo parameter)
- {
- return parameter.IsIn;
- }
-
- public InAttribute()
- {
- }
- }
-
- [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
- public sealed class OutAttribute : Attribute
- {
- internal static Attribute GetCustomAttribute(RuntimeParameterInfo parameter)
- {
- return parameter.IsOut ? new OutAttribute() : null;
- }
- internal static bool IsDefined(RuntimeParameterInfo parameter)
- {
- return parameter.IsOut;
- }
-
- public OutAttribute()
- {
- }
- }
-
- [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
- public sealed class OptionalAttribute : Attribute
- {
- internal static Attribute GetCustomAttribute(RuntimeParameterInfo parameter)
- {
- return parameter.IsOptional ? new OptionalAttribute() : null;
- }
- internal static bool IsDefined(RuntimeParameterInfo parameter)
- {
- return parameter.IsOptional;
- }
-
- public OptionalAttribute()
- {
- }
- }
-
- [Flags]
- public enum DllImportSearchPath
- {
- UseDllDirectoryForDependencies = 0x100,
- ApplicationDirectory = 0x200,
- UserDirectories = 0x400,
- System32 = 0x800,
- SafeDirectories = 0x1000,
- AssemblyDirectory = 0x2,
- LegacyBehavior = 0x0
- }
-
- [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method, AllowMultiple = false)]
- public sealed class DefaultDllImportSearchPathsAttribute : Attribute
- {
- internal DllImportSearchPath _paths;
- public DefaultDllImportSearchPathsAttribute(DllImportSearchPath paths)
- {
- _paths = paths;
- }
-
- public DllImportSearchPath Paths { get { return _paths; } }
- }
-
- [AttributeUsage(AttributeTargets.Method, Inherited = false)]
- public unsafe sealed class DllImportAttribute : Attribute
- {
- internal static Attribute GetCustomAttribute(RuntimeMethodInfo method)
- {
- if ((method.Attributes & MethodAttributes.PinvokeImpl) == 0)
- return null;
-
- MetadataImport scope = ModuleHandle.GetMetadataImport(method.Module.ModuleHandle.GetRuntimeModule());
- string entryPoint, dllName = null;
- int token = method.MetadataToken;
- PInvokeAttributes flags = 0;
-
- scope.GetPInvokeMap(token, out flags, out entryPoint, out dllName);
-
- CharSet charSet = CharSet.None;
-
- switch (flags & PInvokeAttributes.CharSetMask)
- {
- case PInvokeAttributes.CharSetNotSpec: charSet = CharSet.None; break;
- case PInvokeAttributes.CharSetAnsi: charSet = CharSet.Ansi; break;
- case PInvokeAttributes.CharSetUnicode: charSet = CharSet.Unicode; break;
- case PInvokeAttributes.CharSetAuto: charSet = CharSet.Auto; break;
-
- // Invalid: default to CharSet.None
- default: break;
- }
-
- CallingConvention callingConvention = CallingConvention.Cdecl;
-
- switch (flags & PInvokeAttributes.CallConvMask)
- {
- case PInvokeAttributes.CallConvWinapi: callingConvention = CallingConvention.Winapi; break;
- case PInvokeAttributes.CallConvCdecl: callingConvention = CallingConvention.Cdecl; break;
- case PInvokeAttributes.CallConvStdcall: callingConvention = CallingConvention.StdCall; break;
- case PInvokeAttributes.CallConvThiscall: callingConvention = CallingConvention.ThisCall; break;
- case PInvokeAttributes.CallConvFastcall: callingConvention = CallingConvention.FastCall; break;
-
- // Invalid: default to CallingConvention.Cdecl
- default: break;
- }
-
- bool exactSpelling = (flags & PInvokeAttributes.NoMangle) != 0;
- bool setLastError = (flags & PInvokeAttributes.SupportsLastError) != 0;
- bool bestFitMapping = (flags & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitEnabled;
- bool throwOnUnmappableChar = (flags & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharEnabled;
- bool preserveSig = (method.GetMethodImplementationFlags() & MethodImplAttributes.PreserveSig) != 0;
-
- return new DllImportAttribute(
- dllName, entryPoint, charSet, exactSpelling, setLastError, preserveSig,
- callingConvention, bestFitMapping, throwOnUnmappableChar);
- }
-
- internal static bool IsDefined(RuntimeMethodInfo method)
- {
- return (method.Attributes & MethodAttributes.PinvokeImpl) != 0;
- }
-
-
- internal DllImportAttribute(
- string dllName, string entryPoint, CharSet charSet, bool exactSpelling, bool setLastError, bool preserveSig,
- CallingConvention callingConvention, bool bestFitMapping, bool throwOnUnmappableChar)
- {
- _val = dllName;
- EntryPoint = entryPoint;
- CharSet = charSet;
- ExactSpelling = exactSpelling;
- SetLastError = setLastError;
- PreserveSig = preserveSig;
- CallingConvention = callingConvention;
- BestFitMapping = bestFitMapping;
- ThrowOnUnmappableChar = throwOnUnmappableChar;
- }
-
- internal String _val;
-
- public DllImportAttribute(String dllName)
- {
- _val = dllName;
- }
- public String Value { get { return _val; } }
-
- public String EntryPoint;
- public CharSet CharSet;
- public bool SetLastError;
- public bool ExactSpelling;
- public bool PreserveSig;
- public CallingConvention CallingConvention;
- public bool BestFitMapping;
- public bool ThrowOnUnmappableChar;
- }
-
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
- public unsafe sealed class StructLayoutAttribute : Attribute
- {
- private const int DEFAULT_PACKING_SIZE = 8;
-
- internal static Attribute GetCustomAttribute(RuntimeType type)
- {
- if (!IsDefined(type))
- return null;
-
- int pack = 0, size = 0;
- LayoutKind layoutKind = LayoutKind.Auto;
- switch (type.Attributes & TypeAttributes.LayoutMask)
- {
- case TypeAttributes.ExplicitLayout: layoutKind = LayoutKind.Explicit; break;
- case TypeAttributes.AutoLayout: layoutKind = LayoutKind.Auto; break;
- case TypeAttributes.SequentialLayout: layoutKind = LayoutKind.Sequential; break;
- default: Contract.Assume(false); break;
- }
-
- CharSet charSet = CharSet.None;
- switch (type.Attributes & TypeAttributes.StringFormatMask)
- {
- case TypeAttributes.AnsiClass: charSet = CharSet.Ansi; break;
- case TypeAttributes.AutoClass: charSet = CharSet.Auto; break;
- case TypeAttributes.UnicodeClass: charSet = CharSet.Unicode; break;
- default: Contract.Assume(false); break;
- }
- type.GetRuntimeModule().MetadataImport.GetClassLayout(type.MetadataToken, out pack, out size);
-
- // Metadata parameter checking should not have allowed 0 for packing size.
- // The runtime later converts a packing size of 0 to 8 so do the same here
- // because it's more useful from a user perspective.
- if (pack == 0)
- pack = DEFAULT_PACKING_SIZE;
-
- return new StructLayoutAttribute(layoutKind, pack, size, charSet);
- }
-
- internal static bool IsDefined(RuntimeType type)
- {
- if (type.IsInterface || type.HasElementType || type.IsGenericParameter)
- return false;
-
- return true;
- }
-
- internal LayoutKind _val;
-
- internal StructLayoutAttribute(LayoutKind layoutKind, int pack, int size, CharSet charSet)
- {
- _val = layoutKind;
- Pack = pack;
- Size = size;
- CharSet = charSet;
- }
-
- public StructLayoutAttribute(LayoutKind layoutKind)
- {
- _val = layoutKind;
- }
- public StructLayoutAttribute(short layoutKind)
- {
- _val = (LayoutKind)layoutKind;
- }
- public LayoutKind Value { get { return _val; } }
- public int Pack;
- public int Size;
- public CharSet CharSet;
- }
-
- [AttributeUsage(AttributeTargets.Field, Inherited = false)]
- public unsafe sealed class FieldOffsetAttribute : Attribute
- {
- internal static Attribute GetCustomAttribute(RuntimeFieldInfo field)
- {
- int fieldOffset;
-
- if (field.DeclaringType != null &&
- field.GetRuntimeModule().MetadataImport.GetFieldOffset(field.DeclaringType.MetadataToken, field.MetadataToken, out fieldOffset))
- return new FieldOffsetAttribute(fieldOffset);
-
- return null;
- }
-
- internal static bool IsDefined(RuntimeFieldInfo field)
- {
- return GetCustomAttribute(field) != null;
- }
-
- internal int _val;
- public FieldOffsetAttribute(int offset)
- {
- _val = offset;
- }
- public int Value { get { return _val; } }
- }
-
[AttributeUsage(AttributeTargets.Interface, Inherited = false)]
public sealed class CoClassAttribute : Attribute
{
@@ -588,32 +185,5 @@ namespace System.Runtime.InteropServices
public Type CoClass { get { return _CoClass; } }
}
-
- [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
- public sealed class BestFitMappingAttribute : Attribute
- {
- internal bool _bestFitMapping;
-
- public BestFitMappingAttribute(bool BestFitMapping)
- {
- _bestFitMapping = BestFitMapping;
- }
-
- public bool BestFitMapping { get { return _bestFitMapping; } }
- public bool ThrowOnUnmappableChar;
- }
-
- [AttributeUsage(AttributeTargets.Module, Inherited = false)]
- public sealed class DefaultCharSetAttribute : Attribute
- {
- internal CharSet _CharSet;
-
- public DefaultCharSetAttribute(CharSet charSet)
- {
- _CharSet = charSet;
- }
-
- public CharSet CharSet { get { return _CharSet; } }
- }
}
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/COMException.cs b/src/mscorlib/src/System/Runtime/InteropServices/COMException.cs
index 8ee10ed349..6d9927fd97 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/COMException.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/COMException.cs
@@ -27,19 +27,19 @@ namespace System.Runtime.InteropServices
public COMException()
: base(SR.Arg_COMException)
{
- HResult = __HResults.E_FAIL;
+ HResult = HResults.E_FAIL;
}
public COMException(String message)
: base(message)
{
- HResult = __HResults.E_FAIL;
+ HResult = HResults.E_FAIL;
}
public COMException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.E_FAIL;
+ HResult = HResults.E_FAIL;
}
public COMException(String message, int errorCode)
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/ComEventsHelper.cs b/src/mscorlib/src/System/Runtime/InteropServices/ComEventsHelper.cs
index fe69f619fe..50e9ea6fee 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/ComEventsHelper.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/ComEventsHelper.cs
@@ -114,7 +114,7 @@ namespace System.Runtime.InteropServices
/// <param name="rcw">COM object firing the events the caller would like to respond to</param>
/// <param name="iid">identifier of the source interface used by COM object to fire events</param>
/// <param name="dispid">dispatch identifier of the method on the source interface</param>
- /// <param name="d">delegate to invoke when specifed COM event is fired</param>
+ /// <param name="d">delegate to invoke when specified COM event is fired</param>
public static void Combine(object rcw, Guid iid, int dispid, System.Delegate d)
{
rcw = UnwrapIfTransparentProxy(rcw);
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/CriticalHandle.cs b/src/mscorlib/src/System/Runtime/InteropServices/CriticalHandle.cs
index 734a494bdb..cc3462275e 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/CriticalHandle.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/CriticalHandle.cs
@@ -109,7 +109,7 @@ using System.IO;
get { return handle == IntPtr.Zero; }
}
- [DllImport(Win32Native.KERNEL32), SuppressUnmanagedCodeSecurity, ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ [DllImport(Interop.Libraries.Kernel32), SuppressUnmanagedCodeSecurity, ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
private static extern bool CloseHandle(IntPtr handle);
override protected bool ReleaseHandle()
@@ -123,20 +123,14 @@ using System.IO;
Note that when returning a CriticalHandle like this, P/Invoke will call your
classes default constructor.
- [DllImport(Win32Native.KERNEL32)]
+ [DllImport(Interop.Libraries.Kernel32)]
private static extern MyCriticalHandleSubclass CreateHandle(int someState);
*/
namespace System.Runtime.InteropServices
{
- // This class should not be serializable - it's a handle. We require unmanaged
- // code permission to subclass CriticalHandle to prevent people from writing a
- // subclass and suddenly being able to run arbitrary native code with the
- // same signature as CloseHandle. This is technically a little redundant, but
- // we'll do this to ensure we've cut off all attack vectors. Similarly, all
- // methods have a link demand to ensure untrusted code cannot directly edit
- // or alter a handle.
+ // This class should not be serializable - it's a handle
public abstract class CriticalHandle : CriticalFinalizerObject, IDisposable
{
// ! Do not add or rearrange fields as the EE depends on this layout.
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/InvalidComObjectException.cs b/src/mscorlib/src/System/Runtime/InteropServices/InvalidComObjectException.cs
index bf89df94bb..c3ce68df4b 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/InvalidComObjectException.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/InvalidComObjectException.cs
@@ -22,19 +22,19 @@ namespace System.Runtime.InteropServices
public InvalidComObjectException()
: base(SR.Arg_InvalidComObjectException)
{
- HResult = __HResults.COR_E_INVALIDCOMOBJECT;
+ HResult = HResults.COR_E_INVALIDCOMOBJECT;
}
public InvalidComObjectException(String message)
: base(message)
{
- HResult = __HResults.COR_E_INVALIDCOMOBJECT;
+ HResult = HResults.COR_E_INVALIDCOMOBJECT;
}
public InvalidComObjectException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_INVALIDCOMOBJECT;
+ HResult = HResults.COR_E_INVALIDCOMOBJECT;
}
protected InvalidComObjectException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs b/src/mscorlib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs
index 412853920e..f61010cda3 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs
@@ -21,19 +21,19 @@ namespace System.Runtime.InteropServices
public InvalidOleVariantTypeException()
: base(SR.Arg_InvalidOleVariantTypeException)
{
- HResult = __HResults.COR_E_INVALIDOLEVARIANTTYPE;
+ HResult = HResults.COR_E_INVALIDOLEVARIANTTYPE;
}
public InvalidOleVariantTypeException(String message)
: base(message)
{
- HResult = __HResults.COR_E_INVALIDOLEVARIANTTYPE;
+ HResult = HResults.COR_E_INVALIDOLEVARIANTTYPE;
}
public InvalidOleVariantTypeException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_INVALIDOLEVARIANTTYPE;
+ HResult = HResults.COR_E_INVALIDOLEVARIANTTYPE;
}
protected InvalidOleVariantTypeException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs b/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs
index 03750bcb8b..1567e0dd1c 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs
@@ -930,7 +930,6 @@ namespace System.Runtime.InteropServices
// Creates a new instance of "structuretype" and marshals data from a
// native memory block to it.
//====================================================================
- [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
public static Object PtrToStructure(IntPtr ptr, Type structureType)
{
if (ptr == IntPtr.Zero) return null;
@@ -946,9 +945,7 @@ namespace System.Runtime.InteropServices
if (rt == null)
throw new ArgumentException(SR.Arg_MustBeType, nameof(structureType));
- StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
-
- Object structure = rt.CreateInstanceDefaultCtor(false /*publicOnly*/, false /*skipCheckThis*/, false /*fillCache*/, ref stackMark);
+ Object structure = rt.CreateInstanceDefaultCtor(false /*publicOnly*/, false /*skipCheckThis*/, false /*fillCache*/, true /*wrapExceptions*/);
PtrToStructureHelper(ptr, structure, true);
return structure;
}
@@ -1742,15 +1739,15 @@ namespace System.Runtime.InteropServices
return obj;
}
- [DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)]
+ [DllImport(Interop.Libraries.Ole32, PreserveSig = false)]
[SuppressUnmanagedCodeSecurity]
private static extern void CreateBindCtx(UInt32 reserved, out IBindCtx ppbc);
- [DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)]
+ [DllImport(Interop.Libraries.Ole32, PreserveSig = false)]
[SuppressUnmanagedCodeSecurity]
private static extern void MkParseDisplayName(IBindCtx pbc, [MarshalAs(UnmanagedType.LPWStr)] String szUserName, out UInt32 pchEaten, out IMoniker ppmk);
- [DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)]
+ [DllImport(Interop.Libraries.Ole32, PreserveSig = false)]
[SuppressUnmanagedCodeSecurity]
private static extern void BindMoniker(IMoniker pmk, UInt32 grfOpt, ref Guid iidResult, [MarshalAs(UnmanagedType.Interface)] out Object ppvResult);
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs b/src/mscorlib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs
index 75b291af30..48e2dcf411 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs
@@ -22,19 +22,19 @@ namespace System.Runtime.InteropServices
public MarshalDirectiveException()
: base(SR.Arg_MarshalDirectiveException)
{
- HResult = __HResults.COR_E_MARSHALDIRECTIVE;
+ HResult = HResults.COR_E_MARSHALDIRECTIVE;
}
public MarshalDirectiveException(String message)
: base(message)
{
- HResult = __HResults.COR_E_MARSHALDIRECTIVE;
+ HResult = HResults.COR_E_MARSHALDIRECTIVE;
}
public MarshalDirectiveException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_MARSHALDIRECTIVE;
+ HResult = HResults.COR_E_MARSHALDIRECTIVE;
}
protected MarshalDirectiveException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/SEHException.cs b/src/mscorlib/src/System/Runtime/InteropServices/SEHException.cs
index d61e79757c..832c4d289e 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/SEHException.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/SEHException.cs
@@ -24,19 +24,19 @@ namespace System.Runtime.InteropServices
public SEHException()
: base()
{
- HResult = __HResults.E_FAIL;
+ HResult = HResults.E_FAIL;
}
public SEHException(String message)
: base(message)
{
- HResult = __HResults.E_FAIL;
+ HResult = HResults.E_FAIL;
}
public SEHException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.E_FAIL;
+ HResult = HResults.E_FAIL;
}
protected SEHException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs b/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs
index 9df858f0b1..7109b4fe51 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs
@@ -21,19 +21,19 @@ namespace System.Runtime.InteropServices
public SafeArrayRankMismatchException()
: base(SR.Arg_SafeArrayRankMismatchException)
{
- HResult = __HResults.COR_E_SAFEARRAYRANKMISMATCH;
+ HResult = HResults.COR_E_SAFEARRAYRANKMISMATCH;
}
public SafeArrayRankMismatchException(String message)
: base(message)
{
- HResult = __HResults.COR_E_SAFEARRAYRANKMISMATCH;
+ HResult = HResults.COR_E_SAFEARRAYRANKMISMATCH;
}
public SafeArrayRankMismatchException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_SAFEARRAYRANKMISMATCH;
+ HResult = HResults.COR_E_SAFEARRAYRANKMISMATCH;
}
protected SafeArrayRankMismatchException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs b/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs
index 4b03691e82..13a6cf62a8 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs
@@ -22,19 +22,19 @@ namespace System.Runtime.InteropServices
public SafeArrayTypeMismatchException()
: base(SR.Arg_SafeArrayTypeMismatchException)
{
- HResult = __HResults.COR_E_SAFEARRAYTYPEMISMATCH;
+ HResult = HResults.COR_E_SAFEARRAYTYPEMISMATCH;
}
public SafeArrayTypeMismatchException(String message)
: base(message)
{
- HResult = __HResults.COR_E_SAFEARRAYTYPEMISMATCH;
+ HResult = HResults.COR_E_SAFEARRAYTYPEMISMATCH;
}
public SafeArrayTypeMismatchException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_SAFEARRAYTYPEMISMATCH;
+ HResult = HResults.COR_E_SAFEARRAYTYPEMISMATCH;
}
protected SafeArrayTypeMismatchException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/SafeBuffer.cs b/src/mscorlib/src/System/Runtime/InteropServices/SafeBuffer.cs
index 73a3721801..6e6d6ee2b6 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/SafeBuffer.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/SafeBuffer.cs
@@ -109,7 +109,7 @@ namespace System.Runtime.InteropServices
}
/// <summary>
- /// Specifies the the size of the region in memory, as the number of
+ /// Specifies the size of the region in memory, as the number of
/// elements in an array. Must be called before using the SafeBuffer.
/// </summary>
[CLSCompliant(false)]
@@ -126,7 +126,7 @@ namespace System.Runtime.InteropServices
}
/// <summary>
- /// Specifies the the size of the region in memory, as the number of
+ /// Specifies the size of the region in memory, as the number of
/// elements in an array. Must be called before using the SafeBuffer.
/// </summary>
[CLSCompliant(false)]
@@ -173,15 +173,10 @@ namespace System.Runtime.InteropServices
pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- }
- finally
- {
- bool junk = false;
- DangerousAddRef(ref junk);
- pointer = (byte*)handle;
- }
+
+ bool junk = false;
+ DangerousAddRef(ref junk);
+ pointer = (byte*)handle;
}
public void ReleasePointer()
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/BindableVectorToListAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/BindableVectorToListAdapter.cs
index 539b8020b8..f0be957f8c 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/BindableVectorToListAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/BindableVectorToListAdapter.cs
@@ -167,7 +167,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
throw new ArgumentOutOfRangeException(nameof(index));
throw;
@@ -185,7 +185,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
throw new ArgumentOutOfRangeException(nameof(index));
throw;
@@ -203,7 +203,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
throw new ArgumentOutOfRangeException(nameof(index));
throw;
@@ -221,7 +221,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
throw new ArgumentOutOfRangeException(nameof(index));
throw;
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs
index aa0f3ba056..75774caae2 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs
@@ -134,7 +134,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public char GetChar16()
{
if (this.Type != PropertyType.Char16)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Char16"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Char16"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (char)_data;
}
@@ -143,7 +143,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public Boolean GetBoolean()
{
if (this.Type != PropertyType.Boolean)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Boolean"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Boolean"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (bool)_data;
}
@@ -166,7 +166,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public DateTimeOffset GetDateTime()
{
if (this.Type != PropertyType.DateTime)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "DateTime"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "DateTime"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (DateTimeOffset)_data;
}
@@ -175,7 +175,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public TimeSpan GetTimeSpan()
{
if (this.Type != PropertyType.TimeSpan)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "TimeSpan"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "TimeSpan"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (TimeSpan)_data;
}
@@ -184,7 +184,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public Point GetPoint()
{
if (this.Type != PropertyType.Point)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Point"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Point"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return Unbox<Point>(IReferenceFactory.s_pointType);
@@ -194,7 +194,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public Size GetSize()
{
if (this.Type != PropertyType.Size)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Size"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Size"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return Unbox<Size>(IReferenceFactory.s_sizeType);
@@ -204,7 +204,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public Rect GetRect()
{
if (this.Type != PropertyType.Rect)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Rect"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Rect"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return Unbox<Rect>(IReferenceFactory.s_rectType);
@@ -268,7 +268,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public char[] GetChar16Array()
{
if (this.Type != PropertyType.Char16Array)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Char16[]"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Char16[]"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (char[])_data;
}
@@ -277,7 +277,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public Boolean[] GetBooleanArray()
{
if (this.Type != PropertyType.BooleanArray)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Boolean[]"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Boolean[]"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (bool[])_data;
}
@@ -292,7 +292,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public Object[] GetInspectableArray()
{
if (this.Type != PropertyType.InspectableArray)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Inspectable[]"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Inspectable[]"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (Object[])_data;
}
@@ -307,7 +307,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public DateTimeOffset[] GetDateTimeArray()
{
if (this.Type != PropertyType.DateTimeArray)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "DateTimeOffset[]"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "DateTimeOffset[]"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (DateTimeOffset[])_data;
}
@@ -316,7 +316,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public TimeSpan[] GetTimeSpanArray()
{
if (this.Type != PropertyType.TimeSpanArray)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "TimeSpan[]"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "TimeSpan[]"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (TimeSpan[])_data;
}
@@ -325,7 +325,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public Point[] GetPointArray()
{
if (this.Type != PropertyType.PointArray)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Point[]"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Point[]"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return UnboxArray<Point>(IReferenceFactory.s_pointType);
@@ -335,7 +335,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public Size[] GetSizeArray()
{
if (this.Type != PropertyType.SizeArray)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Size[]"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Size[]"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
@@ -346,7 +346,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
public Rect[] GetRectArray()
{
if (this.Type != PropertyType.RectArray)
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Rect[]"), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, "Rect[]"), HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return UnboxArray<Rect>(IReferenceFactory.s_rectType);
@@ -364,7 +364,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
Array dataArray = _data as Array;
if (dataArray == null)
{
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, typeof (T).MakeArrayType().Name), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, this.Type, typeof (T).MakeArrayType().Name), HResults.TYPE_E_TYPEMISMATCH);
}
// Array types are 1024 larger than their equivilent scalar counterpart
@@ -408,7 +408,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
// should not attempt coersion, even if the underlying value is technically convertable
if (!IsCoercable(type, value) && type != PropertyType.Inspectable)
{
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, type, typeof (T).Name), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, type, typeof (T).Name), HResults.TYPE_E_TYPEMISMATCH);
}
try
@@ -438,15 +438,15 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (FormatException)
{
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, type, typeof (T).Name), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, type, typeof (T).Name), HResults.TYPE_E_TYPEMISMATCH);
}
catch (InvalidCastException)
{
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, type, typeof (T).Name), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, type, typeof (T).Name), HResults.TYPE_E_TYPEMISMATCH);
}
catch (OverflowException)
{
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueCoersion, type, value, typeof (T).Name), __HResults.DISP_E_OVERFLOW);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueCoersion, type, value, typeof (T).Name), HResults.DISP_E_OVERFLOW);
}
// If the property type is IInspectable, and we have a nested IPropertyValue, then we need
@@ -497,7 +497,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
// Otherwise, this is an invalid coersion
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, type, typeof (T).Name), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, type, typeof (T).Name), HResults.TYPE_E_TYPEMISMATCH);
}
private static bool IsCoercable(PropertyType type, object data)
@@ -539,7 +539,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (_data.GetType() != expectedBoxedType)
{
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, _data.GetType(), expectedBoxedType.Name), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, _data.GetType(), expectedBoxedType.Name), HResults.TYPE_E_TYPEMISMATCH);
}
T unboxed = new T();
@@ -563,7 +563,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
Array dataArray = _data as Array;
if (dataArray == null || _data.GetType().GetElementType() != expectedArrayElementType)
{
- throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, _data.GetType(), expectedArrayElementType.MakeArrayType().Name), __HResults.TYPE_E_TYPEMISMATCH);
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_WinRTIPropertyValueElement, _data.GetType(), expectedArrayElementType.MakeArrayType().Name), HResults.TYPE_E_TYPEMISMATCH);
}
T[] converted = new T[dataArray.Length];
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ConstantSplittableMap.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ConstantSplittableMap.cs
index 0ed0bb3f7b..5a4a5e8a7b 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ConstantSplittableMap.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ConstantSplittableMap.cs
@@ -102,7 +102,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (!found)
{
Exception e = new KeyNotFoundException(SR.Arg_KeyNotFound);
- e.SetErrorCode(__HResults.E_BOUNDS);
+ e.SetErrorCode(HResults.E_BOUNDS);
throw e;
}
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryToMapAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryToMapAdapter.cs
index bb54d49b60..3a1f8c4f4a 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryToMapAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/DictionaryToMapAdapter.cs
@@ -42,7 +42,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (!keyFound)
{
Exception e = new KeyNotFoundException(SR.Arg_KeyNotFound);
- e.SetErrorCode(__HResults.E_BOUNDS);
+ e.SetErrorCode(HResults.E_BOUNDS);
throw e;
}
@@ -97,7 +97,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (!removed)
{
Exception e = new KeyNotFoundException(SR.Arg_KeyNotFound);
- e.SetErrorCode(__HResults.E_BOUNDS);
+ e.SetErrorCode(HResults.E_BOUNDS);
throw e;
}
}
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EnumeratorToIteratorAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EnumeratorToIteratorAdapter.cs
index 75b8480eeb..70a9da6974 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EnumeratorToIteratorAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/EnumeratorToIteratorAdapter.cs
@@ -92,7 +92,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (!m_hasCurrent)
{
- throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, null);
+ throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, null);
}
return m_enumerator.Current;
@@ -131,7 +131,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (InvalidOperationException e)
{
- throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_CHANGED_STATE, e);
+ throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_CHANGED_STATE, e);
}
return m_hasCurrent;
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IMapViewToIReadOnlyDictionaryAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IMapViewToIReadOnlyDictionaryAdapter.cs
index 0900012338..04958c3bd5 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IMapViewToIReadOnlyDictionaryAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IMapViewToIReadOnlyDictionaryAdapter.cs
@@ -92,7 +92,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex) // Still may hit this case due to a race condition
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
{
value = default(V);
return false;
@@ -113,7 +113,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
throw new KeyNotFoundException(SR.Arg_KeyNotFound);
throw;
}
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyDictionaryToIMapViewAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyDictionaryToIMapViewAdapter.cs
index 73daf876cd..eaf5f471de 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyDictionaryToIMapViewAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyDictionaryToIMapViewAdapter.cs
@@ -41,7 +41,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (!keyFound)
{
Exception e = new KeyNotFoundException(SR.Arg_KeyNotFound);
- e.SetErrorCode(__HResults.E_BOUNDS);
+ e.SetErrorCode(HResults.E_BOUNDS);
throw e;
}
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListToIVectorViewAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListToIVectorViewAdapter.cs
index 5dce7dfc8d..9b87bee372 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListToIVectorViewAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListToIVectorViewAdapter.cs
@@ -43,7 +43,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (ArgumentOutOfRangeException ex)
{
- ex.SetErrorCode(__HResults.E_BOUNDS);
+ ex.SetErrorCode(HResults.E_BOUNDS);
throw;
}
}
@@ -127,7 +127,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (((uint)Int32.MaxValue) <= index || index >= (uint)listCapacity)
{
Exception e = new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexLargerThanMaxValue);
- e.SetErrorCode(__HResults.E_BOUNDS);
+ e.SetErrorCode(HResults.E_BOUNDS);
throw e;
}
}
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IVectorViewToIReadOnlyListAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IVectorViewToIReadOnlyListAdapter.cs
index 76b0fff00d..5090323572 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IVectorViewToIReadOnlyListAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IVectorViewToIReadOnlyListAdapter.cs
@@ -50,7 +50,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
throw new ArgumentOutOfRangeException(nameof(index));
throw;
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs
index 417476dbbe..ccef9d92bf 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IteratorToEnumeratorAdapter.cs
@@ -182,7 +182,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
catch (Exception e)
{
// Translate E_CHANGED_STATE into an InvalidOperationException for an updated enumeration
- if (Marshal.GetHRForException(e) == __HResults.E_CHANGED_STATE)
+ if (Marshal.GetHRForException(e) == HResults.E_CHANGED_STATE)
{
ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
}
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorAdapter.cs
index 5f12322ea8..09fdc3b49e 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorAdapter.cs
@@ -44,7 +44,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (ArgumentOutOfRangeException ex)
{
- throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
+ throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
}
}
@@ -90,7 +90,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (ArgumentOutOfRangeException ex)
{
- throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
+ throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
}
}
@@ -110,7 +110,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
catch (ArgumentOutOfRangeException ex)
{
// Change error code to match what WinRT expects
- ex.SetErrorCode(__HResults.E_BOUNDS);
+ ex.SetErrorCode(HResults.E_BOUNDS);
throw;
}
}
@@ -128,7 +128,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
catch (ArgumentOutOfRangeException ex)
{
// Change error code to match what WinRT expects
- ex.SetErrorCode(__HResults.E_BOUNDS);
+ ex.SetErrorCode(HResults.E_BOUNDS);
throw;
}
}
@@ -147,7 +147,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (_this.Count == 0)
{
Exception e = new InvalidOperationException(SR.InvalidOperation_CannotRemoveLastFromEmptyCollection);
- e.SetErrorCode(__HResults.E_BOUNDS);
+ e.SetErrorCode(HResults.E_BOUNDS);
throw e;
}
@@ -171,7 +171,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (((uint)Int32.MaxValue) <= index || index >= (uint)listCapacity)
{
Exception e = new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexLargerThanMaxValue);
- e.SetErrorCode(__HResults.E_BOUNDS);
+ e.SetErrorCode(HResults.E_BOUNDS);
throw e;
}
}
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorViewAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorViewAdapter.cs
index fc02bedfa6..75c9b9d086 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorViewAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToBindableVectorViewAdapter.cs
@@ -39,7 +39,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (((uint)Int32.MaxValue) <= index || index >= (uint)listCapacity)
{
Exception e = new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexLargerThanMaxValue);
- e.SetErrorCode(__HResults.E_BOUNDS);
+ e.SetErrorCode(HResults.E_BOUNDS);
throw e;
}
}
@@ -64,7 +64,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (ArgumentOutOfRangeException ex)
{
- throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
+ throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
}
}
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToVectorAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToVectorAdapter.cs
index 87330e2559..03c48b546b 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToVectorAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/ListToVectorAdapter.cs
@@ -44,7 +44,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (ArgumentOutOfRangeException ex)
{
- throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
+ throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
}
}
@@ -99,7 +99,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (ArgumentOutOfRangeException ex)
{
- throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
+ throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
}
}
@@ -119,7 +119,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
catch (ArgumentOutOfRangeException ex)
{
// Change error code to match what WinRT expects
- ex.SetErrorCode(__HResults.E_BOUNDS);
+ ex.SetErrorCode(HResults.E_BOUNDS);
throw;
}
}
@@ -137,7 +137,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
catch (ArgumentOutOfRangeException ex)
{
// Change error code to match what WinRT expects
- ex.SetErrorCode(__HResults.E_BOUNDS);
+ ex.SetErrorCode(HResults.E_BOUNDS);
throw;
}
}
@@ -156,7 +156,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (_this.Count == 0)
{
Exception e = new InvalidOperationException(SR.InvalidOperation_CannotRemoveLastFromEmptyCollection);
- e.SetErrorCode(__HResults.E_BOUNDS);
+ e.SetErrorCode(HResults.E_BOUNDS);
throw e;
}
@@ -202,7 +202,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (((uint)Int32.MaxValue) <= index || index >= (uint)listCapacity)
{
Exception e = new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexLargerThanMaxValue);
- e.SetErrorCode(__HResults.E_BOUNDS);
+ e.SetErrorCode(HResults.E_BOUNDS);
throw e;
}
}
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapToDictionaryAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapToDictionaryAdapter.cs
index 224a266b07..13d1aeb8bc 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapToDictionaryAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/MapToDictionaryAdapter.cs
@@ -113,7 +113,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
return false;
throw;
@@ -157,7 +157,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
throw new KeyNotFoundException(SR.Arg_KeyNotFound);
throw;
}
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorToListAdapter.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorToListAdapter.cs
index 56e62a25e7..90381f6be2 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorToListAdapter.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/VectorToListAdapter.cs
@@ -102,7 +102,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
throw new ArgumentOutOfRangeException(nameof(index));
throw;
@@ -120,7 +120,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
throw new ArgumentOutOfRangeException(nameof(index));
throw;
@@ -138,7 +138,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
throw new ArgumentOutOfRangeException(nameof(index));
throw;
@@ -156,7 +156,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
catch (Exception ex)
{
- if (__HResults.E_BOUNDS == ex._HResult)
+ if (HResults.E_BOUNDS == ex._HResult)
throw new ArgumentOutOfRangeException(nameof(index));
throw;
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs
index 6c87739b6b..1d07a022f4 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs
@@ -826,7 +826,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
/// for more than a few instructions (in particular, we never call event APIs
/// or in fact any non-trivial API while holding the spin lock).
///
- /// Currently this ReaderWriterLock does not support recurision, however it is
+ /// Currently this ReaderWriterLock does not support recursion, however it is
/// not hard to add
/// </summary>
internal class MyReaderWriterLock
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/Vector128.cs b/src/mscorlib/src/System/Runtime/Intrinsics/Vector128.cs
new file mode 100644
index 0000000000..8e32f1a9dd
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/Vector128.cs
@@ -0,0 +1,11 @@
+// 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;
+
+namespace System.Runtime.Intrinsics
+{
+ [StructLayout(LayoutKind.Sequential, Size = 16)]
+ public struct Vector128<T> where T : struct {}
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/Vector256.cs b/src/mscorlib/src/System/Runtime/Intrinsics/Vector256.cs
new file mode 100644
index 0000000000..ba1e11d003
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/Vector256.cs
@@ -0,0 +1,11 @@
+// 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;
+
+namespace System.Runtime.Intrinsics
+{
+ [StructLayout(LayoutKind.Sequential, Size = 32)]
+ public struct Vector256<T> where T : struct {}
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Aes.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Aes.cs
new file mode 100644
index 0000000000..43aebc990c
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Aes.cs
@@ -0,0 +1,74 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel AES hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Aes
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// __m128i _mm_aesdec_si128 (__m128i a, __m128i RoundKey)
+ /// </summary>
+ public static Vector128<sbyte> Decrypt(Vector128<sbyte> value, Vector128<sbyte> roundKey) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_aesdec_si128 (__m128i a, __m128i RoundKey)
+ /// </summary>
+ public static Vector128<byte> Decrypt(Vector128<byte> value, Vector128<byte> roundKey) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_aesdeclast_si128 (__m128i a, __m128i RoundKey)
+ /// </summary>
+ public static Vector128<sbyte> DecryptLast(Vector128<sbyte> value, Vector128<sbyte> roundKey) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_aesdeclast_si128 (__m128i a, __m128i RoundKey)
+ /// </summary>
+ public static Vector128<byte> DecryptLast(Vector128<byte> value, Vector128<byte> roundKey) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_aesenc_si128 (__m128i a, __m128i RoundKey)
+ /// </summary>
+ public static Vector128<sbyte> Encrypt(Vector128<sbyte> value, Vector128<sbyte> roundKey) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_aesenc_si128 (__m128i a, __m128i RoundKey)
+ /// </summary>
+ public static Vector128<byte> Encrypt(Vector128<byte> value, Vector128<byte> roundKey) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_aesenclast_si128 (__m128i a, __m128i RoundKey)
+ /// </summary>
+ public static Vector128<sbyte> EncryptLast(Vector128<sbyte> value, Vector128<sbyte> roundKey) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_aesenclast_si128 (__m128i a, __m128i RoundKey)
+ /// </summary>
+ public static Vector128<byte> EncryptLast(Vector128<byte> value, Vector128<byte> roundKey) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_aesimc_si128 (__m128i a)
+ /// </summary>
+ public static Vector128<sbyte> InvisibleMixColumn(Vector128<sbyte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_aesimc_si128 (__m128i a)
+ /// </summary>
+ public static Vector128<byte> InvisibleMixColumn(Vector128<byte> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_aeskeygenassist_si128 (__m128i a, const int imm8)
+ /// </summary>
+ public static Vector128<sbyte> KeygenAssist(Vector128<sbyte> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_aeskeygenassist_si128 (__m128i a, const int imm8)
+ /// </summary>
+ public static Vector128<byte> KeygenAssist(Vector128<byte> value, byte control) { throw new NotImplementedException(); }
+
+ }
+
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Avx.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Avx.cs
new file mode 100644
index 0000000000..ad0bfc9e1b
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Avx.cs
@@ -0,0 +1,996 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel AVX hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Avx
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// __m256 _mm256_add_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> Add(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_add_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> Add(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_addsub_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> AddSubtract(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_addsub_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> AddSubtract(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_and_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> And(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_and_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> And(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_andnot_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> AndNot(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_andnot_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> AndNot(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_blend_ps (__m256 a, __m256 b, const int imm8)
+ /// </summary>
+ public static Vector256<float> Blend(Vector256<float> left, Vector256<float> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_blend_pd (__m256d a, __m256d b, const int imm8)
+ /// </summary>
+ public static Vector256<double> Blend(Vector256<double> left, Vector256<double> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_blendv_ps (__m256 a, __m256 b, __m256 mask)
+ /// </summary>
+ public static Vector256<float> BlendVariable(Vector256<float> left, Vector256<float> right, Vector256<float> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_blendv_pd (__m256d a, __m256d b, __m256d mask)
+ /// </summary>
+ public static Vector256<double> BlendVariable(Vector256<double> left, Vector256<double> right, Vector256<double> mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_broadcast_ss (float const * mem_addr)
+ /// </summary>
+ public static Vector128<float> BroadcastElementToVector128(ref float source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_broadcast_ss (float const * mem_addr)
+ /// </summary>
+ public static Vector256<float> BroadcastElementToVector256(ref float source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_broadcast_sd (double const * mem_addr)
+ /// </summary>
+ public static Vector256<double> BroadcastElementToVector256(ref double source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_broadcast_ps (__m128 const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<float> BroadcastVector128ToVector256(float* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_broadcast_pd (__m128d const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<double> BroadcastVector128ToVector256(double* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_ceil_ps (__m256 a)
+ /// </summary>
+ public static Vector256<float> Ceiling(Vector256<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_ceil_pd (__m256d a)
+ /// </summary>
+ public static Vector256<double> Ceiling(Vector256<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmp_ps (__m128 a, __m128 b, const int imm8)
+ /// </summary>
+ public static Vector128<float> Compare(Vector128<float> left, Vector128<float> right, FloatComparisonMode mode) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_cmp_pd (__m128d a, __m128d b, const int imm8)
+ /// </summary>
+ public static Vector128<double> Compare(Vector128<double> left, Vector128<double> right, FloatComparisonMode mode) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_cmp_ps (__m256 a, __m256 b, const int imm8)
+ /// </summary>
+ public static Vector256<float> Compare(Vector256<float> left, Vector256<float> right, FloatComparisonMode mode) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_cmp_pd (__m256d a, __m256d b, const int imm8)
+ /// </summary>
+ public static Vector256<double> Compare(Vector256<double> left, Vector256<double> right, FloatComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm256_cvtpd_epi32 (__m256d a)
+ /// </summary>
+ public static Vector128<int> ConvertToVector128Int(Vector256<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128 _mm256_cvtpd_ps (__m256d a)
+ /// </summary>
+ public static Vector128<float> ConvertToVector128Float(Vector256<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtps_epi32 (__m256 a)
+ /// </summary>
+ public static Vector256<int> ConvertToVector256Int(Vector256<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_cvtepi32_ps (__m256i a)
+ /// </summary>
+ public static Vector256<float> ConvertToVector256Float(Vector256<int> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_cvtps_pd (__m128 a)
+ /// </summary>
+ public static Vector256<double> ConvertToVector256Double(Vector256<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_cvtepi32_pd (__m128i a)
+ /// </summary>
+ public static Vector256<double> ConvertToVector256Double(Vector256<int> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm256_cvttpd_epi32 (__m256d a)
+ /// </summary>
+ public static Vector128<int> ConvertToVector128IntWithTruncation(Vector256<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvttps_epi32 (__m256 a)
+ /// </summary>
+ public static Vector256<int> ConvertToVector256IntWithTruncation(Vector256<float> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_div_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> Divide(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_div_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> Divide(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_dp_ps (__m256 a, __m256 b, const int imm8)
+ /// </summary>
+ public static Vector256<float> DotProduct(Vector256<float> left, Vector256<float> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_moveldup_ps (__m256 a)
+ /// </summary>
+ public static Vector256<float> DuplicateEvenIndexed(Vector256<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_movedup_pd (__m256d a)
+ /// </summary>
+ public static Vector256<double> DuplicateEvenIndexed(Vector256<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_movehdup_ps (__m256 a)
+ /// </summary>
+ public static Vector256<float> DuplicateOddIndexed(Vector256<float> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __int8 _mm256_extract_epi8 (__m256i a, const int index)
+ /// </summary>
+ public static sbyte ExtractSbyte<T>(Vector256<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __int8 _mm256_extract_epi8 (__m256i a, const int index)
+ /// </summary>
+ public static byte ExtractByte<T>(Vector256<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __int16 _mm256_extract_epi16 (__m256i a, const int index)
+ /// </summary>
+ public static short ExtractShort<T>(Vector256<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __int16 _mm256_extract_epi16 (__m256i a, const int index)
+ /// </summary>
+ public static ushort ExtractUshort<T>(Vector256<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __int32 _mm256_extract_epi32 (__m256i a, const int index)
+ /// </summary>
+ public static int ExtractInt<T>(Vector256<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __int32 _mm256_extract_epi32 (__m256i a, const int index)
+ /// </summary>
+ public static uint ExtractUint<T>(Vector256<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __int64 _mm256_extract_epi64 (__m256i a, const int index)
+ /// </summary>
+ public static long ExtractLong<T>(Vector256<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __int64 _mm256_extract_epi64 (__m256i a, const int index)
+ /// </summary>
+ public static ulong ExtractUlong<T>(Vector256<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm256_extractf128_ps (__m256 a, const int imm8)
+ /// __m128d _mm256_extractf128_pd (__m256d a, const int imm8)
+ /// __m128i _mm256_extractf128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector128<T> ExtractVector128<T>(Vector256<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm256_extractf128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(byte* address, Vector256<byte> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extractf128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(sbyte* address, Vector256<sbyte> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extractf128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(short* address, Vector256<short> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extractf128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(ushort* address, Vector256<ushort> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extractf128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(int* address, Vector256<int> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extractf128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(uint* address, Vector256<uint> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extractf128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(long* address, Vector256<long> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extractf128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(ulong* address, Vector256<ulong> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128 _mm256_extractf128_ps (__m256 a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(float* address, Vector256<float> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm256_extractf128_pd (__m256d a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(double* address, Vector256<double> value, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256d _mm256_castpd128_pd256 (__m128d a)
+ /// __m256 _mm256_castps128_ps256 (__m128 a)
+ /// __m256i _mm256_castsi128_si256 (__m128i a)
+ /// </summary>
+ public static Vector256<T> ExtendToVector256<T>(Vector128<T> value) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_floor_ps (__m256 a)
+ /// </summary>
+ public static Vector256<float> Floor(Vector256<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_floor_pd (__m256d a)
+ /// </summary>
+ public static Vector256<double> Floor(Vector256<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm256_castpd256_pd128 (__m256d a)
+ /// __m128 _mm256_castps256_ps128 (__m256 a)
+ /// __m128i _mm256_castsi256_si128 (__m256i a)
+ /// </summary>
+ public static Vector128<T> GetLowerHalf<T>(Vector256<T> value) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_hadd_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> HorizontalAdd(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_hadd_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> HorizontalAdd(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_hsub_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> HorizontalSubtract(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_hsub_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> HorizontalSubtract(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_insert_epi8 (__m256i a, __int8 i, const int index)
+ /// </summary>
+ public static Vector256<T> InsertSbyte<T>(Vector256<T> value, sbyte data, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insert_epi8 (__m256i a, __int8 i, const int index)
+ /// </summary>
+ public static Vector256<T> InsertByte<T>(Vector256<T> value, byte data, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insert_epi16 (__m256i a, __int16 i, const int index)
+ /// </summary>
+ public static Vector256<T> InsertShort<T>(Vector256<T> value, short data, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insert_epi16 (__m256i a, __int16 i, const int index)
+ /// </summary>
+ public static Vector256<T> InsertUshort<T>(Vector256<T> value, ushort data, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insert_epi32 (__m256i a, __int32 i, const int index)
+ /// </summary>
+ public static Vector256<T> InsertInt<T>(Vector256<T> value, int data, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insert_epi32 (__m256i a, __int32 i, const int index)
+ /// </summary>
+ public static Vector256<T> InsertUint<T>(Vector256<T> value, uint data, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insert_epi64 (__m256i a, __int64 i, const int index)
+ /// </summary>
+ public static Vector256<T> InsertLong<T>(Vector256<T> value, long data, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insert_epi64 (__m256i a, __int64 i, const int index)
+ /// </summary>
+ public static Vector256<T> InsertUlong<T>(Vector256<T> value, ulong data, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_insertf128_ps (__m256 a, __m128 b, int imm8)
+ /// __m256d _mm256_insertf128_pd (__m256d a, __m128d b, int imm8)
+ /// __m256i _mm256_insertf128_si256 (__m256i a, __m128i b, int imm8)
+ /// </summary>
+ public static Vector256<T> Insert<T>(Vector256<T> value, Vector128<T> data, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_insertf128_si256 (__m256i a, __m128i b, int imm8)
+ /// </summary>
+ public static unsafe Vector256<sbyte> Insert(Vector256<sbyte> value, sbyte* address, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insertf128_si256 (__m256i a, __m128i b, int imm8)
+ /// </summary>
+ public static unsafe Vector256<byte> Insert(Vector256<byte> value, byte* address, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insertf128_si256 (__m256i a, __m128i b, int imm8)
+ /// </summary>
+ public static unsafe Vector256<short> Insert(Vector256<short> value, short* address, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insertf128_si256 (__m256i a, __m128i b, int imm8)
+ /// </summary>
+ public static unsafe Vector256<ushort> Insert(Vector256<ushort> value, ushort* address, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insertf128_si256 (__m256i a, __m128i b, int imm8)
+ /// </summary>
+ public static unsafe Vector256<int> Insert(Vector256<int> value, int* address, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insertf128_si256 (__m256i a, __m128i b, int imm8)
+ /// </summary>
+ public static unsafe Vector256<uint> Insert(Vector256<uint> value, uint* address, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insertf128_si256 (__m256i a, __m128i b, int imm8)
+ /// </summary>
+ public static unsafe Vector256<long> Insert(Vector256<long> value, long* address, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_insertf128_si256 (__m256i a, __m128i b, int imm8)
+ /// </summary>
+ public static unsafe Vector256<ulong> Insert(Vector256<ulong> value, ulong* address, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_insertf128_ps (__m256 a, __m128 b, int imm8)
+ /// </summary>
+ public static unsafe Vector256<float> Insert(Vector256<float> value, float* address, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_insertf128_pd (__m256d a, __m128d b, int imm8)
+ /// </summary>
+ public static unsafe Vector256<double> Insert(Vector256<double> value, double* address, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_loadu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<sbyte> Load(sbyte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_loadu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<byte> Load(byte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_loadu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<short> Load(short* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_loadu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<ushort> Load(ushort* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_loadu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<int> Load(int* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_loadu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<uint> Load(uint* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_loadu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<long> Load(long* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_loadu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<ulong> Load(ulong* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_loadu_ps (float const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<float> Load(float* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_loadu_pd (double const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<double> Load(double* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_load_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<sbyte> LoadAligned(sbyte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_load_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<byte> LoadAligned(byte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_load_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<short> LoadAligned(short* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_load_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<ushort> LoadAligned(ushort* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_load_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<int> LoadAligned(int* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_load_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<uint> LoadAligned(uint* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_load_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<long> LoadAligned(long* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_load_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<ulong> LoadAligned(ulong* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_load_ps (float const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<float> LoadAligned(float* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_load_pd (double const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<double> LoadAligned(double* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_lddqu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<sbyte> LoadDqu(sbyte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_lddqu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<byte> LoadDqu(byte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_lddqu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<short> LoadDqu(short* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_lddqu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<ushort> LoadDqu(ushort* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_lddqu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<int> LoadDqu(int* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_lddqu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<uint> LoadDqu(uint* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_lddqu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<long> LoadDqu(long* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_lddqu_si256 (__m256i const * mem_addr)
+ /// </summary>
+ public static unsafe Vector256<ulong> LoadDqu(ulong* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_maskload_pd (double const * mem_addr, __m128i mask)
+ /// </summary>
+ public static unsafe Vector128<float> MaskLoad(float* address, Vector128<uint> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_maskload_pd (double const * mem_addr, __m128i mask)
+ /// </summary>
+ public static unsafe Vector128<double> MaskLoad(double* address, Vector128<ulong> mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_maskload_ps (float const * mem_addr, __m256i mask)
+ /// </summary>
+ public static unsafe Vector256<float> MaskLoad(float* address, Vector256<uint> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_maskload_pd (double const * mem_addr, __m256i mask)
+ /// </summary>
+ public static unsafe Vector256<double> MaskLoad(double* address, Vector256<ulong> mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm_maskstore_ps (float * mem_addr, __m128i mask, __m128 a)
+ /// </summary>
+ public static unsafe void MaskStore(float* address, Vector128<float> mask, Vector128<uint> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_maskstore_pd (double * mem_addr, __m128i mask, __m128d a)
+ /// </summary>
+ public static unsafe void MaskStore(double* address, Vector128<double> mask, Vector128<ulong> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm256_maskstore_ps (float * mem_addr, __m256i mask, __m256 a)
+ /// </summary>
+ public static unsafe void MaskStore(float* address, Vector256<float> mask, Vector256<uint> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_maskstore_pd (double * mem_addr, __m256i mask, __m256d a)
+ /// </summary>
+ public static unsafe void MaskStore(double* address, Vector256<double> mask, Vector256<ulong> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_max_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> Max(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_max_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> Max(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_min_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> Min(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_min_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> Min(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm256_movemask_ps (__m256 a)
+ /// </summary>
+ public static int MoveMask(Vector256<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// int _mm256_movemask_pd (__m256d a)
+ /// </summary>
+ public static int MoveMask(Vector256<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_mul_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> Multiply(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_mul_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> Multiply(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_or_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> Or(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_or_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> Or(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_permute_ps (__m128 a, int imm8)
+ /// </summary>
+ public static Vector128<float> Permute(Vector128<float> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_permute_pd (__m128d a, int imm8)
+ /// </summary>
+ public static Vector128<double> Permute(Vector128<double> value, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_permute_ps (__m256 a, int imm8)
+ /// </summary>
+ public static Vector256<float> Permute(Vector256<float> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_permute_pd (__m256d a, int imm8)
+ /// </summary>
+ public static Vector256<double> Permute(Vector256<double> value, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_permute2f128_ps (__m256 a, __m256 b, int imm8)
+ /// </summary>
+ public static Vector256<float> Permute2x128(Vector256<float> left, Vector256<float> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_permute2f128_pd (__m256d a, __m256d b, int imm8)
+ /// </summary>
+ public static Vector256<double> Permute2x128(Vector256<double> left, Vector256<double> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_permutevar_ps (__m128 a, __m128i b)
+ /// </summary>
+ public static Vector128<float> PermuteVar(Vector128<float> left, Vector128<float> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_permutevar_pd (__m128d a, __m128i b)
+ /// </summary>
+ public static Vector128<double> PermuteVar(Vector128<double> left, Vector128<double> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_permutevar_ps (__m256 a, __m256i b)
+ /// </summary>
+ public static Vector256<float> PermuteVar(Vector256<float> left, Vector256<float> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_permutevar_pd (__m256d a, __m256i b)
+ /// </summary>
+ public static Vector256<double> PermuteVar(Vector256<double> left, Vector256<double> mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_rcp_ps (__m256 a)
+ /// </summary>
+ public static Vector256<float> Reciprocal(Vector256<float> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_rsqrt_ps (__m256 a)
+ /// </summary>
+ public static Vector256<float> ReciprocalSqrt(Vector256<float> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_round_ps (__m256 a, int rounding)
+ /// _MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector256<float> RoundToNearestInteger(Vector256<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_NEG_INF |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector256<float> RoundToNegativeInfinity(Vector256<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_POS_INF |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector256<float> RoundToPositiveInfinity(Vector256<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_ZERO |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector256<float> RoundToZero(Vector256<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_CUR_DIRECTION
+ /// </summary>
+ public static Vector256<float> RoundCurrentDirection(Vector256<float> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256d _mm256_round_pd (__m256d a, int rounding)
+ /// _MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector256<double> RoundToNearestInteger(Vector256<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_NEG_INF |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector256<double> RoundToNegativeInfinity(Vector256<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_POS_INF |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector256<double> RoundToPositiveInfinity(Vector256<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_ZERO |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector256<double> RoundToZero(Vector256<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_CUR_DIRECTION
+ /// </summary>
+ public static Vector256<double> RoundCurrentDirection(Vector256<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_set_epi8 (char e31, char e30, char e29, char e28, char e27, char e26, char e25, char e24, char e23, char e22, char e21, char e20, char e19, char e18, char e17, char e16, char e15, char e14, char e13, char e12, char e11, char e10, char e9, char e8, char e7, char e6, char e5, char e4, char e3, char e2, char e1, char e0)
+ /// </summary>
+ public static Vector256<sbyte> Set(sbyte e31, sbyte e30, sbyte e29, sbyte e28, sbyte e27, sbyte e26, sbyte e25, sbyte e24, sbyte e23, sbyte e22, sbyte e21, sbyte e20, sbyte e19, sbyte e18, sbyte e17, sbyte e16, sbyte e15, sbyte e14, sbyte e13, sbyte e12, sbyte e11, sbyte e10, sbyte e9, sbyte e8, sbyte e7, sbyte e6, sbyte e5, sbyte e4, sbyte e3, sbyte e2, sbyte e1, sbyte e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_set_epi8 (char e31, char e30, char e29, char e28, char e27, char e26, char e25, char e24, char e23, char e22, char e21, char e20, char e19, char e18, char e17, char e16, char e15, char e14, char e13, char e12, char e11, char e10, char e9, char e8, char e7, char e6, char e5, char e4, char e3, char e2, char e1, char e0)
+ /// </summary>
+ public static Vector256<byte> Set(byte e31, byte e30, byte e29, byte e28, byte e27, byte e26, byte e25, byte e24, byte e23, byte e22, byte e21, byte e20, byte e19, byte e18, byte e17, byte e16, byte e15, byte e14, byte e13, byte e12, byte e11, byte e10, byte e9, byte e8, byte e7, byte e6, byte e5, byte e4, byte e3, byte e2, byte e1, byte e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_set_epi16 (short e15, short e14, short e13, short e12, short e11, short e10, short e9, short e8, short e7, short e6, short e5, short e4, short e3, short e2, short e1, short e0)
+ /// </summary>
+ public static Vector256<short> Set(short e15, short e14, short e13, short e12, short e11, short e10, short e9, short e8, short e7, short e6, short e5, short e4, short e3, short e2, short e1, short e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_set_epi16 (short e15, short e14, short e13, short e12, short e11, short e10, short e9, short e8, short e7, short e6, short e5, short e4, short e3, short e2, short e1, short e0)
+ /// </summary>
+ public static Vector256<ushort> Set(ushort e15, ushort e14, ushort e13, ushort e12, ushort e11, ushort e10, ushort e9, ushort e8, ushort e7, ushort e6, ushort e5, ushort e4, ushort e3, ushort e2, ushort e1, ushort e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_set_epi32 (int e7, int e6, int e5, int e4, int e3, int e2, int e1, int e0)
+ /// </summary>
+ public static Vector256<int> Set(int e7, int e6, int e5, int e4, int e3, int e2, int e1, int e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_set_epi32 (int e7, int e6, int e5, int e4, int e3, int e2, int e1, int e0)
+ /// </summary>
+ public static Vector256<uint> Set(uint e7, uint e6, uint e5, uint e4, uint e3, uint e2, uint e1, uint e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_set_epi64x (__int64 e3, __int64 e2, __int64 e1, __int64 e0)
+ /// </summary>
+ public static Vector256<long> Set(long e3, long e2, long e1, long e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_set_epi64x (__int64 e3, __int64 e2, __int64 e1, __int64 e0)
+ /// </summary>
+ public static Vector256<ulong> Set(ulong e3, ulong e2, ulong e1, ulong e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_set_ps (float e7, float e6, float e5, float e4, float e3, float e2, float e1, float e0)
+ /// </summary>
+ public static Vector256<float> Set(float e7, float e6, float e5, float e4, float e3, float e2, float e1, float e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_set_pd (double e3, double e2, double e1, double e0)
+ /// </summary>
+ public static Vector256<double> Set(double e3, double e2, double e1, double e0) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_set1_epi8 (char a)
+ /// __m256i _mm256_set1_epi16 (short a)
+ /// __m256i _mm256_set1_epi32 (int a)
+ /// __m256i _mm256_set1_epi64x (long long a)
+ /// __m256 _mm256_set1_ps (float a)
+ /// __m256d _mm256_set1_pd (double a)
+ /// </summary>
+ public static Vector256<T> Set1<T>(T value) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_set_m128 (__m128 hi, __m128 lo)
+ /// __m256d _mm256_set_m128d (__m128d hi, __m128d lo)
+ /// __m256i _mm256_set_m128i (__m128i hi, __m128i lo)
+ /// </summary>
+ public static Vector256<T> SetHiLo<T>(Vector128<T> hi, Vector128<T> lo) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_setzero_si256 (void)
+ /// __m256 _mm256_setzero_ps (void)
+ /// __m256d _mm256_setzero_pd (void)
+ /// </summary>
+ public static Vector256<T> SetZero<T>() where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_shuffle_ps (__m256 a, __m256 b, const int imm8)
+ /// </summary>
+ public static Vector256<float> Shuffle(Vector256<float> value, Vector256<float> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_shuffle_pd (__m256d a, __m256d b, const int imm8)
+ /// </summary>
+ public static Vector256<double> Shuffle(Vector256<double> value, Vector256<double> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_sqrt_ps (__m256 a)
+ /// </summary>
+ public static Vector256<float> Sqrt(Vector256<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_sqrt_pd (__m256d a)
+ /// </summary>
+ public static Vector256<double> Sqrt(Vector256<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_castpd_ps (__m256d a)
+ /// __m256i _mm256_castpd_si256 (__m256d a)
+ /// __m256d _mm256_castps_pd (__m256 a)
+ /// __m256i _mm256_castps_si256 (__m256 a)
+ /// __m256d _mm256_castsi256_pd (__m256i a)
+ /// __m256 _mm256_castsi256_ps (__m256i a)
+ /// </summary>
+ public static Vector256<U> StaticCast<T, U>(Vector256<T> value) where T : struct where U : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm256_store_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAligned(sbyte* address, Vector256<sbyte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_store_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAligned(byte* address, Vector256<byte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_store_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAligned(short* address, Vector256<short> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_store_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAligned(ushort* address, Vector256<ushort> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_store_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAligned(int* address, Vector256<int> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_store_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAligned(uint* address, Vector256<uint> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_store_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAligned(long* address, Vector256<long> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_store_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAligned(ulong* address, Vector256<ulong> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_store_ps (float * mem_addr, __m256 a)
+ /// </summary>
+ public static unsafe void StoreAligned(float* address, Vector256<float> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_store_pd (double * mem_addr, __m256d a)
+ /// </summary>
+ public static unsafe void StoreAligned(double* address, Vector256<double> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm256_stream_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(sbyte* address, Vector256<sbyte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_stream_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(byte* address, Vector256<byte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_stream_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(short* address, Vector256<short> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_stream_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(ushort* address, Vector256<ushort> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_stream_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(int* address, Vector256<int> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_stream_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(uint* address, Vector256<uint> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_stream_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(long* address, Vector256<long> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_stream_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(ulong* address, Vector256<ulong> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_stream_ps (float * mem_addr, __m256 a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(float* address, Vector256<float> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_stream_pd (double * mem_addr, __m256d a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(double* address, Vector256<double> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm256_storeu_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void Store(sbyte* address, Vector256<sbyte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_storeu_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void Store(byte* address, Vector256<byte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_storeu_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void Store(short* address, Vector256<short> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_storeu_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void Store(ushort* address, Vector256<ushort> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_storeu_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void Store(int* address, Vector256<int> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_storeu_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void Store(uint* address, Vector256<uint> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_storeu_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void Store(long* address, Vector256<long> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_storeu_si256 (__m256i * mem_addr, __m256i a)
+ /// </summary>
+ public static unsafe void Store(ulong* address, Vector256<ulong> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_storeu_ps (float * mem_addr, __m256 a)
+ /// </summary>
+ public static unsafe void Store(float* address, Vector256<float> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_storeu_pd (double * mem_addr, __m256d a)
+ /// </summary>
+ public static unsafe void Store(double* address, Vector256<double> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_sub_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> Subtract(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_sub_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> Subtract(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_testc_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static bool TestC(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// int _mm_testc_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static bool TestC(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm256_testc_si256 (__m256i a, __m256i b)
+ /// int _mm256_testc_ps (__m256 a, __m256 b)
+ /// int _mm256_testc_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static bool TestC<T>(Vector256<T> left, Vector256<T> right) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_testnzc_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static bool TestNotZAndNotC(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// int _mm_testnzc_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static bool TestNotZAndNotC(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm256_testnzc_si256 (__m256i a, __m256i b)
+ /// int _mm256_testnzc_ps (__m256 a, __m256 b)
+ /// int _mm256_testnzc_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static bool TestNotZAndNotC<T>(Vector256<T> left, Vector256<T> right) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_testz_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static bool TestZ(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// int _mm_testz_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static bool TestZ(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm256_testz_si256 (__m256i a, __m256i b)
+ /// int _mm256_testz_ps (__m256 a, __m256 b)
+ /// int _mm256_testz_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static bool TestZ<T>(Vector256<T> left, Vector256<T> right) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_unpackhi_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> UnpackHigh(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_unpackhi_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> UnpackHigh(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_unpacklo_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> UnpackLow(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_unpacklo_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> UnpackLow(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256 _mm256_xor_ps (__m256 a, __m256 b)
+ /// </summary>
+ public static Vector256<float> Xor(Vector256<float> left, Vector256<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_xor_pd (__m256d a, __m256d b)
+ /// </summary>
+ public static Vector256<double> Xor(Vector256<double> left, Vector256<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm256_zeroall (void)
+ /// </summary>
+ public static void ZeroAll() { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_zeroupper (void)
+ /// </summary>
+ public static void ZeroUpper() { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256d _mm256_zextpd128_pd256 (__m128d a)
+ /// __m256 _mm256_zextps128_ps256 (__m128 a)
+ /// __m256i _mm256_zextsi128_si256 (__m128i a)
+ /// </summary>
+ public static Vector256<T> ZeroExtendToVector256<T>(Vector128<T> value) where T : struct { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Avx2.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Avx2.cs
new file mode 100644
index 0000000000..cd652c26e2
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Avx2.cs
@@ -0,0 +1,1362 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel AVX2 hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Avx2
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// __m256i _mm256_abs_epi8 (__m256i a)
+ /// </summary>
+ public static Vector256<byte> Abs(Vector256<sbyte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_abs_epi16 (__m256i a)
+ /// </summary>
+ public static Vector256<ushort> Abs(Vector256<short> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_abs_epi32 (__m256i a)
+ /// </summary>
+ public static Vector256<uint> Abs(Vector256<int> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_add_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> Add(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_add_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> Add(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_add_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> Add(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_add_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> Add(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_add_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> Add(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_add_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<uint> Add(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_add_epi64 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<long> Add(Vector256<long> left, Vector256<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_add_epi64 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ulong> Add(Vector256<ulong> left, Vector256<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_adds_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> AddSaturate(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_adds_epu8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> AddSaturate(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_adds_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> AddSaturate(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_adds_epu16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> AddSaturate(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_alignr_epi8 (__m256i a, __m256i b, const int count)
+ /// </summary>
+ public static Vector256<sbyte> AlignRight(Vector256<sbyte> left, Vector256<sbyte> right, byte mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_and_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> And(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_and_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> And(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_and_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> And(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_and_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> And(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_and_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> And(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_and_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<uint> And(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_and_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<long> And(Vector256<long> left, Vector256<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_and_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ulong> And(Vector256<ulong> left, Vector256<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_andnot_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> AndNot(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_andnot_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> AndNot(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_andnot_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> AndNot(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_andnot_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> AndNot(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_andnot_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> AndNot(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_andnot_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<uint> AndNot(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_andnot_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<long> AndNot(Vector256<long> left, Vector256<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_andnot_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ulong> AndNot(Vector256<ulong> left, Vector256<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_avg_epu8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> Average(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_avg_epu16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> Average(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_blend_epi32 (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<int> Blend(Vector128<int> left, Vector128<int> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_blend_epi32 (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<uint> Blend(Vector128<uint> left, Vector128<uint> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_blend_epi16 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<short> Blend(Vector256<short> left, Vector256<short> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_blend_epi16 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<ushort> Blend(Vector256<ushort> left, Vector256<ushort> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_blend_epi32 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<int> Blend(Vector256<int> left, Vector256<int> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_blend_epi32 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<uint> Blend(Vector256<uint> left, Vector256<uint> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_blendv_epi8 (__m256i a, __m256i b, __m256i mask)
+ /// </summary>
+ public static Vector256<sbyte> BlendVariable(Vector256<sbyte> left, Vector256<sbyte> right, Vector256<sbyte> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_blendv_epi8 (__m256i a, __m256i b, __m256i mask)
+ /// </summary>
+ public static Vector256<byte> BlendVariable(Vector256<byte> left, Vector256<byte> right, Vector256<byte> mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_broadcastb_epi8 (__m128i a)
+ /// __m128i _mm_broadcastw_epi16 (__m128i a)
+ /// __m128i _mm_broadcastd_epi32 (__m128i a)
+ /// __m128i _mm_broadcastq_epi64 (__m128i a)
+ /// __m128 _mm_broadcastss_ps (__m128 a)
+ /// __m128d _mm_broadcastsd_pd (__m128d a)
+ /// </summary>
+ public static Vector128<T> BroadcastElementToVector128<T>(Vector128<T> value) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_broadcastb_epi8 (__m128i a)
+ /// __m256i _mm256_broadcastw_epi16 (__m128i a)
+ /// __m256i _mm256_broadcastd_epi32 (__m128i a)
+ /// __m256i _mm256_broadcastq_epi64 (__m128i a)
+ /// __m256 _mm256_broadcastss_ps (__m128 a)
+ /// __m256d _mm256_broadcastsd_pd (__m128d a)
+ /// </summary>
+ public static Vector256<T> BroadcastElementToVector256<T>(Vector128<T> value) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_broadcastsi128_si256 (__m128i a)
+ /// </summary>
+ public static unsafe Vector256<sbyte> BroadcastVector128ToVector256(sbyte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_broadcastsi128_si256 (__m128i a)
+ /// </summary>
+ public static unsafe Vector256<byte> BroadcastVector128ToVector256(byte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_broadcastsi128_si256 (__m128i a)
+ /// </summary>
+ public static unsafe Vector256<short> BroadcastVector128ToVector256(short* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_broadcastsi128_si256 (__m128i a)
+ /// </summary>
+ public static unsafe Vector256<ushort> BroadcastVector128ToVector256(ushort* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_broadcastsi128_si256 (__m128i a)
+ /// </summary>
+ public static unsafe Vector256<int> BroadcastVector128ToVector256(int* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_broadcastsi128_si256 (__m128i a)
+ /// </summary>
+ public static unsafe Vector256<uint> BroadcastVector128ToVector256(uint* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_broadcastsi128_si256 (__m128i a)
+ /// </summary>
+ public static unsafe Vector256<long> BroadcastVector128ToVector256(long* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_broadcastsi128_si256 (__m128i a)
+ /// </summary>
+ public static unsafe Vector256<ulong> BroadcastVector128ToVector256(ulong* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_cmpeq_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> CompareEqual(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cmpeq_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> CompareEqual(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cmpeq_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> CompareEqual(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cmpeq_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> CompareEqual(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cmpeq_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> CompareEqual(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cmpeq_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<uint> CompareEqual(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cmpeq_epi64 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<long> CompareEqual(Vector256<long> left, Vector256<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cmpeq_epi64 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ulong> CompareEqual(Vector256<ulong> left, Vector256<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_cmpgt_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> CompareGreaterThan(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cmpgt_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> CompareGreaterThan(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cmpgt_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> CompareGreaterThan(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cmpgt_epi64 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<long> CompareGreaterThan(Vector256<long> left, Vector256<long> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_cvtepi8_epi16 (__m128i a)
+ /// </summary>
+ public static Vector256<short> ConvertToVector256Short(Vector128<sbyte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtepu8_epi16 (__m128i a)
+ /// </summary>
+ public static Vector256<ushort> ConvertToVector256UShort(Vector128<byte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtepi8_epi32 (__m128i a)
+ /// </summary>
+ public static Vector256<int> ConvertToVector256Int(Vector128<sbyte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtepi16_epi32 (__m128i a)
+ /// </summary>
+ public static Vector256<int> ConvertToVector256Int(Vector128<short> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtepu8_epi32 (__m128i a)
+ /// </summary>
+ public static Vector256<uint> ConvertToVector256UInt(Vector128<byte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtepu16_epi32 (__m128i a)
+ /// </summary>
+ public static Vector256<uint> ConvertToVector256UInt(Vector128<ushort> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtepi8_epi64 (__m128i a)
+ /// </summary>
+ public static Vector256<long> ConvertToVector256Long(Vector128<sbyte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtepi16_epi64 (__m128i a)
+ /// </summary>
+ public static Vector256<long> ConvertToVector256Long(Vector128<short> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtepi32_epi64 (__m128i a)
+ /// </summary>
+ public static Vector256<long> ConvertToVector256Long(Vector128<int> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtepu8_epi64 (__m128i a)
+ /// </summary>
+ public static Vector256<ulong> ConvertToVector256ULong(Vector128<byte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtepu16_epi64 (__m128i a)
+ /// </summary>
+ public static Vector256<ulong> ConvertToVector256ULong(Vector128<ushort> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_cvtepu32_epi64 (__m128i a)
+ /// </summary>
+ public static Vector256<ulong> ConvertToVector256ULong(Vector128<uint> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector128<sbyte> ExtractVector128(Vector256<sbyte> value, byte index) { throw new NotImplementedException(); }
+ // <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(sbyte* address, Vector256<sbyte> value, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector128<byte> ExtractVector128(Vector256<byte> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(byte* address, Vector256<byte> value, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector128<short> ExtractVector128(Vector256<short> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(short* address, Vector256<short> value, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector128<ushort> ExtractVector128(Vector256<ushort> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(ushort* address, Vector256<ushort> value, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector128<int> ExtractVector128(Vector256<int> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(int* address, Vector256<int> value, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector128<uint> ExtractVector128(Vector256<uint> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(uint* address, Vector256<uint> value, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector128<long> ExtractVector128(Vector256<long> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(long* address, Vector256<long> value, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector128<ulong> ExtractVector128(Vector256<ulong> value, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_extracti128_si256 (__m256i a, const int imm8)
+ /// </summary>
+ public static unsafe void ExtractVector128(ulong* address, Vector256<ulong> value, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_i32gather_epi32 (int const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<int> GatherVector128(int* baseAddress, Vector128<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_i32gather_epi32 (int const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<uint> GatherVector128(uint* baseAddress, Vector128<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_i32gather_epi64 (__int64 const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<long> GatherVector128(long* baseAddress, Vector128<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_i32gather_epi64 (__int64 const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<ulong> GatherVector128(ulong* baseAddress, Vector128<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128 _mm_i32gather_ps (float const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<float> GatherVector128(float* baseAddress, Vector128<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_i32gather_pd (double const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<double> GatherVector128(double* baseAddress, Vector128<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_i64gather_epi32 (int const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<int> GatherVector128(int* baseAddress, Vector128<long> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_i64gather_epi32 (int const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<uint> GatherVector128(uint* baseAddress, Vector128<long> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_i64gather_epi64 (__int64 const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<long> GatherVector128(long* baseAddress, Vector128<long> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_i64gather_epi64 (__int64 const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<ulong> GatherVector128(ulong* baseAddress, Vector128<long> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128 _mm_i64gather_ps (float const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<float> GatherVector128(float* baseAddress, Vector128<long> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_i64gather_pd (double const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<double> GatherVector128(double* baseAddress, Vector128<long> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_i32gather_epi32 (int const* base_addr, __m256i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector256<int> GatherVector256(int* baseAddress, Vector256<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_i32gather_epi32 (int const* base_addr, __m256i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector256<uint> GatherVector256(uint* baseAddress, Vector256<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_i32gather_epi64 (__int64 const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector256<long> GatherVector256(long* baseAddress, Vector128<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_i32gather_epi64 (__int64 const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector256<ulong> GatherVector256(ulong* baseAddress, Vector128<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_i32gather_ps (float const* base_addr, __m256i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector256<float> GatherVector256(float* baseAddress, Vector256<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_i32gather_pd (double const* base_addr, __m128i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector256<double> GatherVector256(double* baseAddress, Vector128<int> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_i64gather_epi32 (int const* base_addr, __m256i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<int> GatherVector128(int* baseAddress, Vector256<long> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_i64gather_epi32 (int const* base_addr, __m256i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<uint> GatherVector128(uint* baseAddress, Vector256<long> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_i64gather_epi64 (__int64 const* base_addr, __m256i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector256<long> GatherVector256(long* baseAddress, Vector256<long> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_i64gather_epi64 (__int64 const* base_addr, __m256i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector256<ulong> GatherVector256(ulong* baseAddress, Vector256<long> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128 _mm256_i64gather_ps (float const* base_addr, __m256i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector128<float> GatherVector128(float* baseAddress, Vector256<long> index, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_i64gather_pd (double const* base_addr, __m256i vindex, const int scale)
+ /// </summary>
+ public static unsafe Vector256<double> GatherVector256(double* baseAddress, Vector256<long> index, byte scale) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_mask_i32gather_epi32 (__m128i src, int const* base_addr, __m128i vindex, __m128i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<int> GatherMaskVector128(Vector128<int> source, int* baseAddress, Vector128<int> index, Vector128<int> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_mask_i32gather_epi32 (__m128i src, int const* base_addr, __m128i vindex, __m128i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<uint> GatherMaskVector128(Vector128<uint> source, uint* baseAddress, Vector128<int> index, Vector128<uint> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_mask_i32gather_epi64 (__m128i src, __int64 const* base_addr, __m128i vindex, __m128i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<long> GatherMaskVector128(Vector128<long> source, long* baseAddress, Vector128<int> index, Vector128<long> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_mask_i32gather_epi64 (__m128i src, __int64 const* base_addr, __m128i vindex, __m128i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<ulong> GatherMaskVector128(Vector128<ulong> source, ulong* baseAddress, Vector128<int> index, Vector128<ulong> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128 _mm_mask_i32gather_ps (__m128 src, float const* base_addr, __m128i vindex, __m128 mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<float> GatherMaskVector128(Vector128<float> source, float* baseAddress, Vector128<int> index, Vector128<float> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_mask_i32gather_pd (__m128d src, double const* base_addr, __m128i vindex, __m128d mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<double> GatherMaskVector128(Vector128<double> source, double* baseAddress, Vector128<int> index, Vector128<double> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_mask_i64gather_epi32 (__m128i src, int const* base_addr, __m128i vindex, __m128i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<int> GatherMaskVector128(Vector128<int> source, int* baseAddress, Vector128<long> index, Vector128<int> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_mask_i64gather_epi32 (__m128i src, int const* base_addr, __m128i vindex, __m128i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<uint> GatherMaskVector128(Vector128<uint> source, uint* baseAddress, Vector128<long> index, Vector128<uint> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_mask_i64gather_epi64 (__m128i src, __int64 const* base_addr, __m128i vindex, __m128i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<long> GatherMaskVector128(Vector128<long> source, long* baseAddress, Vector128<long> index, Vector128<long> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_mask_i64gather_epi64 (__m128i src, __int64 const* base_addr, __m128i vindex, __m128i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<ulong> GatherMaskVector128(Vector128<ulong> source, ulong* baseAddress, Vector128<long> index, Vector128<ulong> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128 _mm_mask_i64gather_ps (__m128 src, float const* base_addr, __m128i vindex, __m128 mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<float> GatherMaskVector128(Vector128<float> source, float* baseAddress, Vector128<long> index, Vector128<float> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_mask_i64gather_pd (__m128d src, double const* base_addr, __m128i vindex, __m128d mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<double> GatherMaskVector128(Vector128<double> source, double* baseAddress, Vector128<long> index, Vector128<double> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_mask_i32gather_epi32 (__m256i src, int const* base_addr, __m256i vindex, __m256i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector256<int> GatherMaskVector256(Vector256<int> source, int* baseAddress, Vector256<int> index, Vector256<int> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_mask_i32gather_epi32 (__m256i src, int const* base_addr, __m256i vindex, __m256i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector256<uint> GatherMaskVector256(Vector256<uint> source, uint* baseAddress, Vector256<int> index, Vector256<uint> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_mask_i32gather_epi64 (__m256i src, __int64 const* base_addr, __m128i vindex, __m256i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector256<long> GatherMaskVector256(Vector256<long> source, long* baseAddress, Vector128<int> index, Vector256<long> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_mask_i32gather_epi64 (__m256i src, __int64 const* base_addr, __m128i vindex, __m256i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector256<ulong> GatherMaskVector256(Vector256<ulong> source, ulong* baseAddress, Vector128<int> index, Vector256<ulong> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_mask_i32gather_ps (__m256 src, float const* base_addr, __m256i vindex, __m256 mask, const int scale)
+ /// </summary>
+ public static unsafe Vector256<float> GatherMaskVector256(Vector256<float> source, float* baseAddress, Vector256<int> index, Vector256<float> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_mask_i32gather_pd (__m256d src, double const* base_addr, __m128i vindex, __m256d mask, const int scale)
+ /// </summary>
+ public static unsafe Vector256<double> GatherMaskVector256(Vector256<double> source, double* baseAddress, Vector128<int> index, Vector256<double> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_mask_i64gather_epi32 (__m128i src, int const* base_addr, __m256i vindex, __m128i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<int> GatherMaskVector128(Vector128<int> source, int* baseAddress, Vector256<long> index, Vector128<int> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm256_mask_i64gather_epi32 (__m128i src, int const* base_addr, __m256i vindex, __m128i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<uint> GatherMaskVector128(Vector128<uint> source, uint* baseAddress, Vector256<long> index, Vector128<uint> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_mask_i64gather_epi64 (__m256i src, __int64 const* base_addr, __m256i vindex, __m256i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector256<long> GatherMaskVector256(Vector256<long> source, long* baseAddress, Vector256<long> index, Vector256<long> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_mask_i64gather_epi64 (__m256i src, __int64 const* base_addr, __m256i vindex, __m256i mask, const int scale)
+ /// </summary>
+ public static unsafe Vector256<ulong> GatherMaskVector256(Vector256<ulong> source, ulong* baseAddress, Vector256<long> index, Vector256<ulong> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128 _mm256_mask_i64gather_ps (__m128 src, float const* base_addr, __m256i vindex, __m128 mask, const int scale)
+ /// </summary>
+ public static unsafe Vector128<float> GatherMaskVector128(Vector128<float> source, float* baseAddress, Vector256<long> index, Vector128<float> mask, byte scale) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_mask_i64gather_pd (__m256d src, double const* base_addr, __m256i vindex, __m256d mask, const int scale)
+ /// </summary>
+ public static unsafe Vector256<double> GatherMaskVector256(Vector256<double> source, double* baseAddress, Vector256<long> index, Vector256<double> mask, byte scale) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_hadd_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> HorizontalAdd(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_hadd_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> HorizontalAdd(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_hadds_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> HorizontalAddSaturate(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_hsub_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> HorizontalSubtract(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_hsub_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> HorizontalSubtract(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_hsubs_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> HorizontalSubtractSaturate(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector256<sbyte> Insert(Vector256<sbyte> value, Vector128<sbyte> data, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static unsafe Vector256<sbyte> Insert(Vector256<sbyte> value, sbyte* address, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector256<byte> Insert(Vector256<byte> value, Vector128<byte> data, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static unsafe Vector256<byte> Insert(Vector256<byte> value, byte* address, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector256<short> Insert(Vector256<short> value, Vector128<short> data, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static unsafe Vector256<short> Insert(Vector256<short> value, short* address, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector256<ushort> Insert(Vector256<ushort> value, Vector128<ushort> data, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static unsafe Vector256<ushort> Insert(Vector256<ushort> value, ushort* address, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector256<int> Insert(Vector256<int> value, Vector128<int> data, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static unsafe Vector256<int> Insert(Vector256<int> value, int* address, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector256<uint> Insert(Vector256<uint> value, Vector128<uint> data, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static unsafe Vector256<uint> Insert(Vector256<uint> value, uint* address, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector256<long> Insert(Vector256<long> value, Vector128<long> data, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static unsafe Vector256<long> Insert(Vector256<long> value, long* address, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector256<ulong> Insert(Vector256<ulong> value, Vector128<ulong> data, byte index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_inserti128_si256 (__m256i a, __m128i b, const int imm8)
+ /// </summary>
+ public static unsafe Vector256<ulong> Insert(Vector256<ulong> value, ulong* address, byte index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_maskload_epi32 (int const* mem_addr, __m128i mask)
+ /// </summary>
+ public static unsafe Vector128<int> MaskLoad(int* address, Vector128<int> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_maskload_epi32 (int const* mem_addr, __m128i mask)
+ /// </summary>
+ public static unsafe Vector128<uint> MaskLoad(uint* address, Vector128<uint> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_maskload_epi64 (__int64 const* mem_addr, __m128i mask)
+ /// </summary>
+ public static unsafe Vector128<long> MaskLoad(long* address, Vector128<long> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_maskload_epi64 (__int64 const* mem_addr, __m128i mask)
+ /// </summary>
+ public static unsafe Vector128<ulong> MaskLoad(ulong* address, Vector128<ulong> mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_maskload_epi32 (int const* mem_addr, __m256i mask)
+ /// </summary>
+ public static unsafe Vector256<int> MaskLoad(int* address, Vector256<int> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_maskload_epi32 (int const* mem_addr, __m256i mask)
+ /// </summary>
+ public static unsafe Vector256<uint> MaskLoad(uint* address, Vector256<uint> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_maskload_epi64 (__int64 const* mem_addr, __m256i mask)
+ /// </summary>
+ public static unsafe Vector256<long> MaskLoad(long* address, Vector256<long> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_maskload_epi64 (__int64 const* mem_addr, __m256i mask)
+ /// </summary>
+ public static unsafe Vector256<ulong> MaskLoad(ulong* address, Vector256<ulong> mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm_maskstore_epi32 (int* mem_addr, __m128i mask, __m128i a)
+ /// </summary>
+ public static unsafe void MaskStore(int* address, Vector128<int> mask, Vector128<int> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_maskstore_epi32 (int* mem_addr, __m128i mask, __m128i a)
+ /// </summary>
+ public static unsafe void MaskStore(uint* address, Vector128<uint> mask, Vector128<uint> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_maskstore_epi64 (__int64* mem_addr, __m128i mask, __m128i a)
+ /// </summary>
+ public static unsafe void MaskStore(long* address, Vector128<long> mask, Vector128<long> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_maskstore_epi64 (__int64* mem_addr, __m128i mask, __m128i a)
+ /// </summary>
+ public static unsafe void MaskStore(ulong* address, Vector128<ulong> mask, Vector128<ulong> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm256_maskstore_epi32 (int* mem_addr, __m256i mask, __m256i a)
+ /// </summary>
+ public static unsafe void MaskStore(int* address, Vector256<int> mask, Vector256<int> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_maskstore_epi32 (int* mem_addr, __m256i mask, __m256i a)
+ /// </summary>
+ public static unsafe void MaskStore(uint* address, Vector256<uint> mask, Vector256<uint> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_maskstore_epi64 (__int64* mem_addr, __m256i mask, __m256i a)
+ /// </summary>
+ public static unsafe void MaskStore(long* address, Vector256<long> mask, Vector256<long> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm256_maskstore_epi64 (__int64* mem_addr, __m256i mask, __m256i a)
+ /// </summary>
+ public static unsafe void MaskStore(ulong* address, Vector256<ulong> mask, Vector256<ulong> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_madd_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> MultiplyAddAdjacent(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_maddubs_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> MultiplyAddAdjacent(Vector256<byte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_max_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> Max(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_max_epu8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> Max(Vector256<byte> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_max_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> Max(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_max_epu16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> Max(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_max_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> Max(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_max_epu32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<uint> Max(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_min_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> Min(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_min_epu8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> Min(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_min_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> Min(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_min_epu16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> Min(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_min_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> Min(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_min_epu32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<uint> Min(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm256_movemask_epi8 (__m256i a)
+ /// </summary>
+ public static int MoveMask(Vector256<sbyte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// int _mm256_movemask_epi8 (__m256i a)
+ /// </summary>
+ public static int MoveMask(Vector256<byte> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_mpsadbw_epu8 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<ushort> MultipleSumAbsoluteDifferences(Vector256<byte> left, Vector256<byte> right, byte mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_mul_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<long> Multiply(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_mul_epu32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ulong> Multiply(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_mulhi_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> MultiplyHigh(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_mulhi_epu16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> MultiplyHigh(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_mulhrs_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> MultiplyHighRoundScale(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_mullo_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> MultiplyLow(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_mullo_epu16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> MultiplyLow(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_or_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> Or(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_or_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> Or(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_or_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> Or(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_or_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> Or(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_or_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> Or(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_or_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<uint> Or(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_or_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<long> Or(Vector256<long> left, Vector256<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_or_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ulong> Or(Vector256<ulong> left, Vector256<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_packs_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> PackSignedSaturate(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_packs_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> PackSignedSaturate(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_packus_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> PackUnsignedSaturate(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_packus_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> PackUnsignedSaturate(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_permute2x128_si256 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<sbyte> Permute2x128(Vector256<sbyte> left, Vector256<sbyte> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_permute2x128_si256 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<byte> Permute2x128(Vector256<byte> left, Vector256<byte> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_permute2x128_si256 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<short> Permute2x128(Vector256<short> left, Vector256<short> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_permute2x128_si256 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<ushort> Permute2x128(Vector256<ushort> left, Vector256<ushort> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_permute2x128_si256 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<int> Permute2x128(Vector256<int> left, Vector256<int> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_permute2x128_si256 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<uint> Permute2x128(Vector256<uint> left, Vector256<uint> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_permute2x128_si256 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<long> Permute2x128(Vector256<long> left, Vector256<long> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_permute2x128_si256 (__m256i a, __m256i b, const int imm8)
+ /// </summary>
+ public static Vector256<ulong> Permute2x128(Vector256<ulong> left, Vector256<ulong> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_permute4x64_epi64 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<long> Permute4x64(Vector256<long> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_permute4x64_epi64 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<ulong> Permute4x64(Vector256<ulong> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_permute4x64_pd (__m256d a, const int imm8)
+ /// </summary>
+ public static Vector256<double> Permute4x64(Vector256<double> value, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_permutevar8x32_epi32 (__m256i a, __m256i idx)
+ /// </summary>
+ public static Vector256<int> PermuteVar8x32(Vector256<int> left, Vector256<int> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_permutevar8x32_epi32 (__m256i a, __m256i idx)
+ /// </summary>
+ public static Vector256<uint> PermuteVar8x32(Vector256<uint> left, Vector256<uint> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_permutevar8x32_ps (__m256 a, __m256i idx)
+ /// </summary>
+ public static Vector256<float> PermuteVar8x32(Vector256<float> left, Vector256<float> mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_slli_epi16 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<short> ShiftLeftLogical(Vector256<short> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_slli_epi16 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<ushort> ShiftLeftLogical(Vector256<ushort> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_slli_epi32 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<int> ShiftLeftLogical(Vector256<int> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_slli_epi32 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<uint> ShiftLeftLogical(Vector256<uint> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_slli_epi64 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<long> ShiftLeftLogical(Vector256<long> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_slli_epi64 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<ulong> ShiftLeftLogical(Vector256<ulong> value, byte count) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_bslli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<sbyte> ShiftLeftLogical128BitLane(Vector256<sbyte> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bslli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<byte> ShiftLeftLogical128BitLane(Vector256<byte> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bslli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<short> ShiftLeftLogical128BitLane(Vector256<short> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bslli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<ushort> ShiftLeftLogical128BitLane(Vector256<ushort> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bslli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<int> ShiftLeftLogical128BitLane(Vector256<int> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bslli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<uint> ShiftLeftLogical128BitLane(Vector256<uint> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bslli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<long> ShiftLeftLogical128BitLane(Vector256<long> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bslli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<ulong> ShiftLeftLogical128BitLane(Vector256<ulong> value, byte numBytes) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_sllv_epi32 (__m256i a, __m256i count)
+ /// </summary>
+ public static Vector256<int> ShiftLeftLogicalVariable(Vector256<int> value, Vector256<uint> count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sllv_epi32 (__m256i a, __m256i count)
+ /// </summary>
+ public static Vector256<uint> ShiftLeftLogicalVariable(Vector256<uint> value, Vector256<uint> count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sllv_epi64 (__m256i a, __m256i count)
+ /// </summary>
+ public static Vector256<long> ShiftLeftLogicalVariable(Vector256<long> value, Vector256<ulong> count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sllv_epi64 (__m256i a, __m256i count)
+ /// </summary>
+ public static Vector256<ulong> ShiftLeftLogicalVariable(Vector256<ulong> value, Vector256<ulong> count) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_srai_epi16 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<short> ShiftRightArithmetic(Vector256<short> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_srai_epi32 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<int> ShiftRightArithmetic(Vector256<int> value, byte count) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_srav_epi32 (__m256i a, __m256i count)
+ /// </summary>
+ public static Vector256<int> ShiftRightArithmeticVariable(Vector256<int> value, Vector256<uint> count) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_srli_epi16 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<short> ShiftRightLogical(Vector256<short> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_srli_epi16 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<ushort> ShiftRightLogical(Vector256<ushort> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_srli_epi32 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<int> ShiftRightLogical(Vector256<int> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_srli_epi32 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<uint> ShiftRightLogical(Vector256<uint> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_srli_epi64 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<long> ShiftRightLogical(Vector256<long> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_srli_epi64 (__m256i a, int imm8)
+ /// </summary>
+ public static Vector256<ulong> ShiftRightLogical(Vector256<ulong> value, byte count) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_bsrli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<sbyte> ShiftRightLogical128BitLane(Vector256<sbyte> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bsrli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<byte> ShiftRightLogical128BitLane(Vector256<byte> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bsrli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<short> ShiftRightLogical128BitLane(Vector256<short> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bsrli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<ushort> ShiftRightLogical128BitLane(Vector256<ushort> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bsrli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<int> ShiftRightLogical128BitLane(Vector256<int> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bsrli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<uint> ShiftRightLogical128BitLane(Vector256<uint> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bsrli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<long> ShiftRightLogical128BitLane(Vector256<long> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_bsrli_epi128 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<ulong> ShiftRightLogical128BitLane(Vector256<ulong> value, byte numBytes) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_srlv_epi32 (__m256i a, __m256i count)
+ /// </summary>
+ public static Vector256<int> ShiftRightLogicalVariable(Vector256<int> value, Vector256<uint> count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_srlv_epi32 (__m256i a, __m256i count)
+ /// </summary>
+ public static Vector256<uint> ShiftRightLogicalVariable(Vector256<uint> value, Vector256<uint> count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_srlv_epi64 (__m256i a, __m256i count)
+ /// </summary>
+ public static Vector256<long> ShiftRightLogicalVariable(Vector256<long> value, Vector256<ulong> count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_srlv_epi64 (__m256i a, __m256i count)
+ /// </summary>
+ public static Vector256<ulong> ShiftRightLogicalVariable(Vector256<ulong> value, Vector256<ulong> count) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_shuffle_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> Shuffle(Vector256<sbyte> value, Vector256<sbyte> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_shuffle_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> Shuffle(Vector256<byte> value, Vector256<byte> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_shuffle_epi32 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<int> Shuffle(Vector256<int> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_shuffle_epi32 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<uint> Shuffle(Vector256<uint> value, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_shufflehi_epi16 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<short> ShuffleHigh(Vector256<short> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_shufflehi_epi16 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<ushort> ShuffleHigh(Vector256<ushort> value, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_shufflelo_epi16 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<short> ShuffleLow(Vector256<short> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_shufflelo_epi16 (__m256i a, const int imm8)
+ /// </summary>
+ public static Vector256<ushort> ShuffleLow(Vector256<ushort> value, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_sign_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> Sign(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sign_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> Sign(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sign_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> Sign(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_sub_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> Subtract(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sub_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> Subtract(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sub_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> Subtract(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sub_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> Subtract(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sub_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> Subtract(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sub_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<uint> Subtract(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sub_epi64 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<long> Subtract(Vector256<long> left, Vector256<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_sub_epi64 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ulong> Subtract(Vector256<ulong> left, Vector256<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_subs_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> SubtractSaturate(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_subs_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> SubtractSaturate(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_subs_epu8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> SubtractSaturate(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_subs_epu16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> SubtractSaturate(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_sad_epu8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ulong> SumAbsoluteDifferences(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_unpackhi_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> UnpackHigh(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpackhi_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> UnpackHigh(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpackhi_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> UnpackHigh(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpackhi_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> UnpackHigh(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpackhi_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> UnpackHigh(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpackhi_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<uint> UnpackHigh(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpackhi_epi64 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<long> UnpackHigh(Vector256<long> left, Vector256<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpackhi_epi64 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ulong> UnpackHigh(Vector256<ulong> left, Vector256<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_unpacklo_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> UnpackLow(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpacklo_epi8 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> UnpackLow(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpacklo_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> UnpackLow(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpacklo_epi16 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> UnpackLow(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpacklo_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> UnpackLow(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpacklo_epi32 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<uint> UnpackLow(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpacklo_epi64 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<long> UnpackLow(Vector256<long> left, Vector256<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_unpacklo_epi64 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ulong> UnpackLow(Vector256<ulong> left, Vector256<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m256i _mm256_xor_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<sbyte> Xor(Vector256<sbyte> left, Vector256<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_xor_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<byte> Xor(Vector256<byte> left, Vector256<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_xor_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<short> Xor(Vector256<short> left, Vector256<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_xor_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ushort> Xor(Vector256<ushort> left, Vector256<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_xor_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<int> Xor(Vector256<int> left, Vector256<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_xor_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<uint> Xor(Vector256<uint> left, Vector256<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_xor_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<long> Xor(Vector256<long> left, Vector256<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256i _mm256_xor_si256 (__m256i a, __m256i b)
+ /// </summary>
+ public static Vector256<ulong> Xor(Vector256<ulong> left, Vector256<ulong> right) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Bmi1.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Bmi1.cs
new file mode 100644
index 0000000000..a2bfcffedc
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Bmi1.cs
@@ -0,0 +1,80 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel BMI1 hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Bmi1
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// unsigned int _andn_u32 (unsigned int a, unsigned int b)
+ /// </summary>
+ public static uint AndNot(uint left, uint right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _andn_u64 (unsigned __int64 a, unsigned __int64 b)
+ /// </summary>
+ public static ulong AndNot(ulong left, ulong right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// unsigned int _bextr_u32 (unsigned int a, unsigned int start, unsigned int len)
+ /// </summary>
+ public static uint BitFieldExtract(uint value, uint start, uint length) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _bextr_u64 (unsigned __int64 a, unsigned int start, unsigned int len)
+ /// </summary>
+ public static ulong BitFieldExtract(ulong value, ulong start, ulong length) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned int _bextr2_u32 (unsigned int a, unsigned int control)
+ /// </summary>
+ public static uint BitFieldExtract(uint value, uint control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _bextr2_u64 (unsigned __int64 a, unsigned __int64 control)
+ /// </summary>
+ public static ulong BitFieldExtract(ulong value, ulong control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// unsigned int _blsi_u32 (unsigned int a)
+ /// </summary>
+ public static uint ExtractLowestSetBit(uint value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _blsi_u64 (unsigned __int64 a)
+ /// </summary>
+ public static ulong ExtractLowestSetBit(ulong value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// unsigned int _blsmsk_u32 (unsigned int a)
+ /// </summary>
+ public static uint GetMaskUptoLowestSetBit(uint value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _blsmsk_u64 (unsigned __int64 a)
+ /// </summary>
+ public static ulong GetMaskUptoLowestSetBit(ulong value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// unsigned int _blsr_u32 (unsigned int a)
+ /// </summary>
+ public static uint ResetLowestSetBit(uint value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _blsr_u64 (unsigned __int64 a)
+ /// </summary>
+ public static ulong ResetLowestSetBit(ulong value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_tzcnt_32 (unsigned int a)
+ /// </summary>
+ public static uint TrailingZeroCount(uint value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __int64 _mm_tzcnt_64 (unsigned __int64 a)
+ /// </summary>
+ public static ulong TrailingZeroCount(ulong value) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Bmi2.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Bmi2.cs
new file mode 100644
index 0000000000..a9a73de926
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Bmi2.cs
@@ -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.
+
+using System;
+using System.Runtime.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel BMI2 hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Bmi2
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// unsigned int _bzhi_u32 (unsigned int a, unsigned int index)
+ /// </summary>
+ public static uint ZeroHighBits(uint value, uint index) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _bzhi_u64 (unsigned __int64 a, unsigned int index)
+ /// </summary>
+ public static ulong ZeroHighBits(ulong value, ulong index) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// unsigned int _mulx_u32 (unsigned int a, unsigned int b, unsigned int* hi)
+ /// </summary>
+ public static unsafe uint MultiplyNoFlags(uint left, uint right, uint* high) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _mulx_u64 (unsigned __int64 a, unsigned __int64 b, unsigned __int64* hi)
+ /// </summary>
+ public static unsafe ulong MultiplyNoFlags(ulong left, ulong right, ulong* high) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// unsigned int _pdep_u32 (unsigned int a, unsigned int mask)
+ /// </summary>
+ public static uint ParallelBitDeposit(uint value, uint mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _pdep_u64 (unsigned __int64 a, unsigned __int64 mask)
+ /// </summary>
+ public static ulong ParallelBitDeposit(ulong value, ulong mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// unsigned int _pext_u32 (unsigned int a, unsigned int mask)
+ /// </summary>
+ public static uint ParallelBitExtract(uint value, uint mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _pext_u64 (unsigned __int64 a, unsigned __int64 mask)
+ /// </summary>
+ public static ulong ParallelBitExtract(ulong value, ulong mask) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Enums.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Enums.cs
new file mode 100644
index 0000000000..6d6a09a0b6
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Enums.cs
@@ -0,0 +1,189 @@
+// 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.Runtime.Intrinsics.X86
+{
+ public enum FloatComparisonMode : byte
+ {
+ /// <summary>
+ /// _CMP_EQ_OQ
+ /// </summary>
+ EqualOrderedNonSignaling = 0,
+
+ /// <summary>
+ /// _CMP_LT_OS
+ /// </summary>
+ LessThanOrderedSignaling = 1,
+
+ /// <summary>
+ /// _CMP_LE_OS
+ /// </summary>
+ LessThanOrEqualOrderedSignaling = 2,
+
+ /// <summary>
+ /// _CMP_UNORD_Q
+ /// </summary>
+ UnorderedNonSignaling = 3,
+
+ /// <summary>
+ /// _CMP_NEQ_UQ
+ /// </summary>
+ NotEqualUnorderedNonSignaling = 4,
+
+ /// <summary>
+ /// _CMP_NLT_US
+ /// </summary>
+ NotLessThanUnorderedSignaling = 5,
+
+ /// <summary>
+ /// _CMP_NLE_US
+ /// </summary>
+ NotLessThanOrEqualUnorderedSignaling = 6,
+
+ /// <summary>
+ /// _CMP_ORD_Q
+ /// </summary>
+ OrderedNonSignaling = 7,
+
+ /// <summary>
+ /// _CMP_EQ_UQ
+ /// </summary>
+ EqualUnorderedNonSignaling = 8,
+
+ /// <summary>
+ /// _CMP_NGE_US
+ /// </summary>
+ NotGreaterThanOrEqualUnorderedSignaling = 9,
+
+ /// <summary>
+ /// _CMP_NGT_US
+ /// </summary>
+ NotGreaterThanUnorderedSignaling = 10,
+
+ /// <summary>
+ /// _CMP_FALSE_OQ
+ /// </summary>
+ FalseOrderedNonSignaling = 11,
+
+ /// <summary>
+ /// _CMP_NEQ_OQ
+ /// </summary>
+ NotEqualOrderedNonSignaling = 12,
+
+ /// <summary>
+ /// _CMP_GE_OS
+ /// </summary>
+ GreaterThanOrEqualOrderedSignaling = 13,
+
+ /// <summary>
+ /// _CMP_GT_OS
+ /// </summary>
+ GreaterThanOrderedSignaling = 14,
+
+ /// <summary>
+ /// _CMP_TRUE_UQ
+ /// </summary>
+ TrueUnorderedNonSignaling = 15,
+
+ /// <summary>
+ /// _CMP_EQ_OS
+ /// </summary>
+ EqualOrderedSignaling = 16,
+
+ /// <summary>
+ /// _CMP_LT_OQ
+ /// </summary>
+ LessThanOrderedNonSignaling = 17,
+
+ /// <summary>
+ /// _CMP_LE_OQ
+ /// </summary>
+ LessThanOrEqualOrderedNonSignaling = 18,
+
+ /// <summary>
+ /// _CMP_UNORD_S
+ /// </summary>
+ UnorderedSignaling = 19,
+
+ /// <summary>
+ /// _CMP_NEQ_US
+ /// </summary>
+ NotEqualUnorderedSignaling = 20,
+
+ /// <summary>
+ /// _CMP_NLT_UQ
+ /// </summary>
+ NotLessThanUnorderedNonSignaling = 21,
+
+ /// <summary>
+ /// _CMP_NLE_UQ
+ /// </summary>
+ NotLessThanOrEqualUnorderedNonSignaling = 22,
+
+ /// <summary>
+ /// _CMP_ORD_S
+ /// </summary>
+ OrderedSignaling = 23,
+
+ /// <summary>
+ /// _CMP_EQ_US
+ /// </summary>
+ EqualUnorderedSignaling = 24,
+
+ /// <summary>
+ /// _CMP_NGE_UQ
+ /// </summary>
+ NotGreaterThanOrEqualUnorderedNonSignaling = 25,
+
+ /// <summary>
+ /// _CMP_NGT_UQ
+ /// </summary>
+ NotGreaterThanUnorderedNonSignaling = 26,
+
+ /// <summary>
+ /// _CMP_FALSE_OS
+ /// </summary>
+ FalseOrderedSignaling = 27,
+
+ /// <summary>
+ /// _CMP_NEQ_OS
+ /// </summary>
+ NotEqualOrderedSignaling = 28,
+
+ /// <summary>
+ /// _CMP_GE_OQ
+ /// </summary>
+ GreaterThanOrEqualOrderedNonSignaling = 29,
+
+ /// <summary>
+ /// _CMP_GT_OQ
+ /// </summary>
+ GreaterThanOrderedNonSignaling = 30,
+
+ /// <summary>
+ /// _CMP_TRUE_US
+ /// </summary>
+ TrueUnorderedSignaling = 31,
+ }
+
+ public enum StringComparisonMode : byte {
+ EqualAny = 0x00,
+ Ranges = 0x04,
+ EqualEach = 0x08,
+ EqualOrdered = 0x0c,
+ NegativePolarity = 0x10,
+ MaskedNegativePolarity = 0x30,
+ LeastSignificant = 0x00,
+ MostSignificant = 0x40,
+ }
+
+
+ public enum ResultsFlag : byte {
+ CFlag = 0,
+ NotCFlagAndNotZFlag = 1,
+ OFlag = 2,
+ SFlag = 3,
+ ZFlag = 4,
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Fma.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Fma.cs
new file mode 100644
index 0000000000..84160f293f
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Fma.cs
@@ -0,0 +1,119 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel FMA hardware instructions via intrinsics
+ /// </summary>
+ public static class Fma
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// __m128 _mm_fmadd_ps (__m128 a, __m128 b, __m128 c)
+ /// </summary>
+ public static Vector128<float> MultiplyAdd(Vector128<float> a, Vector128<float> b, Vector128<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_fmadd_pd (__m128d a, __m128d b, __m128d c)
+ /// </summary>
+ public static Vector128<double> MultiplyAdd(Vector128<double> a, Vector128<double> b, Vector128<double> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_fmadd_ps (__m256 a, __m256 b, __m256 c)
+ /// </summary>
+ public static Vector256<float> MultiplyAdd(Vector256<float> a, Vector256<float> b, Vector256<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_fmadd_pd (__m256d a, __m256d b, __m256d c)
+ /// </summary>
+ public static Vector256<double> MultiplyAdd(Vector256<double> a, Vector256<double> b, Vector256<double> c) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_fmaddsub_ps (__m128 a, __m128 b, __m128 c)
+ /// </summary>
+ public static Vector128<float> MultiplyAddSubtract(Vector128<float> a, Vector128<float> b, Vector128<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_fmaddsub_pd (__m128d a, __m128d b, __m128d c)
+ /// </summary>
+ public static Vector128<double> MultiplyAddSubtract(Vector128<double> a, Vector128<double> b, Vector128<double> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_fmaddsub_ps (__m256 a, __m256 b, __m256 c)
+ /// </summary>
+ public static Vector256<float> MultiplyAddSubtract(Vector256<float> a, Vector256<float> b, Vector256<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_fmaddsub_pd (__m256d a, __m256d b, __m256d c)
+ /// </summary>
+ public static Vector256<double> MultiplyAddSubtract(Vector256<double> a, Vector256<double> b, Vector256<double> c) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_fmsub_ps (__m128 a, __m128 b, __m128 c)
+ /// </summary>
+ public static Vector128<float> MultiplySubtract(Vector128<float> a, Vector128<float> b, Vector128<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_fmsub_pd (__m128d a, __m128d b, __m128d c)
+ /// </summary>
+ public static Vector128<double> MultiplySubtract(Vector128<double> a, Vector128<double> b, Vector128<double> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_fmsub_ps (__m256 a, __m256 b, __m256 c)
+ /// </summary>
+ public static Vector256<float> MultiplySubtract(Vector256<float> a, Vector256<float> b, Vector256<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_fmsub_pd (__m256d a, __m256d b, __m256d c)
+ /// </summary>
+ public static Vector256<double> MultiplySubtract(Vector256<double> a, Vector256<double> b, Vector256<double> c) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_fmsubadd_ps (__m128 a, __m128 b, __m128 c)
+ /// </summary>
+ public static Vector128<float> MultiplySubtractAdd(Vector128<float> a, Vector128<float> b, Vector128<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_fmsubadd_pd (__m128d a, __m128d b, __m128d c)
+ /// </summary>
+ public static Vector128<double> MultiplySubtractAdd(Vector128<double> a, Vector128<double> b, Vector128<double> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_fmsubadd_ps (__m256 a, __m256 b, __m256 c)
+ /// </summary>
+ public static Vector256<float> MultiplySubtractAdd(Vector256<float> a, Vector256<float> b, Vector256<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_fmsubadd_pd (__m256d a, __m256d b, __m256d c)
+ /// </summary>
+ public static Vector256<double> MultiplySubtractAdd(Vector256<double> a, Vector256<double> b, Vector256<double> c) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_fnmadd_ps (__m128 a, __m128 b, __m128 c)
+ /// </summary>
+ public static Vector128<float> MultiplyAddNegated(Vector128<float> a, Vector128<float> b, Vector128<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_fnmadd_pd (__m128d a, __m128d b, __m128d c)
+ /// </summary>
+ public static Vector128<double> MultiplyAddNegated(Vector128<double> a, Vector128<double> b, Vector128<double> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_fnmadd_ps (__m256 a, __m256 b, __m256 c)
+ /// </summary>
+ public static Vector256<float> MultiplyAddNegated(Vector256<float> a, Vector256<float> b, Vector256<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_fnmadd_pd (__m256d a, __m256d b, __m256d c)
+ /// </summary>
+ public static Vector256<double> MultiplyAddNegated(Vector256<double> a, Vector256<double> b, Vector256<double> c) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_fnmsub_ps (__m128 a, __m128 b, __m128 c)
+ /// </summary>
+ public static Vector128<float> MultiplySubtractNegated(Vector128<float> a, Vector128<float> b, Vector128<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_fnmsub_pd (__m128d a, __m128d b, __m128d c)
+ /// </summary>
+ public static Vector128<double> MultiplySubtractNegated(Vector128<double> a, Vector128<double> b, Vector128<double> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256 _mm256_fnmsub_ps (__m256 a, __m256 b, __m256 c)
+ /// </summary>
+ public static Vector256<float> MultiplySubtractNegated(Vector256<float> a, Vector256<float> b, Vector256<float> c) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m256d _mm256_fnmsub_pd (__m256d a, __m256d b, __m256d c)
+ /// </summary>
+ public static Vector256<double> MultiplySubtractNegated(Vector256<double> a, Vector256<double> b, Vector256<double> c) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Lzcnt.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Lzcnt.cs
new file mode 100644
index 0000000000..ffc656e43b
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Lzcnt.cs
@@ -0,0 +1,27 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel LZCNT hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Lzcnt
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// unsigned int _lzcnt_u32 (unsigned int a)
+ /// </summary>
+ public static uint LeadingZeroCount(uint value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _lzcnt_u64 (unsigned __int64 a)
+ /// </summary>
+ public static ulong LeadingZeroCount(ulong value) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Pclmulqdq.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Pclmulqdq.cs
new file mode 100644
index 0000000000..f8576f89fe
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Pclmulqdq.cs
@@ -0,0 +1,27 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel PCLMULQDQ hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Pclmulqdq
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// __m128i _mm_clmulepi64_si128 (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<long> CarryLessMultiply(Vector128<long> left, Vector128<long> right, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_clmulepi64_si128 (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<ulong> CarryLessMultiply(Vector128<ulong> left, Vector128<ulong> right, byte control) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Popcnt.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Popcnt.cs
new file mode 100644
index 0000000000..66f2ab127d
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Popcnt.cs
@@ -0,0 +1,27 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel POPCNT hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Popcnt
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// int _mm_popcnt_u32 (unsigned int a)
+ /// </summary>
+ public static int PopCount(uint value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __int64 _mm_popcnt_u64 (unsigned __int64 a)
+ /// </summary>
+ public static long PopCount(ulong value) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse.cs
new file mode 100644
index 0000000000..c85aee3015
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse.cs
@@ -0,0 +1,218 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel SSE hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Sse
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// __m128 _mm_add_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> Add(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_and_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> And(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_andnot_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> AndNot(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmpeq_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareEqual(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmpgt_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareGreaterThan(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmpge_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareGreaterThanOrEqual(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmplt_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareLessThan(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmple_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareLessThanOrEqual(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmpneq_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareNotEqual(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmpngt_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareNotGreaterThan(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmpnge_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareNotGreaterThanOrEqual(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmpnlt_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareNotLessThan(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmpnle_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareNotLessThanOrEqual(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmpord_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareOrdered(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_cmpunord_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> CompareUnordered(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_div_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> Divide(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_loadu_ps (float const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<float> Load(float* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_load_ps (float const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<float> LoadAligned(float* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_max_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> Max(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_min_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> Min(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_movehl_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> MoveHighToLow(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_movelh_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> MoveLowToHigh(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_mul_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> Multiply(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_or_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> Or(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_rcp_ps (__m128 a)
+ /// </summary>
+ public static Vector128<float> Reciprocal(Vector128<float> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_rsqrt_ps (__m128 a)
+ /// </summary>
+ public static Vector128<float> ReciprocalSquareRoot(Vector128<float> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_set_ps (float e3, float e2, float e1, float e0)
+ /// </summary>
+ public static Vector128<float> Set(float e3, float e2, float e1, float e0) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_set1_ps (float a)
+ /// </summary>
+ public static Vector128<float> Set1(float value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_setzero_ps (void)
+ /// </summary>
+ public static Vector128<float> SetZero() { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_castpd_ps (__m128d a)
+ /// __m128i _mm_castpd_si128 (__m128d a)
+ /// __m128d _mm_castps_pd (__m128 a)
+ /// __m128i _mm_castps_si128 (__m128 a)
+ /// __m128d _mm_castsi128_pd (__m128i a)
+ /// __m128 _mm_castsi128_ps (__m128i a)
+ /// </summary>
+ public static Vector128<U> StaticCast<T, U>(Vector128<T> value) where T : struct where U : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_shuffle_ps (__m128 a, __m128 b, unsigned int control)
+ /// </summary>
+ public static Vector128<float> Shuffle(Vector128<float> left, Vector128<float> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_sqrt_ps (__m128 a)
+ /// </summary>
+ public static Vector128<float> Sqrt(Vector128<float> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm_store_ps (float* mem_addr, __m128 a)
+ /// </summary>
+ public static unsafe void StoreAligned(float* address, Vector128<float> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm_stream_ps (float* mem_addr, __m128 a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(float* address, Vector128<float> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm_storeu_ps (float* mem_addr, __m128 a)
+ /// </summary>
+ public static unsafe void Store(float* address, Vector128<float> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_sub_ps (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<float> Subtract(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_unpackhi_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> UnpackHigh(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_unpacklo_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> UnpackLow(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_xor_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> Xor(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse2.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse2.cs
new file mode 100644
index 0000000000..ffe35f883c
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse2.cs
@@ -0,0 +1,1057 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel SSE2 hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Sse2
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// __m128i _mm_add_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> Add(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_add_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> Add(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_add_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> Add(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_add_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> Add(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_add_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> Add(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_add_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<uint> Add(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_add_epi64 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> Add(Vector128<byte> left, Vector128<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_add_epi64 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ulong> Add(Vector128<sbyte> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_add_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> Add(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_adds_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> AddSaturate(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_adds_epu8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> AddSaturate(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_adds_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> AddSaturate(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_adds_epu16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> AddSaturate(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_and_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> And(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_and_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> And(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_and_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> And(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_and_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> And(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_and_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> And(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_and_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<uint> And(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_and_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> And(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_and_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ulong> And(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_and_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> And(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_andnot_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> AndNot(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_andnot_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> AndNot(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_andnot_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> AndNot(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_andnot_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> AndNot(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_andnot_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> AndNot(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_andnot_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<uint> AndNot(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_andnot_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> AndNot(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_andnot_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ulong> AndNot(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_andnot_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> AndNot(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_avg_epu8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> Average(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_avg_epu16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> Average(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpeq_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> CompareEqual(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cmpeq_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> CompareEqual(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cmpeq_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> CompareEqual(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cmpeq_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> CompareEqual(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cmpeq_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> CompareEqual(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cmpeq_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<uint> CompareEqual(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_cmpeq_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareEqual(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpgt_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> CompareGreaterThan(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cmpgt_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> CompareGreaterThan(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cmpgt_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> CompareGreaterThan(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_cmpgt_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareGreaterThan(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_cmpge_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareGreaterThanOrEqual(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmplt_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> CompareLessThan(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cmplt_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> CompareLessThan(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cmplt_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> CompareLessThan(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_cmplt_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareLessThan(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_cmple_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareLessThanOrEqual(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_cmpneq_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareNotEqual(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_cmpngt_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareNotGreaterThan(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_cmpnge_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareNotGreaterThanOrEqual(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_cmpnlt_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareNotLessThan(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_cmpnle_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareNotLessThanOrEqual(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_cmpord_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareOrdered(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_cmpunord_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> CompareUnordered(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cvtps_epi32 (__m128 a)
+ /// </summary>
+ public static Vector128<int> ConvertToInt(Vector128<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtpd_epi32 (__m128d a)
+ /// </summary>
+ public static Vector128<int> ConvertToInt(Vector128<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128 _mm_cvtepi32_ps (__m128i a)
+ /// </summary>
+ public static Vector128<float> ConvertToFloat(Vector128<int> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128 _mm_cvtpd_ps (__m128d a)
+ /// </summary>
+ public static Vector128<float> ConvertToFloat(Vector128<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_cvtepi32_pd (__m128i a)
+ /// </summary>
+ public static Vector128<double> ConvertToDouble(Vector128<int> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_cvtps_pd (__m128 a)
+ /// </summary>
+ public static Vector128<double> ConvertToDouble(Vector128<float> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cvttps_epi32 (__m128 a)
+ /// </summary>
+ public static Vector128<int> ConvertToIntWithTruncation(Vector128<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvttpd_epi32 (__m128d a)
+ /// </summary>
+ public static Vector128<int> ConvertToIntWithTruncation(Vector128<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_div_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> Divide(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_extract_epi16 (__m128i a, int immediate)
+ /// </summary>
+ public static short ExtractShort<T>(Vector128<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// int _mm_extract_epi16 (__m128i a, int immediate)
+ /// </summary>
+ public static ushort ExtractUshort<T>(Vector128<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_insert_epi16 (__m128i a, int i, int immediate)
+ /// </summary>
+ public static Vector128<T> InsertShort<T>(Vector128<T> value, short data, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_insert_epi16 (__m128i a, int i, int immediate)
+ /// </summary>
+ public static Vector128<T> InsertUshort<T>(Vector128<T> value, ushort data, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_loadu_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<sbyte> Load(sbyte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_loadu_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<byte> Load(byte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_loadu_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<short> Load(short* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_loadu_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<ushort> Load(ushort* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_loadu_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<int> Load(int* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_loadu_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<uint> Load(uint* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_loadu_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<long> Load(long* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_loadu_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<ulong> Load(ulong* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_loadu_pd (double const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<double> Load(double* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_load_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<sbyte> LoadAligned(sbyte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_load_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<byte> LoadAligned(byte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_load_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<short> LoadAligned(short* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_load_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<ushort> LoadAligned(ushort* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_load_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<int> LoadAligned(int* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_load_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<uint> LoadAligned(uint* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_load_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<long> LoadAligned(long* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_load_si128 (__m128i const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<ulong> LoadAligned(ulong* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_load_pd (double const* mem_address)
+ /// </summary>
+ public static unsafe Vector128<double> LoadAligned(double* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm_maskmoveu_si128 (__m128i a, __m128i mask, char* mem_address)
+ /// </summary>
+ public static unsafe void MaskMove(Vector128<sbyte> source, Vector128<sbyte> mask, sbyte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_maskmoveu_si128 (__m128i a, __m128i mask, char* mem_address)
+ /// </summary>
+ public static unsafe void MaskMove(Vector128<byte> source, Vector128<byte> mask, byte* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_max_epu8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> Max(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_max_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> Max(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_max_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> Max(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_min_epu8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> Min(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_min_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> Min(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_min_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> Min(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_movemask_epi8 (__m128i a)
+ /// </summary>
+ public static int MoveMask(Vector128<sbyte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// int _mm_movemask_pd (__m128d a)
+ /// </summary>
+ public static int MoveMask(Vector128<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_mul_epu32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ulong> Multiply(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_mul_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> Multiply(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_mulhi_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> MultiplyHi(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_mulhi_epu16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> MultiplyHi(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_madd_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> MultiplyHorizontalAdd(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_mullo_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> MultiplyLow(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_or_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> Or(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_or_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> Or(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_or_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> Or(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_or_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> Or(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_or_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> Or(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_or_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<uint> Or(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_or_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> Or(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_or_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ulong> Or(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_or_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> Or(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_packs_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> PackSignedSaturate(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_packs_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> PackSignedSaturate(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_packus_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> PackUnsignedSaturate(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// ___m128i _mm_set_epi8 (char e15, char e14, char e13, char e12, char e11, char e10, char e9, char e8, char e7, char e6, char e5, char e4, char e3, char e2, char e1, char e0)
+ /// </summary>
+ public static Vector128<sbyte> Set(sbyte e15, sbyte e14, sbyte e13, sbyte e12, sbyte e11, sbyte e10, sbyte e9, sbyte e8, sbyte e7, sbyte e6, sbyte e5, sbyte e4, sbyte e3, sbyte e2, sbyte e1, sbyte e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// ___m128i _mm_set_epi8 (char e15, char e14, char e13, char e12, char e11, char e10, char e9, char e8, char e7, char e6, char e5, char e4, char e3, char e2, char e1, char e0)
+ /// </summary>
+ public static Vector128<byte> Set(byte e15, byte e14, byte e13, byte e12, byte e11, byte e10, byte e9, byte e8, byte e7, byte e6, byte e5, byte e4, byte e3, byte e2, byte e1, byte e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set_epi16 (short e7, short e6, short e5, short e4, short e3, short e2, short e1, short e0)
+ /// </summary>
+ public static Vector128<short> Set(short e7, short e6, short e5, short e4, short e3, short e2, short e1, short e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set_epi16 (short e7, short e6, short e5, short e4, short e3, short e2, short e1, short e0)
+ /// </summary>
+ public static Vector128<ushort> Set(ushort e7, ushort e6, ushort e5, ushort e4, ushort e3, ushort e2, ushort e1, ushort e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set_epi32 (int e3, int e2, int e1, int e0)
+ /// </summary>
+ public static Vector128<int> Set(int e3, int e2, int e1, int e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set_epi32 (int e3, int e2, int e1, int e0)
+ /// </summary>
+ public static Vector128<uint> Set(uint e3, uint e2, uint e1, uint e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set_epi64x (__int64 e1, __int64 e0)
+ /// </summary>
+ public static Vector128<long> Set(long e1, long e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set_epi64x (__int64 e1, __int64 e0)
+ /// </summary>
+ public static Vector128<ulong> Set(ulong e1, ulong e0) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_set_pd (double e1, double e0)
+ /// </summary>
+ public static Vector128<double> Set(double e1, double e0) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_set1_epi8 (char a)
+ /// </summary>
+ public static Vector128<byte> Set1(byte value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set1_epi8 (char a)
+ /// </summary>
+ public static Vector128<sbyte> Set1(sbyte value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set1_epi16 (short a)
+ /// </summary>
+ public static Vector128<short> Set1(short value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set1_epi16 (short a)
+ /// </summary>
+ public static Vector128<ushort> Set1(ushort value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set1_epi32 (int a)
+ /// </summary>
+ public static Vector128<int> Set1(int value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set1_epi32 (int a)
+ /// </summary>
+ public static Vector128<uint> Set1(uint value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set1_epi64x (long long a)
+ /// </summary>
+ public static Vector128<long> Set1(long value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_set1_epi64x (long long a)
+ /// </summary>
+ public static Vector128<ulong> Set1(ulong value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_set1_pd (double a)
+ /// </summary>
+ public static Vector128<double> Set1(double value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_setzero_si128 ()
+ /// __m128d _mm_setzero_pd (void)
+ /// </summary>
+ public static Vector128<T> SetZero<T>() where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_sad_epu8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> SumAbsoluteDifferences(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_shuffle_epi32 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<int> Shuffle(Vector128<int> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_shuffle_epi32 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<uint> Shuffle(Vector128<uint> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_shuffle_pd (__m128d a, __m128d b, int immediate)
+ /// </summary>
+ public static Vector128<double> Shuffle(Vector128<double> left, Vector128<double> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_shufflehi_epi16 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<short> ShuffleHigh(Vector128<short> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_shufflehi_epi16 (__m128i a, int control)
+ /// </summary>
+ public static Vector128<ushort> ShuffleHigh(Vector128<ushort> value, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_shufflelo_epi16 (__m128i a, int control)
+ /// </summary>
+ public static Vector128<short> ShuffleLow(Vector128<short> value, byte control) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_shufflelo_epi16 (__m128i a, int control)
+ /// </summary>
+ public static Vector128<ushort> ShuffleLow(Vector128<ushort> value, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_slli_epi16 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<short> ShiftLeftLogical(Vector128<short> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_slli_epi16 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<ushort> ShiftLeftLogical(Vector128<ushort> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_slli_epi32 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<int> ShiftLeftLogical(Vector128<int> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_slli_epi32 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<uint> ShiftLeftLogical(Vector128<uint> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_slli_epi64 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<long> ShiftLeftLogical(Vector128<long> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_slli_epi64 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<ulong> ShiftLeftLogical(Vector128<ulong> value, byte count) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_bslli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<sbyte> ShiftLeftLogical128BitLane(Vector128<sbyte> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bslli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<byte> ShiftLeftLogical128BitLane(Vector128<byte> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bslli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<short> ShiftLeftLogical128BitLane(Vector128<short> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bslli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<ushort> ShiftLeftLogical128BitLane(Vector128<ushort> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bslli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<int> ShiftLeftLogical128BitLane(Vector128<int> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bslli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<uint> ShiftLeftLogical128BitLane(Vector128<uint> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bslli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<long> ShiftLeftLogical128BitLane(Vector128<long> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bslli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<ulong> ShiftLeftLogical128BitLane(Vector128<ulong> value, byte numBytes) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_srai_epi16 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<short> ShiftRightArithmetic(Vector128<short> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_srai_epi32 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<int> ShiftRightArithmetic(Vector128<int> value, byte count) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_srli_epi16 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<short> ShiftRightLogical(Vector128<short> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_srli_epi16 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<ushort> ShiftRightLogical(Vector128<ushort> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_srli_epi32 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<int> ShiftRightLogical(Vector128<int> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_srli_epi32 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<uint> ShiftRightLogical(Vector128<uint> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_srli_epi64 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<long> ShiftRightLogical(Vector128<long> value, byte count) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_srli_epi64 (__m128i a, int immediate)
+ /// </summary>
+ public static Vector128<ulong> ShiftRightLogical(Vector128<ulong> value, byte count) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_bsrli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<sbyte> ShiftRightLogical128BitLane(Vector128<sbyte> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bsrli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<byte> ShiftRightLogical128BitLane(Vector128<byte> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bsrli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<short> ShiftRightLogical128BitLane(Vector128<short> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bsrli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<ushort> ShiftRightLogical128BitLane(Vector128<ushort> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bsrli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<int> ShiftRightLogical128BitLane(Vector128<int> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bsrli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<uint> ShiftRightLogical128BitLane(Vector128<uint> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bsrli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<long> ShiftRightLogical128BitLane(Vector128<long> value, byte numBytes) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_bsrli_si128 (__m128i a, int imm8)
+ /// </summary>
+ public static Vector128<ulong> ShiftRightLogical128BitLane(Vector128<ulong> value, byte numBytes) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_sqrt_pd (__m128d a)
+ /// </summary>
+ public static Vector128<double> Sqrt(Vector128<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm_store_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAligned(sbyte* address, Vector128<sbyte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_store_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAligned(byte* address, Vector128<byte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_store_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAligned(short* address, Vector128<short> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_store_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAligned(ushort* address, Vector128<ushort> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_store_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAligned(int* address, Vector128<int> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_store_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAligned(uint* address, Vector128<uint> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_store_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAligned(long* address, Vector128<long> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_store_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAligned(ulong* address, Vector128<ulong> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_store_pd (double* mem_addr, __m128d a)
+ /// </summary>
+ public static unsafe void StoreAligned(double* address, Vector128<double> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm_stream_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(sbyte* address, Vector128<sbyte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_stream_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(byte* address, Vector128<byte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_stream_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(short* address, Vector128<short> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_stream_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(ushort* address, Vector128<ushort> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_stream_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(int* address, Vector128<int> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_stream_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(uint* address, Vector128<uint> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_stream_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(long* address, Vector128<long> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_stream_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(ulong* address, Vector128<ulong> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_stream_pd (double* mem_addr, __m128d a)
+ /// </summary>
+ public static unsafe void StoreAlignedNonTemporal(double* address, Vector128<double> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm_storeu_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void Store(sbyte* address, Vector128<sbyte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_storeu_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void Store(byte* address, Vector128<byte> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_storeu_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void Store(short* address, Vector128<short> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_storeu_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void Store(ushort* address, Vector128<ushort> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_storeu_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void Store(int* address, Vector128<int> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_storeu_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void Store(uint* address, Vector128<uint> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_storeu_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void Store(long* address, Vector128<long> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_storeu_si128 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void Store(ulong* address, Vector128<ulong> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_storeu_pd (double* mem_addr, __m128d a)
+ /// </summary>
+ public static unsafe void Store(double* address, Vector128<double> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm_storeh_pd (double* mem_addr, __m128d a)
+ /// </summary>
+ public static unsafe void StoreHigh(double* address, Vector128<double> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// void _mm_storel_epi64 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreLow(long* address, Vector128<long> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_storel_epi64 (__m128i* mem_addr, __m128i a)
+ /// </summary>
+ public static unsafe void StoreLow(ulong* address, Vector128<ulong> source) { throw new NotImplementedException(); }
+ /// <summary>
+ /// void _mm_storel_pd (double* mem_addr, __m128d a)
+ /// </summary>
+ public static unsafe void StoreLow(double* address, Vector128<double> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_sub_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> Subtract(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_sub_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> Subtract(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_sub_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> Subtract(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_sub_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> Subtract(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_sub_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> Subtract(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_sub_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<uint> Subtract(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_sub_epi64 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> Subtract(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_sub_epi64 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ulong> Subtract(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_sub_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> Subtract(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_subs_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> SubtractSaturate(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_subs_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> SubtractSaturate(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_subs_epu8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> SubtractSaturate(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_subs_epu16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> SubtractSaturate(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_unpackhi_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> UnpackHigh(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpackhi_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> UnpackHigh(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpackhi_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> UnpackHigh(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpackhi_epi16 (__m128i a, __m128i b)
+ /// </summary
+ public static Vector128<ushort> UnpackHigh(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpackhi_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> UnpackHigh(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpackhi_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<uint> UnpackHigh(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpackhi_epi64 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> UnpackHigh(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpackhi_epi64 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ulong> UnpackHigh(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_unpackhi_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> UnpackHigh(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_unpacklo_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> UnpackLow(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpacklo_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> UnpackLow(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpacklo_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> UnpackLow(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpacklo_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> UnpackLow(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpacklo_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> UnpackLow(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpacklo_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<uint> UnpackLow(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpacklo_epi64 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> UnpackLow(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_unpacklo_epi64 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ulong> UnpackLow(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_unpacklo_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> UnpackLow(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_xor_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<byte> Xor(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_xor_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> Xor(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_xor_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> Xor(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_xor_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> Xor(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_xor_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> Xor(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_xor_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<uint> Xor(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_xor_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> Xor(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_xor_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ulong> Xor(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_xor_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> Xor(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse3.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse3.cs
new file mode 100644
index 0000000000..5e4ac9dd0e
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse3.cs
@@ -0,0 +1,78 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel SSE3 hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Sse3
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// __m128 _mm_addsub_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> AddSubtract(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_addsub_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> AddSubtract(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_hadd_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> HorizontalAdd(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_hadd_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> HorizontalAdd(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_hsub_ps (__m128 a, __m128 b)
+ /// </summary>
+ public static Vector128<float> HorizontalSubtract(Vector128<float> left, Vector128<float> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_hsub_pd (__m128d a, __m128d b)
+ /// </summary>
+ public static Vector128<double> HorizontalSubtract(Vector128<double> left, Vector128<double> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_loaddup_pd (double const* mem_addr)
+ /// </summary>
+ public static unsafe Vector128<double> LoadAndDuplicate(double* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_lddqu_si128 (__m128i const* mem_addr)
+ /// </summary>
+ public static unsafe Vector128<sbyte> LoadDqu(sbyte* address) { throw new NotImplementedException(); }
+ public static unsafe Vector128<byte> LoadDqu(byte* address) { throw new NotImplementedException(); }
+ public static unsafe Vector128<short> LoadDqu(short* address) { throw new NotImplementedException(); }
+ public static unsafe Vector128<ushort> LoadDqu(ushort* address) { throw new NotImplementedException(); }
+ public static unsafe Vector128<int> LoadDqu(int* address) { throw new NotImplementedException(); }
+ public static unsafe Vector128<uint> LoadDqu(uint* address) { throw new NotImplementedException(); }
+ public static unsafe Vector128<long> LoadDqu(long* address) { throw new NotImplementedException(); }
+ public static unsafe Vector128<ulong> LoadDqu(ulong* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_movedup_pd (__m128d a)
+ /// </summary>
+ public static Vector128<double> MoveAndDuplicate(Vector128<double> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_movehdup_ps (__m128 a)
+ /// </summary>
+ public static Vector128<float> MoveHighAndDuplicate(Vector128<float> source) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_moveldup_ps (__m128 a)
+ /// </summary>
+ public static Vector128<float> MoveLowAndDuplicate(Vector128<float> source) { throw new NotImplementedException(); }
+
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse41.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse41.cs
new file mode 100644
index 0000000000..5a498bcf4e
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse41.cs
@@ -0,0 +1,408 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel SSE4.1 hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Sse41
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// __m128i _mm_blend_epi16 (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<short> Blend(Vector128<short> left, Vector128<short> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_blend_epi16 (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<ushort> Blend(Vector128<ushort> left, Vector128<ushort> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_blend_ps (__m128 a, __m128 b, const int imm8)
+ /// </summary>
+ public static Vector128<float> Blend(Vector128<float> left, Vector128<float> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_blend_pd (__m128d a, __m128d b, const int imm8)
+ /// </summary>
+ public static Vector128<double> Blend(Vector128<double> left, Vector128<double> right, byte control) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_blendv_epi8 (__m128i a, __m128i b, __m128i mask)
+ /// </summary>
+ public static Vector128<sbyte> BlendVariable(Vector128<sbyte> left, Vector128<sbyte> right, Vector128<sbyte> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_blendv_epi8 (__m128i a, __m128i b, __m128i mask)
+ /// </summary>
+ public static Vector128<byte> BlendVariable(Vector128<byte> left, Vector128<byte> right, Vector128<byte> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128 _mm_blendv_ps (__m128 a, __m128 b, __m128 mask)
+ /// </summary>
+ public static Vector128<float> BlendVariable(Vector128<float> left, Vector128<float> right, Vector128<float> mask) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_blendv_pd (__m128d a, __m128d b, __m128d mask)
+ /// </summary>
+ public static Vector128<double> BlendVariable(Vector128<double> left, Vector128<double> right, Vector128<double> mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_ceil_ps (__m128 a)
+ /// </summary>
+ public static Vector128<float> Ceiling(Vector128<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_ceil_pd (__m128d a)
+ /// </summary>
+ public static Vector128<double> Ceiling(Vector128<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpeq_epi64 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> CompareEqual(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cmpeq_epi64 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ulong> CompareEqual(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cvtepi8_epi16 (__m128i a)
+ /// </summary>
+ public static Vector128<short> ConvertToShort(Vector128<sbyte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtepu8_epi16 (__m128i a)
+ /// </summary>
+ public static Vector128<short> ConvertToShort(Vector128<byte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtepi8_epi32 (__m128i a)
+ /// </summary>
+ public static Vector128<int> ConvertToInt(Vector128<sbyte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtepu8_epi32 (__m128i a)
+ /// </summary>
+ public static Vector128<int> ConvertToInt(Vector128<byte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtepi16_epi32 (__m128i a)
+ /// </summary>
+ public static Vector128<int> ConvertToInt(Vector128<short> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtepu16_epi32 (__m128i a)
+ /// </summary>
+ public static Vector128<int> ConvertToInt(Vector128<ushort> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtepi8_epi64 (__m128i a)
+ /// </summary>
+ public static Vector128<long> ConvertToLong(Vector128<sbyte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtepu8_epi64 (__m128i a)
+ /// </summary>
+ public static Vector128<long> ConvertToLong(Vector128<byte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtepi16_epi64 (__m128i a)
+ /// </summary>
+ public static Vector128<long> ConvertToLong(Vector128<short> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtepu16_epi64 (__m128i a)
+ /// </summary>
+ public static Vector128<long> ConvertToLong(Vector128<ushort> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtepi32_epi64 (__m128i a)
+ /// </summary>
+ public static Vector128<long> ConvertToLong(Vector128<int> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_cvtepu32_epi64 (__m128i a)
+ /// </summary>
+ public static Vector128<long> ConvertToLong(Vector128<uint> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_extract_epi8 (__m128i a, const int imm8)
+ /// </summary>
+ public static sbyte ExtractSbyte<T>(Vector128<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+ /// <summary>
+ /// int _mm_extract_epi8 (__m128i a, const int imm8)
+ /// </summary>
+ public static byte ExtractByte<T>(Vector128<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_extract_epi32 (__m128i a, const int imm8)
+ /// </summary>
+ public static int ExtractInt<T>(Vector128<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_extract_epi32 (__m128i a, const int imm8)
+ /// </summary>
+ public static uint ExtractUint<T>(Vector128<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __int64 _mm_extract_epi64 (__m128i a, const int imm8)
+ /// </summary>
+ public static long ExtractLong<T>(Vector128<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __int64 _mm_extract_epi64 (__m128i a, const int imm8)
+ /// </summary>
+ public static ulong ExtractUlong<T>(Vector128<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_extract_ps (__m128 a, const int imm8)
+ /// </summary>
+ public static float ExtractFloat<T>(Vector128<T> value, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_floor_ps (__m128 a)
+ /// </summary>
+ public static Vector128<float> Floor(Vector128<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128d _mm_floor_pd (__m128d a)
+ /// </summary>
+ public static Vector128<double> Floor(Vector128<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_insert_epi8 (__m128i a, int i, const int imm8)
+ /// </summary>
+ public static Vector128<T> InsertSbyte<T>(Vector128<T> value, sbyte data, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_insert_epi8 (__m128i a, int i, const int imm8)
+ /// </summary>
+ public static Vector128<T> InsertByte<T>(Vector128<T> value, byte data, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_insert_epi32 (__m128i a, int i, const int imm8)
+ /// </summary>
+ public static Vector128<T> InsertInt<T>(Vector128<T> value, int data, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_insert_epi32 (__m128i a, int i, const int imm8)
+ /// </summary>
+ public static Vector128<T> InsertUint<T>(Vector128<T> value, uint data, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_insert_epi64 (__m128i a, __int64 i, const int imm8)
+ /// </summary>
+ public static Vector128<T> InsertLong<T>(Vector128<T> value, long data, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_insert_epi64 (__m128i a, __int64 i, const int imm8)
+ /// </summary>
+ public static Vector128<T> InsertUlong<T>(Vector128<T> value, ulong data, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_insert_ps (__m128 a, __m128 b, const int imm8)
+ /// </summary>
+ public static Vector128<T> InsertFloat<T>(Vector128<T> value, float data, byte index) where T : struct { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_max_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> Max(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_max_epu16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> Max(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_max_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> Max(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_max_epu32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<uint> Max(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_min_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> Min(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_min_epu16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> Min(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_min_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> Min(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_min_epu32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<uint> Min(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_minpos_epu16 (__m128i a)
+ /// </summary>
+ public static Vector128<ushort> MinHorizontal(Vector128<ushort> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_mpsadbw_epu8 (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<ushort> MultipleSumAbsoluteDifferences(Vector128<byte> left, Vector128<byte> right, byte mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_mul_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> Multiply(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_mullo_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> MultiplyLow(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_packus_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<ushort> PackUnsignedSaturate(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128 _mm_round_ps (__m128 a, int rounding)
+ /// _MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector128<float> RoundToNearestInteger(Vector128<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_NEG_INF |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector128<float> RoundToNegativeInfinity(Vector128<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_POS_INF |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector128<float> RoundToPositiveInfinity(Vector128<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_ZERO |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector128<float> RoundToZero(Vector128<float> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_CUR_DIRECTION
+ /// </summary>
+ public static Vector128<float> RoundCurrentDirection(Vector128<float> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128d _mm_round_pd (__m128d a, int rounding)
+ /// _MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector128<double> RoundToNearestInteger(Vector128<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_NEG_INF |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector128<double> RoundToNegativeInfinity(Vector128<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_POS_INF |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector128<double> RoundToPositiveInfinity(Vector128<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_TO_ZERO |_MM_FROUND_NO_EXC
+ /// </summary>
+ public static Vector128<double> RoundToZero(Vector128<double> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// _MM_FROUND_CUR_DIRECTION
+ /// </summary>
+ public static Vector128<double> RoundCurrentDirection(Vector128<double> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_stream_load_si128 (const __m128i* mem_addr)
+ /// </summary>
+ public static unsafe Vector128<sbyte> LoadAlignedNonTemporal(sbyte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_stream_load_si128 (const __m128i* mem_addr)
+ /// </summary>
+ public static unsafe Vector128<byte> LoadAlignedNonTemporal(byte* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_stream_load_si128 (const __m128i* mem_addr)
+ /// </summary>
+ public static unsafe Vector128<short> LoadAlignedNonTemporal(short* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_stream_load_si128 (const __m128i* mem_addr)
+ /// </summary>
+ public static unsafe Vector128<ushort> LoadAlignedNonTemporal(ushort* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_stream_load_si128 (const __m128i* mem_addr)
+ /// </summary>
+ public static unsafe Vector128<int> LoadAlignedNonTemporal(int* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_stream_load_si128 (const __m128i* mem_addr)
+ /// </summary>
+ public static unsafe Vector128<uint> LoadAlignedNonTemporal(uint* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_stream_load_si128 (const __m128i* mem_addr)
+ /// </summary>
+ public static unsafe Vector128<long> LoadAlignedNonTemporal(long* address) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_stream_load_si128 (const __m128i* mem_addr)
+ /// </summary>
+ public static unsafe Vector128<ulong> LoadAlignedNonTemporal(ulong* address) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_test_all_ones (__m128i a)
+ /// </summary>
+ public static bool TestAllOnes(Vector128<sbyte> value) { throw new NotImplementedException(); }
+ public static bool TestAllOnes(Vector128<byte> value) { throw new NotImplementedException(); }
+ public static bool TestAllOnes(Vector128<short> value) { throw new NotImplementedException(); }
+ public static bool TestAllOnes(Vector128<ushort> value) { throw new NotImplementedException(); }
+ public static bool TestAllOnes(Vector128<int> value) { throw new NotImplementedException(); }
+ public static bool TestAllOnes(Vector128<uint> value) { throw new NotImplementedException(); }
+ public static bool TestAllOnes(Vector128<long> value) { throw new NotImplementedException(); }
+ public static bool TestAllOnes(Vector128<ulong> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_test_all_zeros (__m128i a, __m128i mask)
+ /// </summary>
+ public static bool TestAllZeros(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ public static bool TestAllZeros(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ public static bool TestAllZeros(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ public static bool TestAllZeros(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ public static bool TestAllZeros(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ public static bool TestAllZeros(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ public static bool TestAllZeros(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ public static bool TestAllZeros(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_testc_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static bool TestC(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ public static bool TestC(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ public static bool TestC(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ public static bool TestC(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ public static bool TestC(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ public static bool TestC(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ public static bool TestC(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ public static bool TestC(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_test_mix_ones_zeros (__m128i a, __m128i mask)
+ /// </summary>
+ public static bool TestMixOnesZeros(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ public static bool TestMixOnesZeros(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ public static bool TestMixOnesZeros(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ public static bool TestMixOnesZeros(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ public static bool TestMixOnesZeros(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ public static bool TestMixOnesZeros(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ public static bool TestMixOnesZeros(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ public static bool TestMixOnesZeros(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_testnzc_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static bool TestNotZAndNotC(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ public static bool TestNotZAndNotC(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ public static bool TestNotZAndNotC(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ public static bool TestNotZAndNotC(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ public static bool TestNotZAndNotC(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ public static bool TestNotZAndNotC(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ public static bool TestNotZAndNotC(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ public static bool TestNotZAndNotC(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_testz_si128 (__m128i a, __m128i b)
+ /// </summary>
+ public static bool TestZ(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ public static bool TestZ(Vector128<byte> left, Vector128<byte> right) { throw new NotImplementedException(); }
+ public static bool TestZ(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ public static bool TestZ(Vector128<ushort> left, Vector128<ushort> right) { throw new NotImplementedException(); }
+ public static bool TestZ(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ public static bool TestZ(Vector128<uint> left, Vector128<uint> right) { throw new NotImplementedException(); }
+ public static bool TestZ(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+ public static bool TestZ(Vector128<ulong> left, Vector128<ulong> right) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse42.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse42.cs
new file mode 100644
index 0000000000..f644577ae1
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Sse42.cs
@@ -0,0 +1,233 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel SSE4.2 hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Sse42
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// int _mm_cmpistra (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrc (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistro (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrs (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrz (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static bool CompareImplicitLength(Vector128<sbyte> left, Vector128<sbyte> right, ResultsFlag flag, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpistra (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrc (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistro (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrs (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrz (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static bool CompareImplicitLength(Vector128<byte> left, Vector128<byte> right, ResultsFlag flag, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpistra (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrc (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistro (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrs (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrz (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static bool CompareImplicitLength(Vector128<short> left, Vector128<short> right, ResultsFlag flag, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+
+ /// <summary>
+ /// int _mm_cmpistra (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrc (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistro (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrs (__m128i a, __m128i b, const int imm8)
+ /// int _mm_cmpistrz (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static bool CompareImplicitLength(Vector128<ushort> left, Vector128<ushort> right, ResultsFlag flag, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpestra (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrc (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestro (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrs (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrz (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static bool CompareExplicitLength(Vector128<sbyte> left, byte leftLength, Vector128<sbyte> right, byte rightLength, ResultsFlag flag, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpestra (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrc (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestro (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrs (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrz (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static bool CompareExplicitLength(Vector128<byte> left, byte leftLength, Vector128<byte> right, byte rightLength, ResultsFlag flag, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpestra (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrc (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestro (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrs (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrz (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static bool CompareExplicitLength(Vector128<short> left, byte leftLength, Vector128<short> right, byte rightLength, ResultsFlag flag, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpestra (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrc (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestro (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrs (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// int _mm_cmpestrz (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static bool CompareExplicitLength(Vector128<ushort> left, byte leftLength, Vector128<ushort> right, byte rightLength, ResultsFlag flag, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpistri (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static int CompareImplicitLengthIndex(Vector128<sbyte> left, Vector128<sbyte> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpistri (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static int CompareImplicitLengthIndex(Vector128<byte> left, Vector128<byte> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpistri (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static int CompareImplicitLengthIndex(Vector128<short> left, Vector128<short> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpistri (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static int CompareImplicitLengthIndex(Vector128<ushort> left, Vector128<ushort> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpestri (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static int CompareExplicitLengthIndex(Vector128<sbyte> left, byte leftLength, Vector128<sbyte> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpestri (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static int CompareExplicitLengthIndex(Vector128<byte> left, byte leftLength, Vector128<byte> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpestri (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static int CompareExplicitLengthIndex(Vector128<short> left, byte leftLength, Vector128<short> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// int _mm_cmpestri (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static int CompareExplicitLengthIndex(Vector128<ushort> left, byte leftLength, Vector128<ushort> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpistrm (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<ushort> CompareImplicitLengthBitMask(Vector128<sbyte> left, Vector128<sbyte> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpistrm (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<ushort> CompareImplicitLengthBitMask(Vector128<byte> left, Vector128<byte> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpistrm (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<byte> CompareImplicitLengthBitMask(Vector128<short> left, Vector128<short> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpistrm (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<byte> CompareImplicitLengthBitMask(Vector128<ushort> left, Vector128<ushort> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpistrm (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<byte> CompareImplicitLengthUnitMask(Vector128<sbyte> left, Vector128<sbyte> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpistrm (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<byte> CompareImplicitLengthUnitMask(Vector128<byte> left, Vector128<byte> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpistrm (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<ushort> CompareImplicitLengthUnitMask(Vector128<short> left, Vector128<short> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpistrm (__m128i a, __m128i b, const int imm8)
+ /// </summary>
+ public static Vector128<ushort> CompareImplicitLengthUnitMask(Vector128<ushort> left, Vector128<ushort> right, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpestrm (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static Vector128<ushort> CompareExplicitLengthBitMask(Vector128<sbyte> left, byte leftLength, Vector128<sbyte> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpestrm (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static Vector128<ushort> CompareExplicitLengthBitMask(Vector128<byte> left, byte leftLength, Vector128<byte> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpestrm (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static Vector128<byte> CompareExplicitLengthBitMask(Vector128<short> left, byte leftLength, Vector128<short> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpestrm (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static Vector128<byte> CompareExplicitLengthBitMask(Vector128<ushort> left, byte leftLength, Vector128<ushort> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpestrm (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static Vector128<byte> CompareExplicitLengthUnitMask(Vector128<sbyte> left, byte leftLength, Vector128<sbyte> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpestrm (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static Vector128<byte> CompareExplicitLengthUnitMask(Vector128<byte> left, byte leftLength, Vector128<byte> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpestrm (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static Vector128<ushort> CompareExplicitLengthUnitMask(Vector128<short> left, byte leftLength, Vector128<short> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpestrm (__m128i a, int la, __m128i b, int lb, const int imm8)
+ /// </summary>
+ public static Vector128<ushort> CompareExplicitLengthUnitMask(Vector128<ushort> left, byte leftLength, Vector128<ushort> right, byte rightLength, StringComparisonMode mode) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_cmpgt_epi64 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<long> CompareGreaterThan(Vector128<long> left, Vector128<long> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// unsigned int _mm_crc32_u8 (unsigned int crc, unsigned char v)
+ /// </summary>
+ public static uint Crc32(uint crc, byte data) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned int _mm_crc32_u16 (unsigned int crc, unsigned short v)
+ /// </summary>
+ public static uint Crc32(uint crc, ushort data) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned int _mm_crc32_u32 (unsigned int crc, unsigned int v)
+ /// </summary>
+ public static uint Crc32(uint crc, uint data) { throw new NotImplementedException(); }
+ /// <summary>
+ /// unsigned __int64 _mm_crc32_u64 (unsigned __int64 crc, unsigned __int64 v)
+ /// </summary>
+ public static ulong Crc32(ulong crc, ulong data) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/X86/Ssse3.cs b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Ssse3.cs
new file mode 100644
index 0000000000..0ce9036a8f
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/Intrinsics/X86/Ssse3.cs
@@ -0,0 +1,92 @@
+// 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.Intrinsics;
+
+namespace System.Runtime.Intrinsics.X86
+{
+ /// <summary>
+ /// This class provides access to Intel SSSE3 hardware instructions via intrinsics
+ /// </summary>
+ [CLSCompliant(false)]
+ public static class Ssse3
+ {
+ public static bool IsSupported { get { return false; } }
+
+ /// <summary>
+ /// __m128i _mm_abs_epi8 (__m128i a)
+ /// </summary>
+ public static Vector128<byte> Abs(Vector128<sbyte> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_abs_epi16 (__m128i a)
+ /// </summary>
+ public static Vector128<ushort> Abs(Vector128<short> value) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_abs_epi32 (__m128i a)
+ /// </summary>
+ public static Vector128<uint> Abs(Vector128<int> value) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_alignr_epi8 (__m128i a, __m128i b, int count)
+ /// </summary>
+ public static Vector128<sbyte> AlignRight(Vector128<sbyte> left, Vector128<sbyte> right, byte mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_hadd_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> HorizontalAdd(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_hadd_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> HorizontalAdd(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_hadds_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> HorizontalAddSaturate(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_hsub_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> HorizontalSubtract(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_hsub_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> HorizontalSubtract(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_hsubs_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> HorizontalSubtractSaturate(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_maddubs_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> MultiplyAddAdjacent(Vector128<byte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_mulhrs_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> MultiplyHighRoundScale(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_shuffle_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> Shuffle(Vector128<sbyte> value, Vector128<sbyte> mask) { throw new NotImplementedException(); }
+
+ /// <summary>
+ /// __m128i _mm_sign_epi8 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<sbyte> Sign(Vector128<sbyte> left, Vector128<sbyte> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_sign_epi16 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<short> Sign(Vector128<short> left, Vector128<short> right) { throw new NotImplementedException(); }
+ /// <summary>
+ /// __m128i _mm_sign_epi32 (__m128i a, __m128i b)
+ /// </summary>
+ public static Vector128<int> Sign(Vector128<int> left, Vector128<int> right) { throw new NotImplementedException(); }
+ }
+}
diff --git a/src/mscorlib/src/System/Runtime/MemoryFailPoint.cs b/src/mscorlib/src/System/Runtime/MemoryFailPoint.cs
index f9f87bc928..4b96836daf 100644
--- a/src/mscorlib/src/System/Runtime/MemoryFailPoint.cs
+++ b/src/mscorlib/src/System/Runtime/MemoryFailPoint.cs
@@ -241,24 +241,20 @@ namespace System.Runtime
// Attempt to grow the OS's page file. Note that we ignore
// any allocation routines from the host intentionally.
RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- }
- finally
+
+ // This shouldn't overflow due to the if clauses above.
+ UIntPtr numBytes = new UIntPtr(segmentSize);
+ unsafe
{
- // This shouldn't overflow due to the if clauses above.
- UIntPtr numBytes = new UIntPtr(segmentSize);
- unsafe
+ void* pMemory = Win32Native.VirtualAlloc(null, numBytes, Win32Native.MEM_COMMIT, Win32Native.PAGE_READWRITE);
+ if (pMemory != null)
{
- void* pMemory = Win32Native.VirtualAlloc(null, numBytes, Win32Native.MEM_COMMIT, Win32Native.PAGE_READWRITE);
- if (pMemory != null)
- {
- bool r = Win32Native.VirtualFree(pMemory, UIntPtr.Zero, Win32Native.MEM_RELEASE);
- if (!r)
- __Error.WinIOError();
- }
+ bool r = Win32Native.VirtualFree(pMemory, UIntPtr.Zero, Win32Native.MEM_RELEASE);
+ if (!r)
+ throw Win32Marshal.GetExceptionForLastWin32Error();
}
}
+
continue;
case 2:
@@ -304,14 +300,9 @@ namespace System.Runtime
CheckForFreeAddressSpace(segmentSize, true);
RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- }
- finally
- {
- SharedStatics.AddMemoryFailPointReservation((long)size);
- _mustSubtractReservation = true;
- }
+
+ SharedStatics.AddMemoryFailPointReservation((long)size);
+ _mustSubtractReservation = true;
#endif
}
@@ -321,7 +312,7 @@ namespace System.Runtime
Win32Native.MEMORYSTATUSEX memory = new Win32Native.MEMORYSTATUSEX();
r = Win32Native.GlobalMemoryStatusEx(ref memory);
if (!r)
- __Error.WinIOError();
+ throw Win32Marshal.GetExceptionForLastWin32Error();
availPageFile = memory.availPageFile;
totalAddressSpaceFree = memory.availVirtual;
//Console.WriteLine("Memory gate: Mem load: {0}% Available memory (physical + page file): {1} MB Total free address space: {2} MB GC Heap: {3} MB", memory.memoryLoad, memory.availPageFile >> 20, memory.availVirtual >> 20, GC.GetTotalMemory(true) >> 20);
@@ -370,7 +361,7 @@ namespace System.Runtime
{
UIntPtr r = Win32Native.VirtualQuery(address, ref memInfo, sizeOfMemInfo);
if (r == UIntPtr.Zero)
- __Error.WinIOError();
+ throw Win32Marshal.GetExceptionForLastWin32Error();
ulong regionSize = memInfo.RegionSize.ToUInt64();
if (memInfo.State == Win32Native.MEM_FREE)
@@ -414,14 +405,9 @@ namespace System.Runtime
if (_mustSubtractReservation)
{
RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- }
- finally
- {
- SharedStatics.AddMemoryFailPointReservation(-((long)_reservedMemory));
- _mustSubtractReservation = false;
- }
+
+ SharedStatics.AddMemoryFailPointReservation(-((long)_reservedMemory));
+ _mustSubtractReservation = false;
}
/*
diff --git a/src/mscorlib/src/System/Runtime/Reliability/CriticalFinalizerObject.cs b/src/mscorlib/src/System/Runtime/Reliability/CriticalFinalizerObject.cs
index 6d6d6f3e77..cbb9562148 100644
--- a/src/mscorlib/src/System/Runtime/Reliability/CriticalFinalizerObject.cs
+++ b/src/mscorlib/src/System/Runtime/Reliability/CriticalFinalizerObject.cs
@@ -11,7 +11,6 @@
** (i.e. the finalizer is guaranteed to run, won't be aborted by the host and is
** run after the finalizers of other objects collected at the same time).
**
-** You must possess UnmanagedCode permission in order to derive from this class.
**
**
===========================================================*/
diff --git a/src/mscorlib/src/System/Runtime/Reliability/PrePrepareMethodAttribute.cs b/src/mscorlib/src/System/Runtime/Reliability/PrePrepareMethodAttribute.cs
deleted file mode 100644
index 6594562b97..0000000000
--- a/src/mscorlib/src/System/Runtime/Reliability/PrePrepareMethodAttribute.cs
+++ /dev/null
@@ -1,33 +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.
-
-//
-/*============================================================
-**
-**
-** Purpose: Serves as a hint to ngen that the decorated method
-** (and its statically determinable call graph) should be
-** prepared (as for Constrained Execution Region use). This
-** is primarily useful in the scenario where the method in
-** question will be prepared explicitly at runtime and the
-** author of the method knows this and wishes to avoid the
-** overhead of runtime preparation.
-**
-**
-===========================================================*/
-
-using System;
-using System.Runtime.InteropServices;
-
-namespace System.Runtime.ConstrainedExecution
-{
- [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)]
-
- internal sealed class PrePrepareMethodAttribute : Attribute
- {
- public PrePrepareMethodAttribute()
- {
- }
- }
-}
diff --git a/src/mscorlib/src/System/RuntimeHandles.cs b/src/mscorlib/src/System/RuntimeHandles.cs
index fca37dd868..20783ef467 100644
--- a/src/mscorlib/src/System/RuntimeHandles.cs
+++ b/src/mscorlib/src/System/RuntimeHandles.cs
@@ -220,7 +220,7 @@ namespace System
}
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern Object CreateInstance(RuntimeType type, bool publicOnly, ref bool canBeCached, ref RuntimeMethodHandleInternal ctor);
+ internal static extern Object CreateInstance(RuntimeType type, bool publicOnly, bool wrapExceptions, ref bool canBeCached, ref RuntimeMethodHandleInternal ctor);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern Object CreateCaInstance(RuntimeType type, IRuntimeMethodInfo ctor);
@@ -377,6 +377,9 @@ namespace System
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal extern static bool IsInterface(RuntimeType type);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal extern static bool IsByRefLike(RuntimeType type);
+
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
@@ -909,7 +912,7 @@ namespace System
[DebuggerStepThroughAttribute]
[Diagnostics.DebuggerHidden]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal extern static object InvokeMethod(object target, object[] arguments, Signature sig, bool constructor);
+ internal extern static object InvokeMethod(object target, object[] arguments, Signature sig, bool constructor, bool wrapExceptions);
#region Private Invocation Helpers
internal static INVOCATION_FLAGS GetSecurityFlags(IRuntimeMethodInfo handle)
@@ -975,7 +978,6 @@ namespace System
return fRet;
}
-
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal extern static bool IsTypicalMethodDefinition(IRuntimeMethodInfo method);
@@ -991,6 +993,11 @@ namespace System
return method;
}
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern static int GetGenericParameterCount(RuntimeMethodHandleInternal method);
+
+ internal static int GetGenericParameterCount(IRuntimeMethodInfo method) => GetGenericParameterCount(method.Value);
+
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
private extern static void StripMethodInstantiation(IRuntimeMethodInfo method, ObjectHandleOnStack outMethod);
diff --git a/src/mscorlib/src/System/SerializableAttribute.cs b/src/mscorlib/src/System/SerializableAttribute.cs
deleted file mode 100644
index 266e73d78d..0000000000
--- a/src/mscorlib/src/System/SerializableAttribute.cs
+++ /dev/null
@@ -1,35 +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.
-
-/*============================================================
-**
-**
-**
-** Purpose: Used to mark a class as being serializable
-**
-**
-============================================================*/
-
-using System;
-using System.Reflection;
-
-namespace System
-{
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate, Inherited = false)]
- public sealed class SerializableAttribute : Attribute
- {
- internal static Attribute GetCustomAttribute(RuntimeType type)
- {
- return (type.Attributes & TypeAttributes.Serializable) == TypeAttributes.Serializable ? new SerializableAttribute() : null;
- }
- internal static bool IsDefined(RuntimeType type)
- {
- return type.IsSerializable;
- }
-
- public SerializableAttribute()
- {
- }
- }
-}
diff --git a/src/mscorlib/src/System/String.Comparison.cs b/src/mscorlib/src/System/String.Comparison.cs
index 7316322bed..afdcddfe2f 100644
--- a/src/mscorlib/src/System/String.Comparison.cs
+++ b/src/mscorlib/src/System/String.Comparison.cs
@@ -44,7 +44,7 @@ namespace System
//Return the (case-insensitive) difference between them.
if (charA != charB)
- goto ReturnCharAMinusCharB; // TODO: Workaround for https://github.com/dotnet/coreclr/issues/9692
+ return charA - charB;
// Next char
a++; b++;
@@ -52,9 +52,6 @@ namespace System
}
return strA.Length - strB.Length;
-
- ReturnCharAMinusCharB:
- return charA - charB;
}
}
@@ -168,14 +165,11 @@ namespace System
}
else
{
- goto ReturnFalse;
+ return false;
}
}
return true;
-
- ReturnFalse:
- return false;
}
}
@@ -745,7 +739,7 @@ namespace System
return string.Compare(this, strB, StringComparison.CurrentCulture);
}
- // Determines whether a specified string is a suffix of the the current instance.
+ // Determines whether a specified string is a suffix of the current instance.
//
// The case-sensitive and culture-sensitive option is set by options,
// and the default culture is used.
@@ -1008,34 +1002,14 @@ namespace System
return !String.Equals(a, b);
}
-#if FEATURE_RANDOMIZED_STRING_HASHING
- // Do not remove!
- // This method is called by reflection in System.Xml
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- internal static extern int InternalMarvin32HashString(string s, int strLen, long additionalEntropy);
-
- internal static bool UseRandomizedHashing()
- {
- return InternalUseRandomizedHashing();
- }
-
- [System.Security.SuppressUnmanagedCodeSecurity]
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- private static extern bool InternalUseRandomizedHashing();
-#endif
+ private static extern int InternalMarvin32HashString(string s);
// Gets a hash code for this string. If strings A and B are such that A.Equals(B), then
// they will return the same hash code.
public override int GetHashCode()
{
-#if FEATURE_RANDOMIZED_STRING_HASHING
- if (HashHelpers.s_UseRandomizedStringHashing)
- {
- return InternalMarvin32HashString(this, this.Length, 0);
- }
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
-
- return GetLegacyNonRandomizedHashCode();
+ return InternalMarvin32HashString(this);
}
// Gets a hash code for this string and this comparison. If strings A and B and comparition C are such
diff --git a/src/mscorlib/src/System/String.Searching.cs b/src/mscorlib/src/System/String.Searching.cs
index 22e2a808b1..02e800427b 100644
--- a/src/mscorlib/src/System/String.Searching.cs
+++ b/src/mscorlib/src/System/String.Searching.cs
@@ -96,8 +96,97 @@ namespace System
}
[Pure]
+ public int IndexOfAny(char[] anyOf, int startIndex, int count)
+ {
+ if (anyOf == null)
+ {
+ throw new ArgumentNullException(nameof(anyOf));
+ }
+
+ if ((uint)startIndex > (uint)Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
+ }
+
+ if ((uint)count > (uint)(Length - startIndex))
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
+ }
+
+ if (anyOf.Length == 2)
+ {
+ // Very common optimization for directory separators (/, \), quotes (", '), brackets, etc
+ return IndexOfAny(anyOf[0], anyOf[1], startIndex, count);
+ }
+ else if (anyOf.Length == 3)
+ {
+ return IndexOfAny(anyOf[0], anyOf[1], anyOf[2], startIndex, count);
+ }
+ else if (anyOf.Length > 3)
+ {
+ return IndexOfCharArray(anyOf, startIndex, count);
+ }
+ else if (anyOf.Length == 1)
+ {
+ return IndexOf(anyOf[0], startIndex, count);
+ }
+ else // anyOf.Length == 0
+ {
+ return -1;
+ }
+ }
+
+ private unsafe int IndexOfAny(char value1, char value2, int startIndex, int count)
+ {
+ fixed (char* pChars = &_firstChar)
+ {
+ char* pCh = pChars + startIndex;
+
+ while (count > 0)
+ {
+ char c = *pCh;
+
+ if (c == value1 || c == value2)
+ return (int)(pCh - pChars);
+
+ // Possibly reads outside of count and can include null terminator
+ // Handled in the return logic
+ c = *(pCh + 1);
+
+ if (c == value1 || c == value2)
+ return (count == 1 ? -1 : (int)(pCh - pChars) + 1);
+
+ pCh += 2;
+ count -= 2;
+ }
+
+ return -1;
+ }
+ }
+
+ private unsafe int IndexOfAny(char value1, char value2, char value3, int startIndex, int count)
+ {
+ fixed (char* pChars = &_firstChar)
+ {
+ char* pCh = pChars + startIndex;
+
+ while (count > 0)
+ {
+ char c = *pCh;
+
+ if (c == value1 || c == value2 || c == value3)
+ return (int)(pCh - pChars);
+
+ pCh++;
+ count--;
+ }
+
+ return -1;
+ }
+ }
+
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- public extern int IndexOfAny(char[] anyOf, int startIndex, int count);
+ private extern int IndexOfCharArray(char[] anyOf, int startIndex, int count);
// Determines the position within this string of the first occurrence of the specified
diff --git a/src/mscorlib/src/System/String.cs b/src/mscorlib/src/System/String.cs
index a34ea21423..c74abe9903 100644
--- a/src/mscorlib/src/System/String.cs
+++ b/src/mscorlib/src/System/String.cs
@@ -16,6 +16,7 @@ namespace System
{
using System.Text;
using System;
+ using System.Buffers;
using System.Runtime;
using System.Runtime.ConstrainedExecution;
using System.Globalization;
@@ -666,6 +667,45 @@ namespace System
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern String(char c, int count);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ public extern String(ReadOnlySpan<char> value);
+
+ private unsafe string CtorReadOnlySpanOfChar(ReadOnlySpan<char> value)
+ {
+ if (value.Length == 0)
+ {
+ return Empty;
+ }
+
+ string result = FastAllocateString(value.Length);
+ fixed (char* dest = &result._firstChar, src = &value.DangerousGetPinnableReference())
+ {
+ wstrcpy(dest, src, value.Length);
+ }
+ return result;
+ }
+
+ public static string Create<TState>(int length, TState state, SpanAction<char, TState> action)
+ {
+ if (action == null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
+
+ if (length > 0)
+ {
+ string result = FastAllocateString(length);
+ action(new Span<char>(ref result.GetRawStringData(), length), state);
+ return result;
+ }
+
+ if (length == 0)
+ {
+ return Empty;
+ }
+
+ throw new ArgumentOutOfRangeException(nameof(length));
+ }
// Returns this string.
public override String ToString()
diff --git a/src/mscorlib/src/System/Threading/CancellationToken.cs b/src/mscorlib/src/System/Threading/CancellationToken.cs
index 17847f7eb9..4d86881267 100644
--- a/src/mscorlib/src/System/Threading/CancellationToken.cs
+++ b/src/mscorlib/src/System/Threading/CancellationToken.cs
@@ -52,7 +52,7 @@ namespace System.Threading
/// <remarks>
/// <para>
/// This property indicates whether cancellation has been requested for this token,
- /// either through the token initially being construted in a canceled state, or through
+ /// either through the token initially being constructed in a canceled state, or through
/// calling <see cref="System.Threading.CancellationTokenSource.Cancel()">Cancel</see>
/// on the token's associated <see cref="CancellationTokenSource"/>.
/// </para>
diff --git a/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs b/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs
index c09f175a36..e0a52581f6 100644
--- a/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs
+++ b/src/mscorlib/src/System/Threading/CancellationTokenRegistration.cs
@@ -36,6 +36,23 @@ namespace System.Threading
}
}
+ /// <summary>
+ /// Gets the <see cref="CancellationToken"/> with which this registration is associated. If the
+ /// registration isn't associated with a token (such as after the registration has been disposed),
+ /// this will return a default token.
+ /// </summary>
+ internal CancellationToken Token => _node?.Partition.Source.Token ?? default(CancellationToken);
+
+ /// <summary>
+ /// Disposes of the registration and unregisters the target callback from the associated
+ /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see>.
+ /// </summary>
+ internal bool TryDeregister() // corefx currently has an InternalsVisibleTo dependency on this
+ {
+ CancellationTokenSource.CallbackNode node = _node;
+ return node != null && node.Partition.Unregister(_id, node);
+ }
+
private void WaitForCallbackIfNecessary()
{
// We're a valid registration but we were unable to unregister, which means the callback wasn't in the list,
diff --git a/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.Windows.cs b/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.Windows.cs
index 21c1c5f4a4..a40661c1b6 100644
--- a/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.Windows.cs
+++ b/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.Windows.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;
using System.Diagnostics;
using System.Runtime.InteropServices;
@@ -27,10 +28,10 @@ namespace System.Threading
// We do not let either of these leak and convert them to ArgumentException to
// indicate that the specified handles are invalid.
- if (ex.HResult == System.HResults.E_HANDLE) // Bad handle
+ if (ex.HResult == HResults.E_HANDLE) // Bad handle
throw new ArgumentException(SR.Argument_InvalidHandle, nameof(handle));
- if (ex.HResult == System.HResults.E_INVALIDARG) // Handle already bound or sync handle
+ if (ex.HResult == HResults.E_INVALIDARG) // Handle already bound or sync handle
throw new ArgumentException(SR.Argument_AlreadyBoundOrSyncHandle, nameof(handle));
throw;
diff --git a/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.cs b/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.cs
index a4a3b980cf..5e0f2affc4 100644
--- a/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.cs
+++ b/src/mscorlib/src/System/Threading/ClrThreadPoolBoundHandle.cs
@@ -243,7 +243,7 @@ namespace System.Threading
/// </summary>
/// <param name="overlapped">
/// An unmanaged pointer to the <see cref="NativeOverlapped"/> structure from which to return the
- /// asscociated user-provided object.
+ /// associated user-provided object.
/// </param>
/// <returns>
/// A user-provided object that distinguishes this <see cref="NativeOverlapped"/>
diff --git a/src/mscorlib/src/System/Threading/EventWaitHandle.cs b/src/mscorlib/src/System/Threading/EventWaitHandle.cs
index e910984439..cfb9c28415 100644
--- a/src/mscorlib/src/System/Threading/EventWaitHandle.cs
+++ b/src/mscorlib/src/System/Threading/EventWaitHandle.cs
@@ -83,7 +83,7 @@ namespace System.Threading
if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name));
- __Error.WinIOError(errorCode, name);
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, name);
}
SetHandleInternal(_handle);
}
@@ -132,7 +132,7 @@ namespace System.Threading
if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name));
- __Error.WinIOError(errorCode, name);
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, name);
}
createdNew = errorCode != Win32Native.ERROR_ALREADY_EXISTS;
SetHandleInternal(_handle);
@@ -160,8 +160,7 @@ namespace System.Threading
throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name));
case OpenExistingResult.PathNotFound:
- __Error.WinIOError(Win32Native.ERROR_PATH_NOT_FOUND, "");
- return result; //never executes
+ throw Win32Marshal.GetExceptionForWin32Error(Win32Native.ERROR_PATH_NOT_FOUND, "");
default:
return result;
@@ -210,7 +209,7 @@ namespace System.Threading
if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
return OpenExistingResult.NameInvalid;
//this is for passed through Win32Native Errors
- __Error.WinIOError(errorCode, "");
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, "");
}
result = new EventWaitHandle(myHandle);
return OpenExistingResult.Success;
@@ -220,7 +219,7 @@ namespace System.Threading
{
bool res = Win32Native.ResetEvent(safeWaitHandle);
if (!res)
- __Error.WinIOError();
+ throw Win32Marshal.GetExceptionForLastWin32Error();
return res;
}
public bool Set()
@@ -228,7 +227,7 @@ namespace System.Threading
bool res = Win32Native.SetEvent(safeWaitHandle);
if (!res)
- __Error.WinIOError();
+ throw Win32Marshal.GetExceptionForLastWin32Error();
return res;
}
diff --git a/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs b/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs
index 402a76cdc7..0807e19301 100644
--- a/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs
+++ b/src/mscorlib/src/System/Threading/ManualResetEventSlim.cs
@@ -12,9 +12,6 @@
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-using System;
-using System.Threading;
-using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Diagnostics.Contracts;
@@ -48,7 +45,6 @@ namespace System.Threading
{
// These are the default spin counts we use on single-proc and MP machines.
private const int DEFAULT_SPIN_SP = 1;
- private const int DEFAULT_SPIN_MP = SpinWait.YIELD_THRESHOLD;
private volatile object m_lock;
// A lock used for waiting and pulsing. Lazily initialized via EnsureLockObjectCreated()
@@ -185,7 +181,7 @@ namespace System.Threading
/// <summary>
/// Initializes a new instance of the <see cref="ManualResetEventSlim"/>
- /// class with a Boolen value indicating whether to set the intial state to signaled.
+ /// class with a boolean value indicating whether to set the initial state to signaled.
/// </summary>
/// <param name="initialState">true to set the initial state signaled; false to set the initial state
/// to nonsignaled.</param>
@@ -193,12 +189,12 @@ namespace System.Threading
{
// Specify the defualt spin count, and use default spin if we're
// on a multi-processor machine. Otherwise, we won't.
- Initialize(initialState, DEFAULT_SPIN_MP);
+ Initialize(initialState, SpinWait.SpinCountforSpinBeforeWait);
}
/// <summary>
/// Initializes a new instance of the <see cref="ManualResetEventSlim"/>
- /// class with a Boolen value indicating whether to set the intial state to signaled and a specified
+ /// class with a Boolean value indicating whether to set the initial state to signaled and a specified
/// spin count.
/// </summary>
/// <param name="initialState">true to set the initial state to signaled; false to set the initial state
@@ -563,44 +559,19 @@ namespace System.Threading
bNeedTimeoutAdjustment = true;
}
- //spin
- int HOW_MANY_SPIN_BEFORE_YIELD = 10;
- int HOW_MANY_YIELD_EVERY_SLEEP_0 = 5;
- int HOW_MANY_YIELD_EVERY_SLEEP_1 = 20;
-
+ // Spin
int spinCount = SpinCount;
- for (int i = 0; i < spinCount; i++)
+ var spinner = new SpinWait();
+ while (spinner.Count < spinCount)
{
+ spinner.SpinOnce(SpinWait.Sleep1ThresholdForSpinBeforeWait);
+
if (IsSet)
{
return true;
}
- else if (i < HOW_MANY_SPIN_BEFORE_YIELD)
- {
- if (i == HOW_MANY_SPIN_BEFORE_YIELD / 2)
- {
- Thread.Yield();
- }
- else
- {
- Thread.SpinWait(PlatformHelper.ProcessorCount * (4 << i));
- }
- }
- else if (i % HOW_MANY_YIELD_EVERY_SLEEP_1 == 0)
- {
- Thread.Sleep(1);
- }
- else if (i % HOW_MANY_YIELD_EVERY_SLEEP_0 == 0)
- {
- Thread.Sleep(0);
- }
- else
- {
- Thread.Yield();
- }
-
- if (i >= 100 && i % 10 == 0) // check the cancellation token if the user passed a very large spin count
+ if (spinner.Count >= 100 && spinner.Count % 10 == 0) // check the cancellation token if the user passed a very large spin count
cancellationToken.ThrowIfCancellationRequested();
}
diff --git a/src/mscorlib/src/System/Threading/Mutex.cs b/src/mscorlib/src/System/Threading/Mutex.cs
index e3fc8e4233..095c27bf8e 100644
--- a/src/mscorlib/src/System/Threading/Mutex.cs
+++ b/src/mscorlib/src/System/Threading/Mutex.cs
@@ -123,7 +123,7 @@ namespace System.Threading
throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, m_name));
}
}
- __Error.WinIOError(errorCode, m_name);
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, m_name);
}
m_newMutex = errorCode != Win32Native.ERROR_ALREADY_EXISTS;
m_mutex.SetHandleInternal(mutexHandle);
@@ -201,8 +201,7 @@ namespace System.Threading
throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name));
case OpenExistingResult.PathNotFound:
- __Error.WinIOError(Win32Native.ERROR_PATH_NOT_FOUND, name);
- return result; //never executes
+ throw Win32Marshal.GetExceptionForWin32Error(Win32Native.ERROR_PATH_NOT_FOUND, name);
default:
return result;
@@ -261,7 +260,7 @@ namespace System.Threading
return OpenExistingResult.NameInvalid;
// this is for passed through Win32Native Errors
- __Error.WinIOError(errorCode, name);
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, name);
}
result = new Mutex(myHandle);
diff --git a/src/mscorlib/src/System/Threading/PinnableBufferCache.cs b/src/mscorlib/src/System/Threading/PinnableBufferCache.cs
index 3f7853ce59..a896fa22e3 100644
--- a/src/mscorlib/src/System/Threading/PinnableBufferCache.cs
+++ b/src/mscorlib/src/System/Threading/PinnableBufferCache.cs
@@ -210,7 +210,7 @@ namespace System
}
/// <summary>
- /// See if we can promote the buffers to the free list. Returns true if sucessful.
+ /// See if we can promote the buffers to the free list. Returns true if successful.
/// </summary>
private bool AgePendingBuffers()
{
@@ -393,7 +393,7 @@ namespace System
private Func<object> m_factory;
/// <summary>
- /// Contains 'good' buffers to reuse. They are guarenteed to be Gen 2 ENFORCED!
+ /// Contains 'good' buffers to reuse. They are guaranteed to be Gen 2 ENFORCED!
/// </summary>
private ConcurrentStack<object> m_FreeList = new ConcurrentStack<object>();
/// <summary>
@@ -403,7 +403,7 @@ namespace System
/// </summary>
private List<object> m_NotGen2;
/// <summary>
- /// What whas the gen 1 count the last time re restocked? If it is now greater, then
+ /// What was the gen 1 count the last time re restocked? If it is now greater, then
/// we know that all objects are in Gen 2 so we don't have to check. Should be updated
/// every time something gets added to the m_NotGen2 list.
/// </summary>
diff --git a/src/mscorlib/src/System/Threading/Semaphore.cs b/src/mscorlib/src/System/Threading/Semaphore.cs
index 7d2396a9c6..136eee6e76 100644
--- a/src/mscorlib/src/System/Threading/Semaphore.cs
+++ b/src/mscorlib/src/System/Threading/Semaphore.cs
@@ -46,7 +46,7 @@ namespace System.Threading
throw new WaitHandleCannotBeOpenedException(
SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name));
- __Error.WinIOError();
+ throw Win32Marshal.GetExceptionForLastWin32Error();
}
this.SafeWaitHandle = myHandle;
}
@@ -76,7 +76,7 @@ namespace System.Threading
if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
throw new WaitHandleCannotBeOpenedException(
SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name));
- __Error.WinIOError();
+ throw Win32Marshal.GetExceptionForLastWin32Error();
}
createdNew = errorCode != Win32Native.ERROR_ALREADY_EXISTS;
this.SafeWaitHandle = myHandle;
@@ -116,7 +116,7 @@ namespace System.Threading
case OpenExistingResult.NameInvalid:
throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name));
case OpenExistingResult.PathNotFound:
- throw new IOException(Win32Native.GetMessage(Win32Native.ERROR_PATH_NOT_FOUND));
+ throw new IOException(Interop.Kernel32.GetMessage(Win32Native.ERROR_PATH_NOT_FOUND));
default:
return result;
}
@@ -155,7 +155,7 @@ namespace System.Threading
if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
return OpenExistingResult.NameInvalid;
//this is for passed through NativeMethods Errors
- __Error.WinIOError();
+ throw Win32Marshal.GetExceptionForLastWin32Error();
}
result = new Semaphore(myHandle);
diff --git a/src/mscorlib/src/System/Threading/SemaphoreSlim.cs b/src/mscorlib/src/System/Threading/SemaphoreSlim.cs
index 97bbae18cc..a61753d9a9 100644
--- a/src/mscorlib/src/System/Threading/SemaphoreSlim.cs
+++ b/src/mscorlib/src/System/Threading/SemaphoreSlim.cs
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// 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.
@@ -56,7 +56,13 @@ namespace System.Threading
// The number of synchronously waiting threads, it is set to zero in the constructor and increments before blocking the
// threading and decrements it back after that. It is used as flag for the release call to know if there are
// waiting threads in the monitor or not.
- private volatile int m_waitCount;
+ private int m_waitCount;
+
+ /// <summary>
+ /// This is used to help prevent waking more waiters than necessary. It's not perfect and sometimes more waiters than
+ /// necessary may still be woken, see <see cref="WaitUntilCountOrTimeout"/>.
+ /// </summary>
+ private int m_countOfWaitersPulsedToWake;
// Dummy object used to in lock statements to protect the semaphore count, wait handle and cancelation
private object m_lockObj;
@@ -342,15 +348,28 @@ namespace System.Threading
CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCanceledEventHandler, this);
try
{
- // Perf: first spin wait for the count to be positive, but only up to the first planned yield.
+ // Perf: first spin wait for the count to be positive.
// This additional amount of spinwaiting in addition
// to Monitor.Enter()’s spinwaiting has shown measurable perf gains in test scenarios.
//
- SpinWait spin = new SpinWait();
- while (m_currentCount == 0 && !spin.NextSpinWillYield)
+
+ // Monitor.Enter followed by Monitor.Wait is much more expensive than waiting on an event as it involves another
+ // spin, contention, etc. The usual number of spin iterations that would otherwise be used here is increased to
+ // lessen that extra expense of doing a proper wait.
+ int spinCount = SpinWait.SpinCountforSpinBeforeWait * 4;
+ int sleep1Threshold = SpinWait.Sleep1ThresholdForSpinBeforeWait * 4;
+
+ SpinWait spinner = new SpinWait();
+ while (spinner.Count < spinCount)
{
- spin.SpinOnce();
+ spinner.SpinOnce(sleep1Threshold);
+
+ if (m_currentCount != 0)
+ {
+ break;
+ }
}
+
// entering the lock and incrementing waiters must not suffer a thread-abort, else we cannot
// clean up m_waitCount correctly, which may lead to deadlock due to non-woken waiters.
try { }
@@ -442,13 +461,13 @@ namespace System.Threading
}
/// <summary>
- /// Local helper function, waits on the monitor until the monitor recieves signal or the
+ /// Local helper function, waits on the monitor until the monitor receives signal or the
/// timeout is expired
/// </summary>
/// <param name="millisecondsTimeout">The maximum timeout</param>
/// <param name="startTime">The start ticks to calculate the elapsed time</param>
/// <param name="cancellationToken">The CancellationToken to observe.</param>
- /// <returns>true if the monitor recieved a signal, false if the timeout expired</returns>
+ /// <returns>true if the monitor received a signal, false if the timeout expired</returns>
private bool WaitUntilCountOrTimeout(int millisecondsTimeout, uint startTime, CancellationToken cancellationToken)
{
int remainingWaitMilliseconds = Timeout.Infinite;
@@ -468,8 +487,22 @@ namespace System.Threading
return false;
}
}
+
// ** the actual wait **
- if (!Monitor.Wait(m_lockObj, remainingWaitMilliseconds))
+ bool waitSuccessful = Monitor.Wait(m_lockObj, remainingWaitMilliseconds);
+
+ // This waiter has woken up and this needs to be reflected in the count of waiters pulsed to wake. Since we
+ // don't have thread-specific pulse state, there is not enough information to tell whether this thread woke up
+ // because it was pulsed. For instance, this thread may have timed out and may have been waiting to reacquire
+ // the lock before returning from Monitor.Wait, in which case we don't know whether this thread got pulsed. So
+ // in any woken case, decrement the count if possible. As such, timeouts could cause more waiters to wake than
+ // necessary.
+ if (m_countOfWaitersPulsedToWake != 0)
+ {
+ --m_countOfWaitersPulsedToWake;
+ }
+
+ if (!waitSuccessful)
{
return false;
}
@@ -791,13 +824,27 @@ namespace System.Threading
// Increment the count by the actual release count
currentCount += releaseCount;
- // Signal to any synchronous waiters
+ // Signal to any synchronous waiters, taking into account how many waiters have previously been pulsed to wake
+ // but have not yet woken
int waitCount = m_waitCount;
-
- int waitersToNotify = Math.Min(releaseCount, waitCount);
- for (int i = 0; i < waitersToNotify; i++)
+ Debug.Assert(m_countOfWaitersPulsedToWake <= waitCount);
+ int waitersToNotify = Math.Min(currentCount, waitCount) - m_countOfWaitersPulsedToWake;
+ if (waitersToNotify > 0)
{
- Monitor.Pulse(m_lockObj);
+ // Ideally, limiting to a maximum of releaseCount would not be necessary and could be an assert instead, but
+ // since WaitUntilCountOrTimeout() does not have enough information to tell whether a woken thread was
+ // pulsed, it's possible for m_countOfWaitersPulsedToWake to be less than the number of threads that have
+ // actually been pulsed to wake.
+ if (waitersToNotify > releaseCount)
+ {
+ waitersToNotify = releaseCount;
+ }
+
+ m_countOfWaitersPulsedToWake += waitersToNotify;
+ for (int i = 0; i < waitersToNotify; i++)
+ {
+ Monitor.Pulse(m_lockObj);
+ }
}
// Now signal to any asynchronous waiters, if there are any. While we've already
@@ -910,14 +957,6 @@ namespace System.Threading
}
}
- /// <summary>
- /// local helper function to retrieve the exception string message from the resource file
- /// </summary>
- /// <param name="str">The key string</param>
- private static string GetResourceString(string str)
- {
- return SR.GetResourceString(str);
- }
#endregion
}
}
diff --git a/src/mscorlib/src/System/Threading/SpinLock.cs b/src/mscorlib/src/System/Threading/SpinLock.cs
index eee73ce2bf..917ece2d4d 100644
--- a/src/mscorlib/src/System/Threading/SpinLock.cs
+++ b/src/mscorlib/src/System/Threading/SpinLock.cs
@@ -65,16 +65,9 @@ namespace System.Threading
private volatile int m_owner;
- // The multiplier factor for the each spinning iteration
- // This number has been chosen after trying different numbers on different CPUs (4, 8 and 16 ) and this provided the best results
- private const int SPINNING_FACTOR = 100;
-
// After how many yields, call Sleep(1)
private const int SLEEP_ONE_FREQUENCY = 40;
- // After how many yields, call Sleep(0)
- private const int SLEEP_ZERO_FREQUENCY = 10;
-
// After how many yields, check the timeout
private const int TIMEOUT_CHECK_FREQUENCY = 10;
@@ -277,7 +270,7 @@ namespace System.Threading
/// <summary>
/// Try acquire the lock with long path, this is usually called after the first path in Enter and
/// TryEnter failed The reason for short path is to make it inline in the run time which improves the
- /// performance. This method assumed that the parameter are validated in Enter ir TryENter method
+ /// performance. This method assumed that the parameter are validated in Enter or TryEnter method.
/// </summary>
/// <param name="millisecondsTimeout">The timeout milliseconds</param>
/// <param name="lockTaken">The lockTaken param</param>
@@ -347,48 +340,24 @@ namespace System.Threading
else //failed to acquire the lock,then try to update the waiters. If the waiters count reached the maximum, jsut break the loop to avoid overflow
{
if ((observedOwner & WAITERS_MASK) != MAXIMUM_WAITERS)
+ {
+ // This can still overflow, but maybe there will never be that many waiters
turn = (Interlocked.Add(ref m_owner, 2) & WAITERS_MASK) >> 1;
+ }
}
- //***Step 2. Spinning
//lock acquired failed and waiters updated
- int processorCount = PlatformHelper.ProcessorCount;
- if (turn < processorCount)
- {
- int processFactor = 1;
- for (int i = 1; i <= turn * SPINNING_FACTOR; i++)
- {
- Thread.SpinWait((turn + i) * SPINNING_FACTOR * processFactor);
- if (processFactor < processorCount)
- processFactor++;
- observedOwner = m_owner;
- if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
- {
- int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero
- observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters
- : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit
- Debug.Assert((newOwner & WAITERS_MASK) >= 0);
-
- if (CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner)
- {
- return;
- }
- }
- }
- // Check the timeout.
- if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0)
- {
- DecrementWaiters();
- return;
- }
+ //*** Step 2, Spinning and Yielding
+ var spinner = new SpinWait();
+ if (turn > PlatformHelper.ProcessorCount)
+ {
+ spinner.Count = SpinWait.YieldThreshold;
}
-
- //*** Step 3, Yielding
- //Sleep(1) every 50 yields
- int yieldsoFar = 0;
while (true)
{
+ spinner.SpinOnce(SLEEP_ONE_FREQUENCY);
+
observedOwner = m_owner;
if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED)
{
@@ -403,20 +372,7 @@ namespace System.Threading
}
}
- if (yieldsoFar % SLEEP_ONE_FREQUENCY == 0)
- {
- Thread.Sleep(1);
- }
- else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0)
- {
- Thread.Sleep(0);
- }
- else
- {
- Thread.Yield();
- }
-
- if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0)
+ if (spinner.Count % TIMEOUT_CHECK_FREQUENCY == 0)
{
//Check the timeout.
if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0)
@@ -425,8 +381,6 @@ namespace System.Threading
return;
}
}
-
- yieldsoFar++;
}
}
diff --git a/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs b/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs
index 07a673bf4e..765dcd8408 100644
--- a/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs
@@ -41,10 +41,10 @@ namespace System.Threading.Tasks
private readonly TaskScheduler m_underlyingTaskScheduler;
/// <summary>
/// The maximum number of tasks allowed to run concurrently. This only applies to concurrent tasks,
- /// since exlusive tasks are inherently limited to 1.
+ /// since exclusive tasks are inherently limited to 1.
/// </summary>
private readonly int m_maxConcurrencyLevel;
- /// <summary>The maximum number of tasks we can process before recyling our runner tasks.</summary>
+ /// <summary>The maximum number of tasks we can process before recycling our runner tasks.</summary>
private readonly int m_maxItemsPerTask;
/// <summary>
/// If positive, it represents the number of concurrently running concurrent tasks.
@@ -225,7 +225,7 @@ namespace System.Threading.Tasks
}
}
- /// <summary>Initiatites scheduler shutdown due to a worker task faulting..</summary>
+ /// <summary>Initiates scheduler shutdown due to a worker task faulting.</summary>
/// <param name="faultedTask">The faulted worker task that's initiating the shutdown.</param>
private void FaultWithTask(Task faultedTask)
{
diff --git a/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs
index 60a7c81dcf..bf63a53df9 100644
--- a/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs
@@ -1293,7 +1293,7 @@ namespace System.Threading.Tasks
/// <summary>
/// Completes the asynchronous operation using information in the IAsyncResult.
- /// IAsyncResult.AsyncState neeeds to be the FromAsyncTrimPromise to complete.
+ /// IAsyncResult.AsyncState needs to be the FromAsyncTrimPromise to complete.
/// </summary>
/// <param name="asyncResult">The IAsyncResult for the async operation.</param>
internal static void CompleteFromAsyncResult(IAsyncResult asyncResult)
diff --git a/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs b/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs
index f9d5f89398..df0dbe3942 100644
--- a/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/ProducerConsumerQueues.cs
@@ -332,7 +332,7 @@ namespace System.Threading.Tasks
private struct SegmentState
{
/// <summary>Padding to reduce false sharing between the segment's array and m_first.</summary>
- internal PaddingFor32 m_pad0;
+ internal Internal.PaddingFor32 m_pad0;
/// <summary>The index of the current head in the segment.</summary>
internal volatile int m_first;
@@ -340,7 +340,7 @@ namespace System.Threading.Tasks
internal int m_lastCopy; // not volatile as read and written by the producer, except for IsEmpty, and there m_lastCopy is only read after reading the volatile m_first
/// <summary>Padding to reduce false sharing between the first and last.</summary>
- internal PaddingFor32 m_pad1;
+ internal Internal.PaddingFor32 m_pad1;
/// <summary>A copy of the current head index.</summary>
internal int m_firstCopy; // not voliatle as only read and written by the consumer thread
@@ -348,7 +348,7 @@ namespace System.Threading.Tasks
internal volatile int m_last;
/// <summary>Padding to reduce false sharing with the last and what's after the segment.</summary>
- internal PaddingFor32 m_pad2;
+ internal Internal.PaddingFor32 m_pad2;
}
/// <summary>Debugger type proxy for a SingleProducerSingleConsumerQueue of T.</summary>
@@ -366,17 +366,4 @@ namespace System.Threading.Tasks
}
}
}
-
- /// <summary>A placeholder class for common padding constants and eventually routines.</summary>
- internal static class PaddingHelpers
- {
- /// <summary>A size greater than or equal to the size of the most common CPU cache lines.</summary>
- internal const int CACHE_LINE_SIZE = 128;
- }
-
- /// <summary>Padding structure used to minimize false sharing in SingleProducerSingleConsumerQueue{T}.</summary>
- [StructLayout(LayoutKind.Explicit, Size = PaddingHelpers.CACHE_LINE_SIZE - sizeof(Int32))] // Based on common case of 64-byte cache lines
- internal struct PaddingFor32
- {
- }
}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs b/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs
index 33bf792370..bdfe8eeff9 100644
--- a/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs
@@ -130,7 +130,7 @@ namespace System.Threading.Tasks
/// <summary>
/// TasksSetActivityIds will cause the task operations to set Activity Ids
/// This option is incompatible with TasksFlowActivityIds flow is ignored
- /// if that keyword is set. This option is likley to be removed in the future
+ /// if that keyword is set. This option is likely to be removed in the future
/// </summary>
public const EventKeywords TasksSetActivityIds = (EventKeywords)0x10000;
@@ -360,7 +360,7 @@ namespace System.Threading.Tasks
}
/// <summary>
- /// Fired when the the work (method) associated with a TaskWaitEnd completes
+ /// Fired when the work (method) associated with a TaskWaitEnd completes
/// </summary>
/// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
/// <param name="OriginatingTaskID">The task ID.</param>
@@ -375,7 +375,7 @@ namespace System.Threading.Tasks
}
/// <summary>
- /// Fired when the the work (method) associated with a TaskWaitEnd completes
+ /// Fired when the work (method) associated with a TaskWaitEnd completes
/// </summary>
/// <param name="OriginatingTaskSchedulerID">The scheduler ID.</param>
/// <param name="OriginatingTaskID">The task ID.</param>
diff --git a/src/mscorlib/src/System/Threading/Tasks/Task.cs b/src/mscorlib/src/System/Threading/Tasks/Task.cs
index c19fd3ec64..84811fb068 100644
--- a/src/mscorlib/src/System/Threading/Tasks/Task.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/Task.cs
@@ -10,19 +10,14 @@
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.Runtime;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Runtime.ExceptionServices;
-using System.Security;
-using System.Threading;
using System.Diagnostics;
using System.Diagnostics.Contracts;
-using Microsoft.Win32;
using System.Diagnostics.Tracing;
+using System.Runtime.CompilerServices;
+using System.Runtime.ExceptionServices;
+using Internal.Runtime.Augments;
// Disable the "reference to volatile field not treated as volatile" error.
#pragma warning disable 0420
@@ -440,7 +435,7 @@ namespace System.Threading.Tasks
}
/// <summary>
- /// Initializes a new <see cref="Task"/> with the specified action, state, snd options.
+ /// Initializes a new <see cref="Task"/> with the specified action, state, and options.
/// </summary>
/// <param name="action">The delegate that represents the code to execute in the task.</param>
/// <param name="state">An object representing data to be used by the action.</param>
@@ -457,7 +452,7 @@ namespace System.Threading.Tasks
}
/// <summary>
- /// Initializes a new <see cref="Task"/> with the specified action, state, snd options.
+ /// Initializes a new <see cref="Task"/> with the specified action, state, and options.
/// </summary>
/// <param name="action">The delegate that represents the code to execute in the task.</param>
/// <param name="state">An object representing data to be used by the action.</param>
@@ -478,7 +473,7 @@ namespace System.Threading.Tasks
}
/// <summary>
- /// Initializes a new <see cref="Task"/> with the specified action, state, snd options.
+ /// Initializes a new <see cref="Task"/> with the specified action, state, and options.
/// </summary>
/// <param name="action">The delegate that represents the code to execute in the task.</param>
/// <param name="state">An object representing data to be used by the action.</param>
@@ -613,7 +608,7 @@ namespace System.Threading.Tasks
/// <summary>
/// Handles everything needed for associating a CancellationToken with a task which is being constructed.
- /// This method is meant to be be called either from the TaskConstructorCore or from ContinueWithCore.
+ /// This method is meant to be called either from the TaskConstructorCore or from ContinueWithCore.
/// </summary>
private void AssignCancellationToken(CancellationToken cancellationToken, Task antecedent, TaskContinuation continuation)
{
@@ -2489,7 +2484,7 @@ namespace System.Threading.Tasks
private static readonly ContextCallback s_ecCallback = obj => ((Task)obj).InnerInvoke();
/// <summary>
- /// The actual code which invokes the body of the task. This can be overriden in derived types.
+ /// The actual code which invokes the body of the task. This can be overridden in derived types.
/// </summary>
internal virtual void InnerInvoke()
{
@@ -2827,11 +2822,16 @@ namespace System.Threading.Tasks
}
/// <summary>
- /// The core wait function, which is only accesible internally. It's meant to be used in places in TPL code where
+ /// The core wait function, which is only accessible internally. It's meant to be used in places in TPL code where
/// the current context is known or cached.
/// </summary>
[MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
- internal bool InternalWait(int millisecondsTimeout, CancellationToken cancellationToken)
+ internal bool InternalWait(int millisecondsTimeout, CancellationToken cancellationToken) =>
+ InternalWaitCore(millisecondsTimeout, cancellationToken);
+
+ // Separated out to allow it to be optimized (caller is marked NoOptimization for VS parallel debugger
+ // to be able to see the method on the stack and inspect arguments).
+ private bool InternalWaitCore(int millisecondsTimeout, CancellationToken cancellationToken)
{
// ETW event for Task Wait Begin
var etwLog = TplEtwProvider.Log;
@@ -2966,26 +2966,19 @@ namespace System.Threading.Tasks
return false;
}
- //This code is pretty similar to the custom spinning in MRES except there is no yieling after we exceed the spin count
- int spinCount = PlatformHelper.IsSingleProcessor ? 1 : System.Threading.SpinWait.YIELD_THRESHOLD; //spin only once if we are running on a single CPU
- for (int i = 0; i < spinCount; i++)
+ int spinCount = Threading.SpinWait.SpinCountforSpinBeforeWait;
+ var spinner = new SpinWait();
+ while (spinner.Count < spinCount)
{
+ spinner.SpinOnce(Threading.SpinWait.Sleep1ThresholdForSpinBeforeWait);
+
if (IsCompleted)
{
return true;
}
-
- if (i == spinCount / 2)
- {
- Thread.Yield();
- }
- else
- {
- Thread.SpinWait(PlatformHelper.ProcessorCount * (4 << i));
- }
}
- return IsCompleted;
+ return false;
}
/// <summary>
@@ -3222,7 +3215,7 @@ namespace System.Threading.Tasks
// Skip synchronous execution of continuations if this task's thread was aborted
bool bCanInlineContinuations = !(((m_stateFlags & TASK_STATE_THREAD_WAS_ABORTED) != 0) ||
- (Thread.CurrentThread.ThreadState == ThreadState.AbortRequested) ||
+ (RuntimeThread.CurrentThread.ThreadState == ThreadState.AbortRequested) ||
((m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) != 0));
// Handle the single-Action case
@@ -4458,7 +4451,7 @@ namespace System.Threading.Tasks
#if DEBUG
bool waitResult =
#endif
- WaitAll(tasks, Timeout.Infinite);
+ WaitAllCore(tasks, Timeout.Infinite, default(CancellationToken));
#if DEBUG
Debug.Assert(waitResult, "expected wait to succeed");
@@ -4503,7 +4496,7 @@ namespace System.Threading.Tasks
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.timeout);
}
- return WaitAll(tasks, (int)totalMilliseconds);
+ return WaitAllCore(tasks, (int)totalMilliseconds, default(CancellationToken));
}
/// <summary>
@@ -4535,7 +4528,7 @@ namespace System.Threading.Tasks
[MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
public static bool WaitAll(Task[] tasks, int millisecondsTimeout)
{
- return WaitAll(tasks, millisecondsTimeout, default(CancellationToken));
+ return WaitAllCore(tasks, millisecondsTimeout, default(CancellationToken));
}
/// <summary>
@@ -4567,7 +4560,7 @@ namespace System.Threading.Tasks
[MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
public static void WaitAll(Task[] tasks, CancellationToken cancellationToken)
{
- WaitAll(tasks, Timeout.Infinite, cancellationToken);
+ WaitAllCore(tasks, Timeout.Infinite, cancellationToken);
}
/// <summary>
@@ -4605,7 +4598,12 @@ namespace System.Threading.Tasks
/// The <paramref name="cancellationToken"/> was canceled.
/// </exception>
[MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
- public static bool WaitAll(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
+ public static bool WaitAll(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken) =>
+ WaitAllCore(tasks, millisecondsTimeout, cancellationToken);
+
+ // Separated out to allow it to be optimized (caller is marked NoOptimization for VS parallel debugger
+ // to be able to see the method on the stack and inspect arguments).
+ private static bool WaitAllCore(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
{
if (tasks == null)
{
@@ -4847,7 +4845,7 @@ namespace System.Threading.Tasks
[MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
public static int WaitAny(params Task[] tasks)
{
- int waitResult = WaitAny(tasks, Timeout.Infinite);
+ int waitResult = WaitAnyCore(tasks, Timeout.Infinite, default(CancellationToken));
Debug.Assert(tasks.Length == 0 || waitResult != -1, "expected wait to succeed");
return waitResult;
}
@@ -4886,7 +4884,7 @@ namespace System.Threading.Tasks
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.timeout);
}
- return WaitAny(tasks, (int)totalMilliseconds);
+ return WaitAnyCore(tasks, (int)totalMilliseconds, default(CancellationToken));
}
/// <summary>
@@ -4913,7 +4911,7 @@ namespace System.Threading.Tasks
[MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
public static int WaitAny(Task[] tasks, CancellationToken cancellationToken)
{
- return WaitAny(tasks, Timeout.Infinite, cancellationToken);
+ return WaitAnyCore(tasks, Timeout.Infinite, cancellationToken);
}
/// <summary>
@@ -4943,7 +4941,7 @@ namespace System.Threading.Tasks
[MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
public static int WaitAny(Task[] tasks, int millisecondsTimeout)
{
- return WaitAny(tasks, millisecondsTimeout, default(CancellationToken));
+ return WaitAnyCore(tasks, millisecondsTimeout, default(CancellationToken));
}
/// <summary>
@@ -4977,7 +4975,12 @@ namespace System.Threading.Tasks
/// The <paramref name="cancellationToken"/> was canceled.
/// </exception>
[MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
- public static int WaitAny(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
+ public static int WaitAny(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken) =>
+ WaitAnyCore(tasks, millisecondsTimeout, cancellationToken);
+
+ // Separated out to allow it to be optimized (caller is marked NoOptimization for VS parallel debugger
+ // to be able to inspect arguments).
+ private static int WaitAnyCore(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
{
if (tasks == null)
{
diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs b/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs
index 666a19297f..eddaeee9d7 100644
--- a/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/TaskScheduler.cs
@@ -642,7 +642,7 @@ namespace System.Threading.Tasks
}
/// <summary>
- /// Implemetation of <see cref="T:System.Threading.Tasks.TaskScheduler.QueueTask"/> for this scheduler class.
+ /// Implementation of <see cref="T:System.Threading.Tasks.TaskScheduler.QueueTask"/> for this scheduler class.
///
/// Simply posts the tasks to be executed on the associated <see cref="T:System.Threading.SynchronizationContext"/>.
/// </summary>
@@ -677,7 +677,7 @@ namespace System.Threading.Tasks
}
/// <summary>
- /// Implementes the <see cref="T:System.Threading.Tasks.TaskScheduler.MaximumConcurrencyLevel"/> property for
+ /// Implements the <see cref="T:System.Threading.Tasks.TaskScheduler.MaximumConcurrencyLevel"/> property for
/// this scheduler class.
///
/// By default it returns 1, because a <see cref="T:System.Threading.SynchronizationContext"/> based
diff --git a/src/mscorlib/src/System/Threading/Thread.cs b/src/mscorlib/src/System/Threading/Thread.cs
index 84c5ebb552..d8ea6410eb 100644
--- a/src/mscorlib/src/System/Threading/Thread.cs
+++ b/src/mscorlib/src/System/Threading/Thread.cs
@@ -128,7 +128,12 @@ namespace System.Threading
private IntPtr DONT_USE_InternalThread; // Pointer
private int m_Priority; // INT32
- private int m_ManagedThreadId; // INT32
+
+ // The following field is required for interop with the VS Debugger
+ // Prior to making any changes to this field, please reach out to the VS Debugger
+ // team to make sure that your changes are not going to prevent the debugger
+ // from working.
+ private int _managedThreadId; // INT32
#pragma warning restore 414
#pragma warning restore 169
@@ -200,7 +205,7 @@ namespace System.Threading
public override int GetHashCode()
{
- return m_ManagedThreadId;
+ return _managedThreadId;
}
extern public new int ManagedThreadId
diff --git a/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs b/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs
index fb72110fdc..db42b77ca4 100644
--- a/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs
+++ b/src/mscorlib/src/System/Threading/ThreadInterruptedException.cs
@@ -24,19 +24,19 @@ namespace System.Threading
public ThreadInterruptedException()
: base(GetMessageFromNativeResources(ExceptionMessageKind.ThreadInterrupted))
{
- HResult = __HResults.COR_E_THREADINTERRUPTED;
+ HResult = HResults.COR_E_THREADINTERRUPTED;
}
public ThreadInterruptedException(String message)
: base(message)
{
- HResult = __HResults.COR_E_THREADINTERRUPTED;
+ HResult = HResults.COR_E_THREADINTERRUPTED;
}
public ThreadInterruptedException(String message, Exception innerException)
: base(message, innerException)
{
- HResult = __HResults.COR_E_THREADINTERRUPTED;
+ HResult = HResults.COR_E_THREADINTERRUPTED;
}
protected ThreadInterruptedException(SerializationInfo info, StreamingContext context) : base(info, context)
diff --git a/src/mscorlib/src/System/Threading/ThreadLocal.cs b/src/mscorlib/src/System/Threading/ThreadLocal.cs
index 64b8a60196..cdfa2448c0 100644
--- a/src/mscorlib/src/System/Threading/ThreadLocal.cs
+++ b/src/mscorlib/src/System/Threading/ThreadLocal.cs
@@ -712,7 +712,7 @@ namespace System.Threading
/// A class that facilitates ThreadLocal cleanup after a thread exits.
///
/// After a thread with an associated thread-local table has exited, the FinalizationHelper
- /// is reponsible for removing back-references to the table. Since an instance of FinalizationHelper
+ /// is responsible for removing back-references to the table. Since an instance of FinalizationHelper
/// is only referenced from a single thread-local slot, the FinalizationHelper will be GC'd once
/// the thread has exited.
///
diff --git a/src/mscorlib/src/System/Threading/ThreadPool.cs b/src/mscorlib/src/System/Threading/ThreadPool.cs
index 0084050c43..ec9ceef156 100644
--- a/src/mscorlib/src/System/Threading/ThreadPool.cs
+++ b/src/mscorlib/src/System/Threading/ThreadPool.cs
@@ -39,6 +39,7 @@ namespace System.Threading
public static readonly ThreadPoolWorkQueue workQueue = new ThreadPoolWorkQueue();
}
+ [StructLayout(LayoutKind.Sequential)] // enforce layout so that padding reduces false sharing
internal sealed class ThreadPoolWorkQueue
{
internal static class WorkStealingQueueList
@@ -47,6 +48,12 @@ namespace System.Threading
public static WorkStealingQueue[] Queues => _queues;
+ // Track whether the WorkStealingQueueList is empty
+ // Three states simplifies race conditions. They may be considered.
+ // Now Active --> Maybe Inactive -> Confirmed Inactive
+ public const int WsqNowActive = 2;
+ public static int wsqActive;
+
public static void Add(WorkStealingQueue queue)
{
Debug.Assert(queue != null);
@@ -378,8 +385,12 @@ namespace System.Threading
internal bool loggingEnabled;
internal readonly ConcurrentQueue<IThreadPoolWorkItem> workItems = new ConcurrentQueue<IThreadPoolWorkItem>();
+ private Internal.PaddingFor32 pad1;
+
private volatile int numOutstandingThreadRequests = 0;
+ private Internal.PaddingFor32 pad2;
+
public ThreadPoolWorkQueue()
{
loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool | FrameworkEventSource.Keywords.ThreadTransfer);
@@ -389,15 +400,20 @@ namespace System.Threading
ThreadPoolWorkQueueThreadLocals.threadLocals ??
(ThreadPoolWorkQueueThreadLocals.threadLocals = new ThreadPoolWorkQueueThreadLocals(this));
+ internal bool ThreadRequestNeeded(int count) => (count < ThreadPoolGlobals.processorCount) &&
+ (!workItems.IsEmpty || (WorkStealingQueueList.wsqActive > 0));
+
internal void EnsureThreadRequested()
{
//
- // If we have not yet requested #procs threads from the VM, then request a new thread.
+ // If we have not yet requested #procs threads from the VM, then request a new thread
+ // as needed
+ //
// Note that there is a separate count in the VM which will also be incremented in this case,
// which is handled by RequestWorkerThread.
//
int count = numOutstandingThreadRequests;
- while (count < ThreadPoolGlobals.processorCount)
+ while (ThreadRequestNeeded(count))
{
int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count + 1, count);
if (prev == count)
@@ -409,7 +425,7 @@ namespace System.Threading
}
}
- internal void MarkThreadRequestSatisfied()
+ internal void MarkThreadRequestSatisfied(bool dequeueSuccessful)
{
//
// The VM has called us, so one of our outstanding thread requests has been satisfied.
@@ -418,8 +434,17 @@ namespace System.Threading
// by the time we reach this point.
//
int count = numOutstandingThreadRequests;
+
while (count > 0)
{
+ if (dequeueSuccessful && (count == ThreadPoolGlobals.processorCount) && ThreadRequestNeeded(count - 1))
+ {
+ // If we gated threads due to too many outstanding requests and queue was not empty
+ // Request another thread.
+ ThreadPool.RequestWorkerThread();
+ return;
+ }
+
int prev = Interlocked.CompareExchange(ref numOutstandingThreadRequests, count - 1, count);
if (prev == count)
{
@@ -441,6 +466,18 @@ namespace System.Threading
if (null != tl)
{
tl.workStealingQueue.LocalPush(callback);
+
+ // We must guarantee wsqActive is set to WsqNowActive after we push
+ // The ordering must be global because we rely on other threads
+ // observing in this order
+ Interlocked.MemoryBarrier();
+
+ // We do not want to simply write. We want to prevent unnecessary writes
+ // which would invalidate reader's caches
+ if (WorkStealingQueueList.wsqActive != WorkStealingQueueList.WsqNowActive)
+ {
+ Volatile.Write(ref WorkStealingQueueList.wsqActive, WorkStealingQueueList.WsqNowActive);
+ }
}
else
{
@@ -458,33 +495,56 @@ namespace System.Threading
public IThreadPoolWorkItem Dequeue(ThreadPoolWorkQueueThreadLocals tl, ref bool missedSteal)
{
- WorkStealingQueue localWsq = tl.workStealingQueue;
IThreadPoolWorkItem callback;
-
- if ((callback = localWsq.LocalPop()) == null && // first try the local queue
- !workItems.TryDequeue(out callback)) // then try the global queue
+ int wsqActiveObserved = WorkStealingQueueList.wsqActive;
+ if (wsqActiveObserved > 0)
{
- // finally try to steal from another thread's local queue
- WorkStealingQueue[] queues = WorkStealingQueueList.Queues;
- int c = queues.Length;
- Debug.Assert(c > 0, "There must at least be a queue for this thread.");
- int maxIndex = c - 1;
- int i = tl.random.Next(c);
- while (c > 0)
+ WorkStealingQueue localWsq = tl.workStealingQueue;
+
+ if ((callback = localWsq.LocalPop()) == null && // first try the local queue
+ !workItems.TryDequeue(out callback)) // then try the global queue
{
- i = (i < maxIndex) ? i + 1 : 0;
- WorkStealingQueue otherQueue = queues[i];
- if (otherQueue != localWsq && otherQueue.CanSteal)
+ // finally try to steal from another thread's local queue
+ WorkStealingQueue[] queues = WorkStealingQueueList.Queues;
+ int c = queues.Length;
+ Debug.Assert(c > 0, "There must at least be a queue for this thread.");
+ int maxIndex = c - 1;
+ int i = tl.random.Next(c);
+ while (c > 0)
{
- callback = otherQueue.TrySteal(ref missedSteal);
- if (callback != null)
+ i = (i < maxIndex) ? i + 1 : 0;
+ WorkStealingQueue otherQueue = queues[i];
+ if (otherQueue != localWsq && otherQueue.CanSteal)
{
- break;
+ callback = otherQueue.TrySteal(ref missedSteal);
+ if (callback != null)
+ {
+ break;
+ }
}
+ c--;
+ }
+ if ((callback == null) && !missedSteal)
+ {
+ // Only decrement if the value is unchanged since we started looking for work
+ // This prevents multiple threads decrementing based on overlapping scans.
+ //
+ // When we decrement from active, the producer may have inserted a queue item during our scan
+ // therefore we cannot transition to empty
+ //
+ // When we decrement from Maybe Inactive, if the producer inserted a queue item during our scan,
+ // the producer must write Active. We may transition to empty briefly if we beat the
+ // producer's write, but the producer will then overwrite us before waking threads.
+ // So effectively we cannot mark the queue empty when an item is in the queue.
+ Interlocked.CompareExchange(ref WorkStealingQueueList.wsqActive, wsqActiveObserved - 1, wsqActiveObserved);
}
- c--;
}
}
+ else
+ {
+ // We only need to look at the global queue since WorkStealingQueueList is inactive
+ workItems.TryDequeue(out callback);
+ }
return callback;
}
@@ -498,15 +558,7 @@ namespace System.Threading
//
int quantumStartTime = Environment.TickCount;
- //
- // Update our records to indicate that an outstanding request for a thread has now been fulfilled.
- // From this point on, we are responsible for requesting another thread if we stop working for any
- // reason, and we believe there might still be work in the queue.
- //
- // Note that if this thread is aborted before we get a chance to request another one, the VM will
- // record a thread request on our behalf. So we don't need to worry about getting aborted right here.
- //
- workQueue.MarkThreadRequestSatisfied();
+ bool markThreadRequestSatisfied = true;
// Has the desire for logging changed since the last time we entered?
workQueue.loggingEnabled = FrameworkEventSource.Log.IsEnabled(EventLevel.Verbose, FrameworkEventSource.Keywords.ThreadPool | FrameworkEventSource.Keywords.ThreadTransfer);
@@ -555,7 +607,21 @@ namespace System.Threading
// If we found work, there may be more work. Ask for another thread so that the other work can be processed
// in parallel. Note that this will only ask for a max of #procs threads, so it's safe to call it for every dequeue.
//
- workQueue.EnsureThreadRequested();
+ if (markThreadRequestSatisfied)
+ {
+ //
+ // Update our records to indicate that an outstanding request for a thread has now been fulfilled
+ // and that an item was successfully dispatched and another thread may be needed
+ //
+ // From this point on, we are responsible for requesting another thread if we stop working for any
+ // reason, and we believe there might still be work in the queue.
+ //
+ // Note that if this thread is aborted before we get a chance to request another one, the VM will
+ // record a thread request on our behalf. So we don't need to worry about getting aborted right here.
+ //
+ workQueue.MarkThreadRequestSatisfied(true);
+ markThreadRequestSatisfied = false;
+ }
//
// Execute the workitem outside of any finally blocks, so that it can be aborted if needed.
@@ -610,6 +676,15 @@ namespace System.Threading
}
finally
{
+ if (markThreadRequestSatisfied)
+ {
+ //
+ // Update our records to indicate that an outstanding request for a thread has now been fulfilled
+ // and that an item was not successfully dispatched. We will request thread below if needed
+ //
+ workQueue.MarkThreadRequestSatisfied(false);
+ }
+
//
// If we are exiting for any reason other than that the queue is definitely empty, ask for another
// thread to pick up where we left off.
@@ -715,16 +790,11 @@ namespace System.Threading
{
// needed for DangerousAddRef
RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- }
- finally
+
+ m_internalWaitObject = waitObject;
+ if (waitObject != null)
{
- m_internalWaitObject = waitObject;
- if (waitObject != null)
- {
- m_internalWaitObject.SafeWaitHandle.DangerousAddRef(ref bReleaseNeeded);
- }
+ m_internalWaitObject.SafeWaitHandle.DangerousAddRef(ref bReleaseNeeded);
}
}
@@ -735,47 +805,43 @@ namespace System.Threading
bool result = false;
// needed for DangerousRelease
RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- }
- finally
+
+ // lock(this) cannot be used reliably in Cer since thin lock could be
+ // promoted to syncblock and that is not a guaranteed operation
+ bool bLockTaken = false;
+ do
{
- // lock(this) cannot be used reliably in Cer since thin lock could be
- // promoted to syncblock and that is not a guaranteed operation
- bool bLockTaken = false;
- do
+ if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
{
- if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0)
+ bLockTaken = true;
+ try
{
- bLockTaken = true;
- try
+ if (ValidHandle())
{
- if (ValidHandle())
+ result = UnregisterWaitNative(GetHandle(), waitObject == null ? null : waitObject.SafeWaitHandle);
+ if (result == true)
{
- result = UnregisterWaitNative(GetHandle(), waitObject == null ? null : waitObject.SafeWaitHandle);
- if (result == true)
+ if (bReleaseNeeded)
{
- if (bReleaseNeeded)
- {
- m_internalWaitObject.SafeWaitHandle.DangerousRelease();
- bReleaseNeeded = false;
- }
- // if result not true don't release/suppress here so finalizer can make another attempt
- SetHandle(InvalidHandle);
- m_internalWaitObject = null;
- GC.SuppressFinalize(this);
+ m_internalWaitObject.SafeWaitHandle.DangerousRelease();
+ bReleaseNeeded = false;
}
+ // if result not true don't release/suppress here so finalizer can make another attempt
+ SetHandle(InvalidHandle);
+ m_internalWaitObject = null;
+ GC.SuppressFinalize(this);
}
}
- finally
- {
- m_lock = 0;
- }
}
- Thread.SpinWait(1); // yield to processor
+ finally
+ {
+ m_lock = 0;
+ }
}
- while (!bLockTaken);
+ Thread.SpinWait(1); // yield to processor
}
+ while (!bLockTaken);
+
return result;
}
diff --git a/src/mscorlib/src/System/ThrowHelper.cs b/src/mscorlib/src/System/ThrowHelper.cs
index ff76738e8e..6c5ce05b7f 100644
--- a/src/mscorlib/src/System/ThrowHelper.cs
+++ b/src/mscorlib/src/System/ThrowHelper.cs
@@ -123,7 +123,7 @@ namespace System
internal static void ThrowKeyNotFoundException()
{
- throw new System.Collections.Generic.KeyNotFoundException();
+ throw new KeyNotFoundException();
}
internal static void ThrowArgumentException(ExceptionResource resource)
@@ -226,6 +226,11 @@ namespace System
throw new AggregateException(exceptions);
}
+ internal static void ThrowOutOfMemoryException()
+ {
+ throw new OutOfMemoryException();
+ }
+
internal static void ThrowArgumentException_Argument_InvalidArrayType()
{
throw GetArgumentException(ExceptionResource.Argument_InvalidArrayType);
@@ -241,6 +246,11 @@ namespace System
throw GetInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded);
}
+ internal static void ThrowInvalidOperationException_EnumCurrent(int index)
+ {
+ throw GetInvalidOperationException_EnumCurrent(index);
+ }
+
internal static void ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion()
{
throw GetInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
@@ -304,6 +314,14 @@ namespace System
return new ArgumentOutOfRangeException(GetArgumentName(argument) + "[" + paramNumber.ToString() + "]", GetResourceString(resource));
}
+ private static InvalidOperationException GetInvalidOperationException_EnumCurrent(int index)
+ {
+ return GetInvalidOperationException(
+ index < 0 ?
+ ExceptionResource.InvalidOperation_EnumNotStarted :
+ ExceptionResource.InvalidOperation_EnumEnded);
+ }
+
// Allow nulls for reference types and Nullable<U>, but not for value types.
// Aggressively inline so the jit evaluates the if in place and either drops the call altogether
// Or just leaves null test and call to the Non-returning ThrowHelper.ThrowArgumentNullException
@@ -343,7 +361,6 @@ namespace System
{
obj,
dictionary,
- dictionaryCreationThreshold,
array,
info,
key,
@@ -351,8 +368,6 @@ namespace System
list,
match,
converter,
- queue,
- stack,
capacity,
index,
startIndex,
@@ -360,7 +375,6 @@ namespace System
count,
arrayIndex,
name,
- mode,
item,
options,
view,
@@ -407,16 +421,18 @@ namespace System
beginMethod,
continuationOptions,
continuationAction,
- valueFactory,
- addValueFactory,
- updateValueFactory,
concurrencyLevel,
text,
callBack,
type,
stateMachine,
pHandle,
- values
+ values,
+ task,
+ s,
+ keyValuePair,
+ input,
+ ownedMemory
}
//
@@ -509,12 +525,13 @@ namespace System
TaskT_TransitionToFinal_AlreadyCompleted,
TaskCompletionSourceT_TrySetException_NullException,
TaskCompletionSourceT_TrySetException_NoExceptions,
+ Memory_ThrowIfDisposed,
+ Memory_OutstandingReferences,
InvalidOperation_WrongAsyncResultOrEndCalledMultiple,
ConcurrentDictionary_ConcurrencyLevelMustBePositive,
ConcurrentDictionary_CapacityMustNotBeNegative,
ConcurrentDictionary_TypeOfValueIncorrect,
ConcurrentDictionary_TypeOfKeyIncorrect,
- ConcurrentDictionary_SourceContainsDuplicateKeys,
ConcurrentDictionary_KeyAlreadyExisted,
ConcurrentDictionary_ItemKeyIsNull,
ConcurrentDictionary_IndexIsNegative,
diff --git a/src/mscorlib/src/System/TimeZoneInfo.AdjustmentRule.cs b/src/mscorlib/src/System/TimeZoneInfo.AdjustmentRule.cs
index 79fe8db815..3cab928749 100644
--- a/src/mscorlib/src/System/TimeZoneInfo.AdjustmentRule.cs
+++ b/src/mscorlib/src/System/TimeZoneInfo.AdjustmentRule.cs
@@ -132,7 +132,7 @@ namespace System
_dateStart.Year == _dateEnd.Year;
/// <summary>
- /// Helper function that performs all of the validation checks for the actory methods and deserialization callback.
+ /// Helper function that performs all of the validation checks for the factory methods and deserialization callback.
/// </summary>
private static void ValidateAdjustmentRule(
DateTime dateStart,
diff --git a/src/mscorlib/src/System/TimeZoneInfo.StringSerializer.cs b/src/mscorlib/src/System/TimeZoneInfo.StringSerializer.cs
index c52f7307d8..e3e9ddbf58 100644
--- a/src/mscorlib/src/System/TimeZoneInfo.StringSerializer.cs
+++ b/src/mscorlib/src/System/TimeZoneInfo.StringSerializer.cs
@@ -175,7 +175,7 @@ namespace System
}
/// <summary>
- /// Helper function to determine if the passed in string token is allowed to be preceeded by an escape sequence token.
+ /// Helper function to determine if the passed in string token is allowed to be preceded by an escape sequence token.
/// </summary>
private static void VerifyIsEscapableCharacter(char c)
{
diff --git a/src/mscorlib/src/System/TimeZoneInfo.Unix.cs b/src/mscorlib/src/System/TimeZoneInfo.Unix.cs
index 02baadcfe5..921b71cbf7 100644
--- a/src/mscorlib/src/System/TimeZoneInfo.Unix.cs
+++ b/src/mscorlib/src/System/TimeZoneInfo.Unix.cs
@@ -39,7 +39,7 @@ namespace System
_baseUtcOffset = TimeSpan.Zero;
// find the best matching baseUtcOffset and display strings based on the current utcNow value.
- // NOTE: read the display strings from the the tzfile now in case they can't be loaded later
+ // NOTE: read the display strings from the tzfile now in case they can't be loaded later
// from the globalization data.
DateTime utcNow = DateTime.UtcNow;
for (int i = 0; i < dts.Length && dts[i] <= utcNow; i++)
@@ -1115,10 +1115,6 @@ namespace System
/// </returns>
private static bool TZif_ParseMDateRule(string dateRule, out int month, out int week, out DayOfWeek dayOfWeek)
{
- month = 0;
- week = 0;
- dayOfWeek = default(DayOfWeek);
-
if (dateRule[0] == 'M')
{
int firstDotIndex = dateRule.IndexOf('.');
@@ -1127,26 +1123,20 @@ namespace System
int secondDotIndex = dateRule.IndexOf('.', firstDotIndex + 1);
if (secondDotIndex > 0)
{
- string monthString = dateRule.Substring(1, firstDotIndex - 1);
- string weekString = dateRule.Substring(firstDotIndex + 1, secondDotIndex - firstDotIndex - 1);
- string dayString = dateRule.Substring(secondDotIndex + 1);
-
- if (int.TryParse(monthString, out month))
+ if (int.TryParse(dateRule.AsReadOnlySpan().Slice(1, firstDotIndex - 1), out month) &&
+ int.TryParse(dateRule.AsReadOnlySpan().Slice(firstDotIndex + 1, secondDotIndex - firstDotIndex - 1), out week) &&
+ int.TryParse(dateRule.AsReadOnlySpan().Slice(secondDotIndex + 1), out int day))
{
- if (int.TryParse(weekString, out week))
- {
- int day;
- if (int.TryParse(dayString, out day))
- {
- dayOfWeek = (DayOfWeek)day;
- return true;
- }
- }
+ dayOfWeek = (DayOfWeek)day;
+ return true;
}
}
}
}
+ month = 0;
+ week = 0;
+ dayOfWeek = default(DayOfWeek);
return false;
}
diff --git a/src/mscorlib/src/System/TimeZoneInfo.Win32.cs b/src/mscorlib/src/System/TimeZoneInfo.Win32.cs
index 4f740bd355..6de52543c0 100644
--- a/src/mscorlib/src/System/TimeZoneInfo.Win32.cs
+++ b/src/mscorlib/src/System/TimeZoneInfo.Win32.cs
@@ -10,6 +10,7 @@ using System.Security;
using System.Text;
using System.Threading;
using Microsoft.Win32;
+using Microsoft.Win32.SafeHandles;
namespace System
{
@@ -851,15 +852,16 @@ namespace System
private static string TryGetLocalizedNameByNativeResource(string filePath, int resource)
{
using (SafeLibraryHandle handle =
- UnsafeNativeMethods.LoadLibraryEx(filePath, IntPtr.Zero, Win32Native.LOAD_LIBRARY_AS_DATAFILE))
+ Interop.Kernel32.LoadLibraryEx(filePath, IntPtr.Zero, Interop.Kernel32.LOAD_LIBRARY_AS_DATAFILE))
{
if (!handle.IsInvalid)
{
- StringBuilder localizedResource = StringBuilderCache.Acquire(Win32Native.LOAD_STRING_MAX_LENGTH);
- localizedResource.Length = Win32Native.LOAD_STRING_MAX_LENGTH;
+ const int LoadStringMaxLength = 500;
- int result = UnsafeNativeMethods.LoadString(handle, resource,
- localizedResource, localizedResource.Length);
+ StringBuilder localizedResource = StringBuilderCache.Acquire(LoadStringMaxLength);
+
+ int result = Interop.User32.LoadString(handle, resource,
+ localizedResource, LoadStringMaxLength);
if (result != 0)
{
diff --git a/src/mscorlib/src/System/TimeZoneInfo.cs b/src/mscorlib/src/System/TimeZoneInfo.cs
index 623942ce3d..8d61c98040 100644
--- a/src/mscorlib/src/System/TimeZoneInfo.cs
+++ b/src/mscorlib/src/System/TimeZoneInfo.cs
@@ -281,7 +281,7 @@ namespace System
}
/// <summary>
- /// Gets the AdjustmentRule that is immediately preceeding the specified rule.
+ /// Gets the AdjustmentRule that is immediately preceding the specified rule.
/// If the specified rule is the first AdjustmentRule, or it isn't in _adjustmentRules,
/// then the specified rule is returned.
/// </summary>
diff --git a/src/mscorlib/src/System/Type.CoreCLR.cs b/src/mscorlib/src/System/Type.CoreCLR.cs
index 9c443b472a..23a96fa32f 100644
--- a/src/mscorlib/src/System/Type.CoreCLR.cs
+++ b/src/mscorlib/src/System/Type.CoreCLR.cs
@@ -145,41 +145,6 @@ namespace System
}
#endif // FEATURE_COMINTEROP
- internal bool NeedsReflectionSecurityCheck
- {
- get
- {
- if (!IsVisible)
- {
- // Types which are not externally visible require security checks
- return true;
- }
- else if (IsSecurityCritical && !IsSecuritySafeCritical)
- {
- // Critical types require security checks
- return true;
- }
- else if (IsGenericType)
- {
- // If any of the generic arguments to this type require a security check, then this type
- // also requires one.
- foreach (Type genericArgument in GetGenericArguments())
- {
- if (genericArgument.NeedsReflectionSecurityCheck)
- {
- return true;
- }
- }
- }
- else if (IsArray || IsPointer)
- {
- return GetElementType().NeedsReflectionSecurityCheck;
- }
-
- return false;
- }
- }
-
// This is only ever called on RuntimeType objects.
internal string FormatTypeName()
{
diff --git a/src/mscorlib/src/System/TypeLoadException.cs b/src/mscorlib/src/System/TypeLoadException.cs
index 5e748a6c58..840d97702b 100644
--- a/src/mscorlib/src/System/TypeLoadException.cs
+++ b/src/mscorlib/src/System/TypeLoadException.cs
@@ -27,19 +27,19 @@ namespace System
public TypeLoadException()
: base(SR.Arg_TypeLoadException)
{
- HResult = __HResults.COR_E_TYPELOAD;
+ HResult = HResults.COR_E_TYPELOAD;
}
public TypeLoadException(String message)
: base(message)
{
- HResult = __HResults.COR_E_TYPELOAD;
+ HResult = HResults.COR_E_TYPELOAD;
}
public TypeLoadException(String message, Exception inner)
: base(message, inner)
{
- HResult = __HResults.COR_E_TYPELOAD;
+ HResult = HResults.COR_E_TYPELOAD;
}
public override String Message
@@ -91,7 +91,7 @@ namespace System
int resourceId)
: base(null)
{
- HResult = __HResults.COR_E_TYPELOAD;
+ HResult = HResults.COR_E_TYPELOAD;
ClassName = className;
AssemblyName = assemblyName;
MessageArg = messageArg;
diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h
index a07d665b1f..4ac91db0cf 100644
--- a/src/pal/inc/pal.h
+++ b/src/pal/inc/pal.h
@@ -501,6 +501,11 @@ PAL_InitializeDebug(
void);
PALIMPORT
+void
+PALAPI
+PAL_IgnoreProfileSignal(int signalNum);
+
+PALIMPORT
HINSTANCE
PALAPI
PAL_RegisterModule(
@@ -586,7 +591,6 @@ CharNextExA(
#define MB_TOPMOST 0x00040000L
#define MB_NOFOCUS 0x00008000L
-#define MB_SETFOREGROUND 0x00010000L
#define MB_DEFAULT_DESKTOP_ONLY 0x00020000L
// Note: this is the NT 4.0 and greater value.
@@ -621,34 +625,6 @@ MessageBoxW(
#define MessageBox MessageBoxA
#endif
-/***************** wincon.h Entrypoints **********************************/
-
-#define CTRL_C_EVENT 0
-#define CTRL_BREAK_EVENT 1
-#define CTRL_CLOSE_EVENT 2
-// 3 is reserved!
-// 4 is reserved!
-#define CTRL_LOGOFF_EVENT 5
-#define CTRL_SHUTDOWN_EVENT 6
-
-typedef
-BOOL
-(PALAPI *PHANDLER_ROUTINE)(
- DWORD CtrlType
- );
-
-#ifndef CORECLR
-PALIMPORT
-BOOL
-PALAPI
-GenerateConsoleCtrlEvent(
- IN DWORD dwCtrlEvent,
- IN DWORD dwProcessGroupId
- );
-#endif // !CORECLR
-
-//end wincon.h Entrypoints
-
// From win32.h
#ifndef _CRTIMP
#ifdef __llvm__
@@ -659,12 +635,6 @@ GenerateConsoleCtrlEvent(
#endif // _CRTIMP
/******************* winbase.h Entrypoints and defines ************************/
-PALIMPORT
-BOOL
-PALAPI
-AreFileApisANSI(
- VOID);
-
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
@@ -730,28 +700,6 @@ CreateFileW(
#define CreateFile CreateFileA
#endif
-PALIMPORT
-BOOL
-PALAPI
-LockFile(
- IN HANDLE hFile,
- IN DWORD dwFileOffsetLow,
- IN DWORD dwFileOffsetHigh,
- IN DWORD nNumberOfBytesToLockLow,
- IN DWORD nNumberOfBytesToLockHigh
- );
-
-PALIMPORT
-BOOL
-PALAPI
-UnlockFile(
- IN HANDLE hFile,
- IN DWORD dwFileOffsetLow,
- IN DWORD dwFileOffsetHigh,
- IN DWORD nNumberOfBytesToUnlockLow,
- IN DWORD nNumberOfBytesToUnlockHigh
- );
-
PALIMPORT
DWORD
@@ -801,19 +749,6 @@ DeleteFileW(
-PALIMPORT
-BOOL
-PALAPI
-MoveFileW(
- IN LPCWSTR lpExistingFileName,
- IN LPCWSTR lpNewFileName);
-
-#ifdef UNICODE
-#define MoveFile MoveFileW
-#else
-#define MoveFile MoveFileA
-#endif
-
#define MOVEFILE_REPLACE_EXISTING 0x00000001
#define MOVEFILE_COPY_ALLOWED 0x00000002
@@ -1085,24 +1020,6 @@ CompareFileTime(
IN CONST FILETIME *lpFileTime2);
PALIMPORT
-BOOL
-PALAPI
-SetFileTime(
- IN HANDLE hFile,
- IN CONST FILETIME *lpCreationTime,
- IN CONST FILETIME *lpLastAccessTime,
- IN CONST FILETIME *lpLastWriteTime);
-
-PALIMPORT
-BOOL
-PALAPI
-GetFileTime(
- IN HANDLE hFile,
- OUT LPFILETIME lpCreationTime,
- OUT LPFILETIME lpLastAccessTime,
- OUT LPFILETIME lpLastWriteTime);
-
-PALIMPORT
VOID
PALAPI
GetSystemTimeAsFileTime(
@@ -1132,15 +1049,6 @@ FileTimeToSystemTime(
IN CONST FILETIME *lpFileTime,
OUT LPSYSTEMTIME lpSystemTime);
-PALIMPORT
-BOOL
-PALAPI
-FileTimeToDosDateTime(
- IN CONST FILETIME *lpFileTime,
- OUT LPWORD lpFatDate,
- OUT LPWORD lpFatTime
- );
-
PALIMPORT
@@ -1149,24 +1057,6 @@ PALAPI
FlushFileBuffers(
IN HANDLE hFile);
-#define FILE_TYPE_UNKNOWN 0x0000
-#define FILE_TYPE_DISK 0x0001
-#define FILE_TYPE_CHAR 0x0002
-#define FILE_TYPE_PIPE 0x0003
-#define FILE_TYPE_REMOTE 0x8000
-
-PALIMPORT
-DWORD
-PALAPI
-GetFileType(
- IN HANDLE hFile);
-
-PALIMPORT
-UINT
-PALAPI
-GetConsoleCP(
- VOID);
-
PALIMPORT
UINT
PALAPI
@@ -1267,31 +1157,6 @@ SetCurrentDirectoryW(
#define SetCurrentDirectory SetCurrentDirectoryA
#endif
-// maximum length of the NETBIOS name (not including NULL)
-#define MAX_COMPUTERNAME_LENGTH 15
-
-// maximum length of the username (not including NULL)
-#define UNLEN 256
-
-PALIMPORT
-BOOL
-PALAPI
-GetUserNameW(
- OUT LPWSTR lpBuffer, // address of name buffer
- IN OUT LPDWORD nSize ); // address of size of name buffer
-
-PALIMPORT
-BOOL
-PALAPI
-GetComputerNameW(
- OUT LPWSTR lpBuffer, // address of name buffer
- IN OUT LPDWORD nSize); // address of size of name buffer
-
-#ifdef UNICODE
-#define GetUserName GetUserNameW
-#define GetComputerName GetComputerNameW
-#endif // UNICODE
-
PALIMPORT
HANDLE
PALAPI
@@ -1626,19 +1491,6 @@ WaitForMultipleObjectsEx(
IN DWORD dwMilliseconds,
IN BOOL bAlertable);
-PALIMPORT
-RHANDLE
-PALAPI
-PAL_LocalHandleToRemote(
- IN HANDLE hLocal);
-
-PALIMPORT
-HANDLE
-PALAPI
-PAL_RemoteHandleToLocal(
- IN RHANDLE hRemote);
-
-
#define DUPLICATE_CLOSE_SOURCE 0x00000001
#define DUPLICATE_SAME_ACCESS 0x00000002
@@ -1697,13 +1549,6 @@ ExitThread(
IN DWORD dwExitCode);
PALIMPORT
-BOOL
-PALAPI
-GetExitCodeThread(
- IN HANDLE hThread,
- IN LPDWORD lpExitCode);
-
-PALIMPORT
DWORD
PALAPI
ResumeThread(
@@ -2569,18 +2414,20 @@ PALAPI
PAL_GetWorkingSetSize(size_t* val);
PALIMPORT
+BOOL
+PALAPI
+PAL_GetCpuLimit(UINT* val);
+
+PALIMPORT
size_t
PALAPI
PAL_GetLogicalProcessorCacheSizeFromOS(VOID);
-typedef BOOL (*ReadMemoryWordCallback)(SIZE_T address, SIZE_T *value);
+typedef BOOL(*UnwindReadMemoryCallback)(PVOID address, PVOID buffer, SIZE_T size);
PALIMPORT BOOL PALAPI PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers);
-PALIMPORT BOOL PALAPI PAL_VirtualUnwindOutOfProc(CONTEXT *context,
- KNONVOLATILE_CONTEXT_POINTERS *contextPointers,
- DWORD pid,
- ReadMemoryWordCallback readMemCallback);
+PALIMPORT BOOL PALAPI PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback);
#define GetLogicalProcessorCacheSizeFromOS PAL_GetLogicalProcessorCacheSizeFromOS
@@ -2728,13 +2575,6 @@ MapViewOfFileEx(
IN DWORD dwFileOffsetLow,
IN SIZE_T dwNumberOfBytesToMap,
IN LPVOID lpBaseAddress);
-
-PALIMPORT
-BOOL
-PALAPI
-FlushViewOfFile(
- IN LPVOID lpBaseAddress,
- IN SIZE_T dwNumberOfBytesToFlush);
PALIMPORT
BOOL
@@ -2953,13 +2793,6 @@ RtlMoveMemory(
IN CONST VOID *Source,
IN SIZE_T Length);
-PALIMPORT
-VOID
-PALAPI
-RtlZeroMemory(
- IN PVOID Destination,
- IN SIZE_T Length);
-
#define MoveMemory memmove
#define CopyMemory memcpy
#define FillMemory(Destination,Length,Fill) memset((Destination),(Fill),(Length))
@@ -4245,6 +4078,52 @@ BitScanForward64(
return bRet;
}
+// Define BitScanReverse64 and BitScanReverse
+// Per MSDN, BitScanReverse64 or BitScanReverse will search the mask data from most significant bit (MSB)
+// to least significant bit (LSB) for a set bit (1).
+// If one is found, its bit position is returned in the out PDWORD argument and 1 is returned.
+// Otherwise, 0 is returned.
+//
+// On GCC, the equivalent function is __builtin_clzll or __builtin_clz. It returns 1+index of the most
+// significant set bit, or undefined result if mask is zero.
+EXTERN_C
+PALIMPORT
+inline
+unsigned char
+PALAPI
+BitScanReverse(
+ IN OUT PDWORD Index,
+ IN UINT qwMask)
+{
+ unsigned char bRet = FALSE;
+ if (qwMask != 0)
+ {
+ *Index = (UINT) (8 * sizeof (UINT) - __builtin_clz(qwMask) - 1);
+ bRet = TRUE;
+ }
+
+ return bRet;
+}
+
+EXTERN_C
+PALIMPORT
+inline
+unsigned char
+PALAPI
+BitScanReverse64(
+ IN OUT PDWORD Index,
+ IN UINT64 qwMask)
+{
+ unsigned char bRet = FALSE;
+ if (qwMask != 0)
+ {
+ *Index = (UINT) (8 * sizeof (UINT64) - __builtin_clzll(qwMask) - 1);
+ bRet = TRUE;
+ }
+
+ return bRet;
+}
+
/*++
Function:
InterlockedIncrement
@@ -4819,20 +4698,6 @@ GetSystemInfo(
PALIMPORT
BOOL
PALAPI
-GetDiskFreeSpaceW(
- LPCWSTR lpDirectoryName,
- LPDWORD lpSectorsPerCluster,
- LPDWORD lpBytesPerSector,
- LPDWORD lpNumberOfFreeClusters,
- LPDWORD lpTotalNumberOfClusters);
-
-#ifdef UNICODE
-#define GetDiskFreeSpace GetDiskFreeSpaceW
-#endif
-
-PALIMPORT
-BOOL
-PALAPI
CreatePipe(
OUT PHANDLE hReadPipe,
OUT PHANDLE hWritePipe,
@@ -4840,33 +4705,6 @@ CreatePipe(
IN DWORD nSize
);
-PALIMPORT
-BOOL
-PALAPI
-DeregisterEventSource (
- IN HANDLE hEventLog
- );
-
-PALIMPORT
-HANDLE
-PALAPI
-RegisterEventSourceA (
- IN OPTIONAL LPCSTR lpUNCServerName,
- IN LPCSTR lpSourceName
- );
-PALIMPORT
-HANDLE
-PALAPI
-RegisterEventSourceW (
- IN OPTIONAL LPCWSTR lpUNCServerName,
- IN LPCWSTR lpSourceName
- );
-#ifdef UNICODE
-#define RegisterEventSource RegisterEventSourceW
-#else
-#define RegisterEventSource RegisterEventSourceA
-#endif // !UNICODE
-
//
// NUMA related APIs
//
@@ -5041,40 +4879,6 @@ GetProcessAffinityMask(
#define EVENTLOG_AUDIT_FAILURE 0x0010
PALIMPORT
-BOOL
-PALAPI
-ReportEventA (
- IN HANDLE hEventLog,
- IN WORD wType,
- IN WORD wCategory,
- IN DWORD dwEventID,
- IN OPTIONAL PSID lpUserSid,
- IN WORD wNumStrings,
- IN DWORD dwDataSize,
- IN OPTIONAL LPCSTR *lpStrings,
- IN OPTIONAL LPVOID lpRawData
- );
-PALIMPORT
-BOOL
-PALAPI
-ReportEventW (
- IN HANDLE hEventLog,
- IN WORD wType,
- IN WORD wCategory,
- IN DWORD dwEventID,
- IN OPTIONAL PSID lpUserSid,
- IN WORD wNumStrings,
- IN DWORD dwDataSize,
- IN OPTIONAL LPCWSTR *lpStrings,
- IN OPTIONAL LPVOID lpRawData
- );
-#ifdef UNICODE
-#define ReportEvent ReportEventW
-#else
-#define ReportEvent ReportEventA
-#endif // !UNICODE
-
-PALIMPORT
HRESULT
PALAPI
CoCreateGuid(OUT GUID * pguid);
@@ -5287,7 +5091,6 @@ PALIMPORT char * __cdecl _strlwr(char *);
PALIMPORT int __cdecl _stricmp(const char *, const char *);
PALIMPORT int __cdecl vsprintf_s(char *, size_t, const char *, va_list);
PALIMPORT char * __cdecl _gcvt_s(char *, int, double, int);
-PALIMPORT char * __cdecl _ecvt(double, int, int *, int *);
PALIMPORT int __cdecl __iscsym(int);
PALIMPORT unsigned char * __cdecl _mbsinc(const unsigned char *);
PALIMPORT unsigned char * __cdecl _mbsninc(const unsigned char *, size_t);
diff --git a/src/pal/inc/palprivate.h b/src/pal/inc/palprivate.h
index 554a5028ad..ce1a9a99a1 100644
--- a/src/pal/inc/palprivate.h
+++ b/src/pal/inc/palprivate.h
@@ -61,13 +61,6 @@ DeleteFileA(
PALIMPORT
BOOL
PALAPI
-MoveFileA(
- IN LPCSTR lpExistingFileName,
- IN LPCSTR lpNewFileName);
-
-PALIMPORT
-BOOL
-PALAPI
MoveFileExA(
IN LPCSTR lpExistingFileName,
IN LPCSTR lpNewFileName,
diff --git a/src/pal/inc/unixasmmacrosarm.inc b/src/pal/inc/unixasmmacrosarm.inc
index 76ea5eb93c..e4a3658b6c 100644
--- a/src/pal/inc/unixasmmacrosarm.inc
+++ b/src/pal/inc/unixasmmacrosarm.inc
@@ -72,7 +72,6 @@ C_FUNC(\Name\()_End):
.macro free_stack Size
add sp, sp, (\Size)
- .pad #-(\Size)
.endm
.macro POP_CALLEE_SAVED_REGISTERS
diff --git a/src/pal/prebuilt/idl/sospriv_i.cpp b/src/pal/prebuilt/idl/sospriv_i.cpp
index 3584b761d2..7c0cd69bba 100644
--- a/src/pal/prebuilt/idl/sospriv_i.cpp
+++ b/src/pal/prebuilt/idl/sospriv_i.cpp
@@ -88,6 +88,9 @@ MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface3,0xB08C5CDC,0xFD8A,0x49C5,0xAB,0x38,0
MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface4,0x74B9D34C,0xA612,0x4B07,0x93,0xDD,0x54,0x62,0x17,0x8F,0xCE,0x11);
+
+MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface5,0x127d6abe,0x6c86,0x4e48,0x8e,0x7b,0x22,0x07,0x81,0xc5,0x81,0x01);
+
#undef MIDL_DEFINE_GUID
#ifdef __cplusplus
diff --git a/src/pal/prebuilt/inc/mscoree.h b/src/pal/prebuilt/inc/mscoree.h
index 29f7b261d3..45b8864d69 100644
--- a/src/pal/prebuilt/inc/mscoree.h
+++ b/src/pal/prebuilt/inc/mscoree.h
@@ -317,7 +317,7 @@ enum __MIDL___MIDL_itf_mscoree_0000_0000_0002
STARTUP_ARM = 0x400000,
STARTUP_SINGLE_APPDOMAIN = 0x800000,
STARTUP_APPX_APP_MODEL = 0x1000000,
- STARTUP_DISABLE_RANDOMIZED_STRING_HASHING = 0x2000000
+ STARTUP_DISABLE_RANDOMIZED_STRING_HASHING = 0x2000000 // not supported
} STARTUP_FLAGS;
typedef /* [public] */
diff --git a/src/pal/prebuilt/inc/sospriv.h b/src/pal/prebuilt/inc/sospriv.h
index ae881659e5..3b810cc9b6 100644
--- a/src/pal/prebuilt/inc/sospriv.h
+++ b/src/pal/prebuilt/inc/sospriv.h
@@ -2186,6 +2186,92 @@ EXTERN_C const IID IID_ISOSDacInterface4;
#endif /* __ISOSDacInterface4_INTERFACE_DEFINED__ */
+#ifndef __ISOSDacInterface5_INTERFACE_DEFINED__
+#define __ISOSDacInterface5_INTERFACE_DEFINED__
+
+/* interface ISOSDacInterface5 */
+/* [uuid][local][object] */
+
+
+EXTERN_C const IID IID_ISOSDacInterface5;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+
+ MIDL_INTERFACE("127d6abe-6c86-4e48-8e7b-220781c58101")
+ ISOSDacInterface5 : public IUnknown
+ {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE GetTieredVersions(
+ CLRDATA_ADDRESS methodDesc,
+ int rejitId,
+ struct DacpTieredVersionData *nativeCodeAddrs,
+ int cNativeCodeAddrs,
+ int *pcNativeCodeAddrs) = 0;
+
+ };
+
+
+#else /* C style interface */
+
+ typedef struct ISOSDacInterface5Vtbl
+ {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ ISOSDacInterface5 * This,
+ /* [in] */ REFIID riid,
+ /* [annotation][iid_is][out] */
+ _COM_Outptr_ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ ISOSDacInterface5 * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ ISOSDacInterface5 * This);
+
+ HRESULT ( STDMETHODCALLTYPE *GetTieredVersions )(
+ ISOSDacInterface5 * This,
+ CLRDATA_ADDRESS methodDesc,
+ int rejitId,
+ CLRDATA_ADDRESS *nativeCodeAddrs,
+ int cNativeCodeAddrs);
+
+ END_INTERFACE
+ } ISOSDacInterface5Vtbl;
+
+ interface ISOSDacInterface5
+ {
+ CONST_VTBL struct ISOSDacInterface5Vtbl *lpVtbl;
+ };
+
+
+
+#ifdef COBJMACROS
+
+
+#define ISOSDacInterface5_QueryInterface(This,riid,ppvObject) \
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+
+#define ISOSDacInterface5_AddRef(This) \
+ ( (This)->lpVtbl -> AddRef(This) )
+
+#define ISOSDacInterface5_Release(This) \
+ ( (This)->lpVtbl -> Release(This) )
+
+
+#define ISOSDacInterface5_GetTieredVersions(This,methodDesc,rejitId,nativeCodeAddrs,cNativeCodeAddrs) \
+ ( (This)->lpVtbl -> GetTieredVersions(This,methodDesc,rejitId,nativeCodeAddrs,cNativeCodeAddrs) )
+
+#endif /* COBJMACROS */
+
+
+#endif /* C style interface */
+
+
+
+
+#endif /* __ISOSDacInterface5_INTERFACE_DEFINED__ */
+
/* Additional Prototypes for ALL interfaces */
/* end of Additional Prototypes */
diff --git a/src/pal/src/CMakeLists.txt b/src/pal/src/CMakeLists.txt
index b8a9fe9e46..370866ca9e 100644
--- a/src/pal/src/CMakeLists.txt
+++ b/src/pal/src/CMakeLists.txt
@@ -147,8 +147,8 @@ set(SOURCES
debug/debug.cpp
exception/seh.cpp
exception/signal.cpp
+ exception/remote-unwind.cpp
file/directory.cpp
- file/disk.cpp
file/file.cpp
file/filetime.cpp
file/find.cpp
@@ -174,7 +174,6 @@ set(SOURCES
misc/error.cpp
misc/errorstrings.cpp
misc/fmtmessage.cpp
- misc/identity.cpp
misc/miscpalapi.cpp
misc/msgbox.cpp
misc/strutil.cpp
diff --git a/src/pal/src/arch/arm/callsignalhandlerwrapper.S b/src/pal/src/arch/arm/callsignalhandlerwrapper.S
index 266e4fdfe9..b9398d6d63 100644
--- a/src/pal/src/arch/arm/callsignalhandlerwrapper.S
+++ b/src/pal/src/arch/arm/callsignalhandlerwrapper.S
@@ -18,12 +18,27 @@ C_FUNC(SignalHandlerWorkerReturnOffset\Alignment):
// address set to SignalHandlerWorkerReturn during SIGSEGV handling.
// It enables the unwinder to unwind stack from the handling code to the actual failure site.
NESTED_ENTRY CallSignalHandlerWrapper\Alignment, _TEXT, NoHandler
- sub sp, sp, #(8 + \Alignment) // red zone + alignment
- stmfd sp!, {r7, lr}
+#ifndef __linux__
+__StackAllocationSize = (8 + 4 + \Alignment) // red zone + alignment
+ alloc_stack __StackAllocationSize
+ PROLOG_PUSH "{r7, r11, lr}"
bl EXTERNAL_C_FUNC(signal_handler_worker)
LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment):
- ldmfd sp!, {r7, lr}
+ EPILOG_POP "{r7, r11, lr}"
+ free_stack __StackAllocationSize
bx lr
+#else
+ // This unwind information is needed for lldb gdb doesn't use it and tries
+ // to read all registers from $sp + 12
+ .save {r0-r15}
+ .pad #12
+ bl EXTERNAL_C_FUNC(signal_handler_worker)
+LOCAL_LABEL(SignalHandlerWorkerReturn\Alignment):
+ // Following instruction are needed to say gdb that this frame is SIGTRAMP_FRAME
+ // and it can restore all registers from stack
+ mov.w r7, #119
+ svc 0
+#endif
NESTED_END CallSignalHandlerWrapper\Alignment, _TEXT
.endm
diff --git a/src/pal/src/arch/arm/signalhandlerhelper.cpp b/src/pal/src/arch/arm/signalhandlerhelper.cpp
index e1ad460905..4efecfc1c1 100644
--- a/src/pal/src/arch/arm/signalhandlerhelper.cpp
+++ b/src/pal/src/arch/arm/signalhandlerhelper.cpp
@@ -45,11 +45,22 @@ void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context,
// preserve 8 bytes long red zone and align stack pointer
size_t* sp = (size_t*)ALIGN_DOWN(faultSp - 8, 8);
+#ifndef __linux__
+ size_t cpsr = (size_t)MCREG_Cpsr(ucontext->uc_mcontext);
+
// Build fake stack frame to enable the stack unwinder to unwind from signal_handler_worker to the faulting instruction
- // pushed LR
- *--sp = (size_t)MCREG_Pc(ucontext->uc_mcontext);
+ // align
+ --sp;
+ // pushed LR with correct mode bit
+ *--sp = (size_t)MCREG_Pc(ucontext->uc_mcontext) | ((cpsr & (1 << 5)) >> 5);
// pushed frame pointer
+ *--sp = (size_t)MCREG_R11(ucontext->uc_mcontext);
*--sp = (size_t)MCREG_R7(ucontext->uc_mcontext);
+#else
+ size_t size = ALIGN_UP(sizeof(ucontext->uc_mcontext), 8);
+ sp -= size / sizeof(size_t);
+ *(sigcontext *)sp = ucontext->uc_mcontext;
+#endif
// Switch the current context to the signal_handler_worker and the original stack
CONTEXT context2;
diff --git a/src/pal/src/config.h.in b/src/pal/src/config.h.in
index 03513a1264..0052880d68 100644
--- a/src/pal/src/config.h.in
+++ b/src/pal/src/config.h.in
@@ -25,7 +25,6 @@
#cmakedefine01 HAVE_PTHREAD_NP_H
#cmakedefine01 HAVE_KQUEUE
-#cmakedefine01 HAVE_GETPWUID_R
#cmakedefine01 HAVE_PTHREAD_SUSPEND
#cmakedefine01 HAVE_PTHREAD_SUSPEND_NP
#cmakedefine01 HAVE_PTHREAD_CONTINUE
@@ -78,6 +77,7 @@
#cmakedefine01 HAVE_PT_REGS
#cmakedefine01 HAVE_GREGSET_T
#cmakedefine01 HAVE___GREGSET_T
+#cmakedefine01 HAVE_FPSTATE_GLIBC_RESERVED1
#cmakedefine01 HAVE_SIGINFO_T
#cmakedefine01 HAVE_UCONTEXT_T
#cmakedefine01 HAVE_PTHREAD_RWLOCK_T
diff --git a/src/pal/src/configure.cmake b/src/pal/src/configure.cmake
index d305081f87..b797caf55f 100644
--- a/src/pal/src/configure.cmake
+++ b/src/pal/src/configure.cmake
@@ -51,7 +51,6 @@ check_include_files(sys/sysctl.h HAVE_SYS_SYSCTL_H)
check_include_files(gnu/lib-names.h HAVE_GNU_LIBNAMES_H)
check_function_exists(kqueue HAVE_KQUEUE)
-check_function_exists(getpwuid_r HAVE_GETPWUID_R)
check_library_exists(c sched_getaffinity "" HAVE_SCHED_GETAFFINITY)
check_library_exists(pthread pthread_create "" HAVE_LIBPTHREAD)
@@ -137,6 +136,7 @@ check_struct_has_member ("struct stat" st_atimensec "sys/types.h;sys/stat.h" HAV
check_struct_has_member ("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF)
check_struct_has_member ("ucontext_t" uc_mcontext.gregs[0] ucontext.h HAVE_GREGSET_T)
check_struct_has_member ("ucontext_t" uc_mcontext.__gregs[0] ucontext.h HAVE___GREGSET_T)
+check_struct_has_member ("ucontext_t" uc_mcontext.fpregs->__glibc_reserved1[0] ucontext.h HAVE_FPSTATE_GLIBC_RESERVED1)
check_struct_has_member ("struct sysinfo" mem_unit "sys/sysinfo.h" HAVE_SYSINFO_WITH_MEM_UNIT)
set(CMAKE_EXTRA_INCLUDE_FILES machine/reg.h)
diff --git a/src/pal/src/cruntime/filecrt.cpp b/src/pal/src/cruntime/filecrt.cpp
index 48079b309c..182a42d5cd 100644
--- a/src/pal/src/cruntime/filecrt.cpp
+++ b/src/pal/src/cruntime/filecrt.cpp
@@ -93,7 +93,7 @@ _open_osfhandle( INT_PTR osfhandle, int flags )
if (NO_ERROR == palError)
{
- if ('\0' != pLocalData->unix_filename[0])
+ if (NULL != pLocalData->unix_filename)
{
nRetVal = InternalOpen(pLocalData->unix_filename, openFlags);
}
diff --git a/src/pal/src/cruntime/misctls.cpp b/src/pal/src/cruntime/misctls.cpp
index 2df32fe115..dd1e5d946d 100644
--- a/src/pal/src/cruntime/misctls.cpp
+++ b/src/pal/src/cruntime/misctls.cpp
@@ -131,167 +131,4 @@ done:
UINT GetExponent(double d)
{
return (*((UINT*)&d + 1) >> 20) & 0x000007ff;
-}
-
-/**
-Function:
-
- _ecvt
-
-See MSDN for more information.
-
-NOTES:
- There is a difference between PAL _ecvt and Win32 _ecvt.
-
- If Window's _ecvt receives a double 0.000000000000000000005, and count 50
- the result is "49999999999999998000000000000000000000000000000000"
-
- Under BSD the same call will result in :
- 49999999999999998021734900744965462766153934333829
-
- The difference is due to the difference between BSD and Win32 sprintf.
-
---*/
-char * __cdecl
-_ecvt( double value, int count, int * dec, int * sign )
-{
- PERF_ENTRY(_ecvt);
- ENTRY( "_ecvt( value=%.30g, count=%d, dec=%p, sign=%p )\n",
- value, count, dec, sign );
-
- _ASSERTE(dec != nullptr && sign != nullptr);
- CPalThread *pThread = InternalGetCurrentThread();
- LPSTR lpStartOfReturnBuffer = pThread->crtInfo.ECVTBuffer;
-
- if (count > ECVT_MAX_COUNT_SIZE)
- {
- count = ECVT_MAX_COUNT_SIZE;
- }
-
- // the caller of _ecvt should already checked the Infinity and NAN values
- _ASSERTE(GetExponent(value) != 0x7ff);
-
- CHAR TempBuffer[ECVT_MAX_BUFFER_SIZE];
-
- *dec = *sign = 0;
-
- if (value < 0.0)
- {
- *sign = 1;
- }
-
- {
- // we have issue #10290 tracking fixing the sign of 0.0 across the platforms
- if (value == 0.0)
- {
- for (int j = 0; j < count; j++)
- {
- lpStartOfReturnBuffer[j] = '0';
- }
- lpStartOfReturnBuffer[count] = '\0';
- goto done;
- }
-
- int tempBufferLength = snprintf(TempBuffer, ECVT_MAX_BUFFER_SIZE, "%.40e", value);
- _ASSERTE(tempBufferLength > 0 && ECVT_MAX_BUFFER_SIZE > tempBufferLength);
-
- //
- // Calculate the exponent value
- //
-
- int exponentIndex = strrchr(TempBuffer, 'e') - TempBuffer;
- _ASSERTE(exponentIndex > 0 && (exponentIndex < tempBufferLength - 1));
-
- int i = exponentIndex + 1;
- int exponentSign = 1;
- if (TempBuffer[i] == '-')
- {
- exponentSign = -1;
- i++;
- }
- else if (TempBuffer[i] == '+')
- {
- i++;
- }
-
- int exponentValue = 0;
- while (i < tempBufferLength)
- {
- _ASSERTE(TempBuffer[i] >= '0' && TempBuffer[i] <= '9');
- exponentValue = exponentValue * 10 + ((BYTE) TempBuffer[i] - (BYTE) '0');
- i++;
- }
- exponentValue *= exponentSign;
-
- //
- // Determine decimal location.
- //
-
- if (exponentValue == 0)
- {
- *dec = 1;
- }
- else
- {
- *dec = exponentValue + 1;
- }
-
- //
- // Copy the string from the temp buffer upto precision characters, removing the sign, and decimal as required.
- //
-
- i = 0;
- int mantissaIndex = 0;
- while (i < count && mantissaIndex < exponentIndex)
- {
- if (TempBuffer[mantissaIndex] >= '0' && TempBuffer[mantissaIndex] <= '9')
- {
- lpStartOfReturnBuffer[i] = TempBuffer[mantissaIndex];
- i++;
- }
- mantissaIndex++;
- }
-
- while (i < count)
- {
- lpStartOfReturnBuffer[i] = '0'; // append zeros as needed
- i++;
- }
-
- lpStartOfReturnBuffer[i] = '\0';
-
- //
- // Round if needed
- //
-
- if (mantissaIndex >= exponentIndex || TempBuffer[mantissaIndex] < '5')
- {
- goto done;
- }
-
- i = count - 1;
- while (lpStartOfReturnBuffer[i] == '9' && i > 0)
- {
- lpStartOfReturnBuffer[i] = '0';
- i--;
- }
-
- if (i == 0 && lpStartOfReturnBuffer[i] == '9')
- {
- lpStartOfReturnBuffer[i] = '1';
- (*dec)++;
- }
- else
- {
- lpStartOfReturnBuffer[i]++;
- }
- }
-
-done:
-
- LOGEXIT( "_ecvt returning %p (%s)\n", lpStartOfReturnBuffer , lpStartOfReturnBuffer );
- PERF_EXIT(_ecvt);
-
- return lpStartOfReturnBuffer;
-}
-
+} \ No newline at end of file
diff --git a/src/pal/src/cruntime/wchar.cpp b/src/pal/src/cruntime/wchar.cpp
index 3de065e361..5b466960a6 100644
--- a/src/pal/src/cruntime/wchar.cpp
+++ b/src/pal/src/cruntime/wchar.cpp
@@ -1194,16 +1194,22 @@ PAL_wcsstr(
i = 0;
while (1)
{
- if (*(string + i) == 0 || *(strCharSet + i) == 0)
+ if (*(strCharSet + i) == 0)
{
ret = (wchar_16 *) string;
goto leave;
}
- if (*(string + i) != *(strCharSet + i))
+ else if (*(string + i) == 0)
+ {
+ ret = NULL;
+ goto leave;
+ }
+ else if (*(string + i) != *(strCharSet + i))
{
break;
}
- i++;
+
+ i++;
}
string++;
}
diff --git a/src/pal/src/exception/remote-unwind.cpp b/src/pal/src/exception/remote-unwind.cpp
new file mode 100644
index 0000000000..18733ded52
--- /dev/null
+++ b/src/pal/src/exception/remote-unwind.cpp
@@ -0,0 +1,1086 @@
+// 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.
+
+/*++
+
+Module Name:
+
+ remote-unwind.cpp
+
+Abstract:
+
+ Implementation of out of context unwind using libunwind8
+ remote unwind API.
+
+This file contains code based on libunwind8
+
+Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+--*/
+
+#include "config.h"
+#include "pal/palinternal.h"
+#include "pal/dbgmsg.h"
+#include "pal/critsect.h"
+#include "pal/debug.h"
+#include "pal_endian.h"
+#include "pal.h"
+#include <dlfcn.h>
+
+#if HAVE_LIBUNWIND_H
+#ifndef __linux__
+#define UNW_LOCAL_ONLY
+#endif // !__linux__
+#include <libunwind.h>
+#endif // HAVE_LIBUNWIND_H
+
+SET_DEFAULT_DEBUG_CHANNEL(EXCEPT);
+
+// Only used on the AMD64 build
+#if defined(_AMD64_) && defined(HAVE_UNW_GET_ACCESSORS)
+
+#include <elf.h>
+#include <link.h>
+
+#define Ehdr ElfW(Ehdr)
+#define Phdr ElfW(Phdr)
+#define Shdr ElfW(Shdr)
+#define Nhdr ElfW(Nhdr)
+#define Dyn ElfW(Dyn)
+
+extern void UnwindContextToWinContext(unw_cursor_t *cursor, CONTEXT *winContext);
+extern void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext, KNONVOLATILE_CONTEXT_POINTERS *contextPointers);
+
+typedef struct _libunwindInfo
+{
+ SIZE_T BaseAddress;
+ CONTEXT *Context;
+ UnwindReadMemoryCallback ReadMemory;
+} libunwindInfo;
+
+#define DW_EH_VERSION 1
+
+// DWARF Pointer-Encoding (PEs).
+//
+// Pointer-Encodings were invented for the GCC exception-handling
+// support for C++, but they represent a rather generic way of
+// describing the format in which an address/pointer is stored.
+// The Pointer-Encoding format is partially documented in Linux Base
+// Spec v1.3 (http://www.linuxbase.org/spec/).
+
+#define DW_EH_PE_FORMAT_MASK 0x0f // format of the encoded value
+#define DW_EH_PE_APPL_MASK 0x70 // how the value is to be applied
+#define DW_EH_PE_indirect 0x80 // Flag bit. If set, the resulting pointer is the
+ // address of the word that contains the final address
+// Pointer-encoding formats
+#define DW_EH_PE_omit 0xff
+#define DW_EH_PE_ptr 0x00 // pointer-sized unsigned value
+#define DW_EH_PE_uleb128 0x01 // unsigned LE base-128 value
+#define DW_EH_PE_udata2 0x02 // unsigned 16-bit value
+#define DW_EH_PE_udata4 0x03 // unsigned 32-bit value
+#define DW_EH_PE_udata8 0x04 // unsigned 64-bit value
+#define DW_EH_PE_sleb128 0x09 // signed LE base-128 value
+#define DW_EH_PE_sdata2 0x0a // signed 16-bit value
+#define DW_EH_PE_sdata4 0x0b // signed 32-bit value
+#define DW_EH_PE_sdata8 0x0c // signed 64-bit value
+
+// Pointer-encoding application
+#define DW_EH_PE_absptr 0x00 // absolute value
+#define DW_EH_PE_pcrel 0x10 // rel. to addr. of encoded value
+#define DW_EH_PE_textrel 0x20 // text-relative (GCC-specific???)
+#define DW_EH_PE_datarel 0x30 // data-relative
+
+// The following are not documented by LSB v1.3, yet they are used by
+// GCC, presumably they aren't documented by LSB since they aren't
+// used on Linux
+#define DW_EH_PE_funcrel 0x40 // start-of-procedure-relative
+#define DW_EH_PE_aligned 0x50 // aligned pointer
+
+#define DWARF_CIE_VERSION 3 // GCC emits version 1???
+
+// DWARF frame header
+typedef struct _eh_frame_hdr
+{
+ unsigned char version;
+ unsigned char eh_frame_ptr_enc;
+ unsigned char fde_count_enc;
+ unsigned char table_enc;
+ // The rest of the header is variable-length and consists of the
+ // following members:
+ //
+ // encoded_t eh_frame_ptr;
+ // encoded_t fde_count;
+ // struct
+ // {
+ // encoded_t start_ip; // first address covered by this FDE
+ // encoded_t fde_offset; // offset of the FDE
+ // } binary_search_table[fde_count];
+} eh_frame_hdr;
+
+// "DW_EH_PE_datarel|DW_EH_PE_sdata4" encoded fde table entry
+typedef struct _table_entry
+{
+ int32_t start_ip;
+ int32_t fde_offset;
+} table_entry;
+
+// DWARF unwind info
+typedef struct dwarf_cie_info
+{
+ unw_word_t cie_instr_start; // start addr. of CIE "initial_instructions"
+ unw_word_t cie_instr_end; // end addr. of CIE "initial_instructions"
+ unw_word_t fde_instr_start; // start addr. of FDE "instructions"
+ unw_word_t fde_instr_end; // end addr. of FDE "instructions"
+ unw_word_t code_align; // code-alignment factor
+ unw_word_t data_align; // data-alignment factor
+ unw_word_t ret_addr_column; // column of return-address register
+ unw_word_t handler; // address of personality-routine
+ uint16_t abi;
+ uint16_t tag;
+ uint8_t fde_encoding;
+ uint8_t lsda_encoding;
+ unsigned int sized_augmentation : 1;
+ unsigned int have_abi_marker : 1;
+ unsigned int signal_frame : 1;
+} dwarf_cie_info_t;
+
+static bool
+ReadValue8(const libunwindInfo* info, unw_word_t* addr, uint8_t* valp)
+{
+ uint8_t value;
+ if (!info->ReadMemory((PVOID)*addr, &value, sizeof(value))) {
+ return false;
+ }
+ *addr += sizeof(value);
+ *valp = value;
+ return true;
+}
+
+static bool
+ReadValue16(const libunwindInfo* info, unw_word_t* addr, uint16_t* valp)
+{
+ uint16_t value;
+ if (!info->ReadMemory((PVOID)*addr, &value, sizeof(value))) {
+ return false;
+ }
+ *addr += sizeof(value);
+ *valp = VAL16(value);
+ return true;
+}
+
+static bool
+ReadValue32(const libunwindInfo* info, unw_word_t* addr, uint32_t* valp)
+{
+ uint32_t value;
+ if (!info->ReadMemory((PVOID)*addr, &value, sizeof(value))) {
+ return false;
+ }
+ *addr += sizeof(value);
+ *valp = VAL32(value);
+ return true;
+}
+
+static bool
+ReadValue64(const libunwindInfo* info, unw_word_t* addr, uint64_t* valp)
+{
+ uint64_t value;
+ if (!info->ReadMemory((PVOID)*addr, &value, sizeof(value))) {
+ return false;
+ }
+ *addr += sizeof(value);
+ *valp = VAL64(value);
+ return true;
+}
+
+static bool
+ReadPointer(const libunwindInfo* info, unw_word_t* addr, unw_word_t* valp)
+{
+#ifdef BIT64
+ uint64_t val64;
+ if (ReadValue64(info, addr, &val64)) {
+ *valp = val64;
+ return true;
+ }
+#else
+ uint32_t val32;
+ if (ReadValue32(info, addr, &val32)) {
+ *valp = val32;
+ return true;
+ }
+#endif
+ return false;
+}
+
+// Read a unsigned "little-endian base 128" value. See Chapter 7.6 of DWARF spec v3.
+static bool
+ReadULEB128(const libunwindInfo* info, unw_word_t* addr, unw_word_t* valp)
+{
+ unw_word_t value = 0;
+ unsigned char byte;
+ int shift = 0;
+
+ do
+ {
+ if (!ReadValue8(info, addr, &byte)) {
+ return false;
+ }
+ value |= ((unw_word_t)byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+
+ *valp = value;
+ return true;
+}
+
+// Read a signed "little-endian base 128" value. See Chapter 7.6 of DWARF spec v3.
+static bool
+ReadSLEB128(const libunwindInfo* info, unw_word_t* addr, unw_word_t* valp)
+{
+ unw_word_t value = 0;
+ unsigned char byte;
+ int shift = 0;
+
+ do
+ {
+ if (!ReadValue8(info, addr, &byte)) {
+ return false;
+ }
+ value |= ((unw_word_t)byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+
+ if ((shift < (8 * sizeof(unw_word_t))) && ((byte & 0x40) != 0)) {
+ value |= ((unw_word_t)-1) << shift;
+ }
+
+ *valp = value;
+ return true;
+}
+
+static bool
+ReadEncodedPointer(const libunwindInfo* info, unw_word_t* addr, unsigned char encoding, unw_word_t funcRel, unw_word_t* valp)
+{
+ unw_word_t initialAddr = *addr;
+ uint16_t value16;
+ uint32_t value32;
+ uint64_t value64;
+ unw_word_t value;
+
+ if (encoding == DW_EH_PE_omit)
+ {
+ *valp = 0;
+ return true;
+ }
+ else if (encoding == DW_EH_PE_aligned)
+ {
+ int size = sizeof(unw_word_t);
+ *addr = (initialAddr + size - 1) & -size;
+ return ReadPointer(info, addr, valp);
+ }
+
+ switch (encoding & DW_EH_PE_FORMAT_MASK)
+ {
+ case DW_EH_PE_ptr:
+ if (!ReadPointer(info, addr, &value)) {
+ return false;
+ }
+ break;
+
+ case DW_EH_PE_uleb128:
+ if (!ReadULEB128(info, addr, &value)) {
+ return false;
+ }
+ break;
+
+ case DW_EH_PE_sleb128:
+ if (!ReadSLEB128(info, addr, &value)) {
+ return false;
+ }
+ break;
+
+ case DW_EH_PE_udata2:
+ if (!ReadValue16(info, addr, &value16)) {
+ return false;
+ }
+ value = value16;
+ break;
+
+ case DW_EH_PE_udata4:
+ if (!ReadValue32(info, addr, &value32)) {
+ return false;
+ }
+ value = value32;
+ break;
+
+ case DW_EH_PE_udata8:
+ if (!ReadValue64(info, addr, &value64)) {
+ return false;
+ }
+ value = value64;
+ break;
+
+ case DW_EH_PE_sdata2:
+ if (!ReadValue16(info, addr, &value16)) {
+ return false;
+ }
+ value = (int16_t)value16;
+ break;
+
+ case DW_EH_PE_sdata4:
+ if (!ReadValue32(info, addr, &value32)) {
+ return false;
+ }
+ value = (int32_t)value32;
+ break;
+
+ case DW_EH_PE_sdata8:
+ if (!ReadValue64(info, addr, &value64)) {
+ return false;
+ }
+ value = (int64_t)value64;
+ break;
+
+ default:
+ ASSERT("ReadEncodedPointer: invalid encoding format %x\n", encoding);
+ return false;
+ }
+
+ // 0 is a special value and always absolute
+ if (value == 0) {
+ *valp = 0;
+ return true;
+ }
+
+ switch (encoding & DW_EH_PE_APPL_MASK)
+ {
+ case DW_EH_PE_absptr:
+ break;
+
+ case DW_EH_PE_pcrel:
+ value += initialAddr;
+ break;
+
+ case DW_EH_PE_funcrel:
+ _ASSERTE(funcRel != UINTPTR_MAX);
+ value += funcRel;
+ break;
+
+ case DW_EH_PE_textrel:
+ case DW_EH_PE_datarel:
+ default:
+ ASSERT("ReadEncodedPointer: invalid application type %x\n", encoding);
+ return false;
+ }
+
+ if (encoding & DW_EH_PE_indirect)
+ {
+ unw_word_t indirect_addr = value;
+ if (!ReadPointer(info, &indirect_addr, &value)) {
+ return false;
+ }
+ }
+
+ *valp = value;
+ return true;
+}
+
+static bool
+LookupTableEntry(const libunwindInfo* info, int32_t ip, unw_word_t tableAddr, size_t tableCount, table_entry* entry, bool* found)
+{
+ size_t low, high, mid;
+ unw_word_t addr;
+ int32_t start_ip;
+
+ *found = false;
+
+ // do a binary search on table
+ for (low = 0, high = tableCount; low < high;)
+ {
+ mid = (low + high) / 2;
+ addr = tableAddr + (mid * sizeof(table_entry));
+
+ if (!ReadValue32(info, &addr, (uint32_t*)&start_ip)) {
+ return false;
+ }
+ if (ip < start_ip) {
+ high = mid;
+ }
+ else {
+ low = mid + 1;
+ }
+ }
+
+ if (high > 0) {
+ addr = tableAddr + ((high - 1) * sizeof(table_entry));
+ // Assumes that the table_entry is two 32 bit values
+ _ASSERTE(sizeof(*entry) == sizeof(uint64_t));
+ if (!ReadValue64(info, &addr, (uint64_t*)entry)) {
+ return false;
+ }
+ *found = true;
+ }
+
+ return true;
+}
+
+static bool
+ParseCie(const libunwindInfo* info, unw_word_t addr, dwarf_cie_info_t* dci)
+{
+ uint8_t ch, version, fdeEncoding, handlerEncoding;
+ unw_word_t cieLength, cieEndAddr;
+ uint32_t value32;
+ uint64_t value64;
+
+ memset(dci, 0, sizeof (*dci));
+
+ // Pick appropriate default for FDE-encoding. DWARF spec says
+ // start-IP (initial_location) and the code-size (address_range) are
+ // "address-unit sized constants". The `R' augmentation can be used
+ // to override this, but by default, we pick an address-sized unit
+ // for fde_encoding.
+#if BIT64
+ fdeEncoding = DW_EH_PE_udata8;
+#else
+ fdeEncoding = DW_EH_PE_udata4;
+#endif
+
+ dci->lsda_encoding = DW_EH_PE_omit;
+ dci->handler = 0;
+
+ if (!ReadValue32(info, &addr, &value32)) {
+ return false;
+ }
+
+ if (value32 != 0xffffffff)
+ {
+ // The CIE is in the 32-bit DWARF format
+ uint32_t cieId;
+
+ // DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0
+ const uint32_t expectedId = 0;
+
+ cieLength = value32;
+ cieEndAddr = addr + cieLength;
+
+ if (!ReadValue32(info, &addr, &cieId)) {
+ return false;
+ }
+ if (cieId != expectedId) {
+ ASSERT("ParseCie: unexpected cie id %x\n", cieId);
+ return false;
+ }
+ }
+ else
+ {
+ // The CIE is in the 64-bit DWARF format
+ uint64_t cieId;
+
+ // DWARF says CIE id should be 0xffffffffffffffff, but in .eh_frame, it's 0
+ const uint64_t expectedId = 0;
+
+ if (!ReadValue64(info, &addr, &value64)) {
+ return false;
+ }
+ cieLength = value64;
+ cieEndAddr = addr + cieLength;
+
+ if (!ReadValue64(info, &addr, &cieId)) {
+ return false;
+ }
+ if (cieId != expectedId) {
+ ASSERT("ParseCie: unexpected cie id %lx\n", cieId);
+ return false;
+ }
+ }
+ dci->cie_instr_end = cieEndAddr;
+
+ if (!ReadValue8(info, &addr, &version)) {
+ return false;
+ }
+ if (version != 1 && version != DWARF_CIE_VERSION) {
+ ASSERT("ParseCie: invalid cie version %x\n", version);
+ return false;
+ }
+
+ // Read the augmentation string
+ uint8_t augmentationString[8];
+ memset(augmentationString, 0, sizeof(augmentationString));
+
+ for (int i = 0; i < sizeof(augmentationString); i++)
+ {
+ if (!ReadValue8(info, &addr, &ch)) {
+ return false;
+ }
+ if (ch == 0) {
+ break;
+ }
+ augmentationString[i] = ch;
+ }
+
+ // Read the code and data alignment
+ if (!ReadULEB128(info, &addr, &dci->code_align)) {
+ return false;
+ }
+ if (!ReadSLEB128(info, &addr, &dci->data_align)) {
+ return false;
+ }
+
+ // Read the return-address column either as a u8 or as a uleb128
+ if (version == 1)
+ {
+ if (!ReadValue8(info, &addr, &ch)) {
+ return false;
+ }
+ dci->ret_addr_column = ch;
+ }
+ else
+ {
+ if (!ReadULEB128(info, &addr, &dci->ret_addr_column)) {
+ return false;
+ }
+ }
+
+ // Parse the augmentation string
+ for (int i = 0; i < sizeof(augmentationString); i++)
+ {
+ bool done = false;
+ unw_word_t augmentationSize;
+
+ switch (augmentationString[i])
+ {
+ case '\0':
+ done = true;
+ break;
+
+ case 'z':
+ dci->sized_augmentation = 1;
+ if (!ReadULEB128(info, &addr, &augmentationSize)) {
+ return false;
+ }
+ break;
+
+ case 'L':
+ // read the LSDA pointer-encoding format
+ if (!ReadValue8(info, &addr, &ch)) {
+ return false;
+ }
+ dci->lsda_encoding = ch;
+ break;
+
+ case 'R':
+ // read the FDE pointer-encoding format
+ if (!ReadValue8(info, &addr, &fdeEncoding)) {
+ return false;
+ }
+ break;
+
+ case 'P':
+ // read the personality-routine pointer-encoding format
+ if (!ReadValue8(info, &addr, &handlerEncoding)) {
+ return false;
+ }
+ if (!ReadEncodedPointer(info, &addr, handlerEncoding, UINTPTR_MAX, &dci->handler)) {
+ return false;
+ }
+ break;
+
+ case 'S':
+ // This is a signal frame
+ dci->signal_frame = 1;
+
+ // Temporarily set it to one so dwarf_parse_fde() knows that
+ // it should fetch the actual ABI/TAG pair from the FDE.
+ dci->have_abi_marker = 1;
+ break;
+
+ default:
+ if (dci->sized_augmentation) {
+ // If we have the size of the augmentation body, we can skip
+ // over the parts that we don't understand, so we're OK
+ done = true;
+ break;
+ }
+ ASSERT("ParseCie: unexpected argumentation string '%s'\n", augmentationString[i]);
+ return false;
+ }
+
+ if (done) {
+ break;
+ }
+ }
+ dci->fde_encoding = fdeEncoding;
+ dci->cie_instr_start = addr;
+ return true;
+}
+
+static bool
+ExtractProcInfoFromFde(const libunwindInfo* info, unw_word_t* addrp, unw_proc_info_t *pip, int need_unwind_info)
+{
+ unw_word_t addr = *addrp, fdeEndAddr, cieOffsetAddr, cieAddr;
+ uint32_t value32;
+ uint64_t value64;
+
+ if (!ReadValue32(info, &addr, &value32)) {
+ return false;
+ }
+ if (value32 != 0xffffffff)
+ {
+ int32_t cieOffset = 0;
+
+ // In some configurations, an FDE with a 0 length indicates the end of the FDE-table
+ if (value32 == 0) {
+ return false;
+ }
+ // the FDE is in the 32-bit DWARF format */
+ *addrp = fdeEndAddr = addr + value32;
+ cieOffsetAddr = addr;
+
+ if (!ReadValue32(info, &addr, (uint32_t*)&cieOffset)) {
+ return false;
+ }
+ // Ignore CIEs (happens during linear search)
+ if (cieOffset == 0) {
+ return true;
+ }
+ // DWARF says that the CIE_pointer in the FDE is a .debug_frame-relative offset,
+ // but the GCC-generated .eh_frame sections instead store a "pcrelative" offset,
+ // which is just as fine as it's self-contained
+ cieAddr = cieOffsetAddr - cieOffset;
+ }
+ else
+ {
+ int64_t cieOffset = 0;
+
+ // the FDE is in the 64-bit DWARF format */
+ if (!ReadValue64(info, &addr, (uint64_t*)&value64)) {
+ return false;
+ }
+ *addrp = fdeEndAddr = addr + value64;
+ cieOffsetAddr = addr;
+
+ if (!ReadValue64(info, &addr, (uint64_t*)&cieOffset)) {
+ return false;
+ }
+ // Ignore CIEs (happens during linear search)
+ if (cieOffset == 0) {
+ return true;
+ }
+ // DWARF says that the CIE_pointer in the FDE is a .debug_frame-relative offset,
+ // but the GCC-generated .eh_frame sections instead store a "pcrelative" offset,
+ // which is just as fine as it's self-contained
+ cieAddr = (unw_word_t)((uint64_t)cieOffsetAddr - cieOffset);
+ }
+
+ dwarf_cie_info_t dci;
+ if (!ParseCie(info, cieAddr, &dci)) {
+ return false;
+ }
+
+ unw_word_t ipStart, ipRange;
+ if (!ReadEncodedPointer(info, &addr, dci.fde_encoding, UINTPTR_MAX, &ipStart)) {
+ return false;
+ }
+
+ // IP-range has same encoding as FDE pointers, except that it's always an absolute value
+ uint8_t ipRangeEncoding = dci.fde_encoding & DW_EH_PE_FORMAT_MASK;
+ if (!ReadEncodedPointer(info, &addr, ipRangeEncoding, UINTPTR_MAX, &ipRange)) {
+ return false;
+ }
+ pip->start_ip = ipStart;
+ pip->end_ip = ipStart + ipRange;
+ pip->handler = dci.handler;
+
+ unw_word_t augmentationSize, augmentationEndAddr;
+ if (dci.sized_augmentation) {
+ if (!ReadULEB128(info, &addr, &augmentationSize)) {
+ return false;
+ }
+ augmentationEndAddr = addr + augmentationSize;
+ }
+
+ // Read language specific data area address
+ if (!ReadEncodedPointer(info, &addr, dci.lsda_encoding, pip->start_ip, &pip->lsda)) {
+ return false;
+ }
+
+ // Now fill out the proc info if requested
+ if (need_unwind_info)
+ {
+ if (dci.have_abi_marker)
+ {
+ if (!ReadValue16(info, &addr, &dci.abi)) {
+ return false;
+ }
+ if (!ReadValue16(info, &addr, &dci.tag)) {
+ return false;
+ }
+ }
+ if (dci.sized_augmentation) {
+ dci.fde_instr_start = augmentationEndAddr;
+ }
+ else {
+ dci.fde_instr_start = addr;
+ }
+ dci.fde_instr_end = fdeEndAddr;
+
+ pip->format = UNW_INFO_FORMAT_TABLE;
+ pip->unwind_info_size = sizeof(dci);
+ pip->unwind_info = malloc(sizeof(dci));
+ if (pip->unwind_info == nullptr) {
+ return -UNW_ENOMEM;
+ }
+ memcpy(pip->unwind_info, &dci, sizeof(dci));
+ }
+
+ return true;
+}
+
+
+static int
+get_dyn_info_list_addr(unw_addr_space_t as, unw_word_t *dilap, void *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int
+access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, int write, void *arg)
+{
+ if (write)
+ {
+ ASSERT("Memory write must never be called by libunwind during stackwalk\n");
+ return -UNW_EINVAL;
+ }
+ const auto *info = (libunwindInfo*)arg;
+
+ if (info->ReadMemory((PVOID)addr, valp, sizeof(*valp)))
+ {
+ return UNW_ESUCCESS;
+ }
+ else
+ {
+ return -UNW_EUNSPEC;
+ }
+}
+
+static int
+access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, int write, void *arg)
+{
+ if (write)
+ {
+ ASSERT("Register write must never be called by libunwind during stackwalk\n");
+ return -UNW_EREADONLYREG;
+ }
+
+ const auto *info = (libunwindInfo*)arg;
+ CONTEXT *winContext = info->Context;
+
+ switch (regnum)
+ {
+#if defined(_AMD64_)
+ case UNW_REG_IP: *valp = (unw_word_t)winContext->Rip; break;
+ case UNW_REG_SP: *valp = (unw_word_t)winContext->Rsp; break;
+ case UNW_X86_64_RBP: *valp = (unw_word_t)winContext->Rbp; break;
+ case UNW_X86_64_RBX: *valp = (unw_word_t)winContext->Rbx; break;
+ case UNW_X86_64_R12: *valp = (unw_word_t)winContext->R12; break;
+ case UNW_X86_64_R13: *valp = (unw_word_t)winContext->R13; break;
+ case UNW_X86_64_R14: *valp = (unw_word_t)winContext->R14; break;
+ case UNW_X86_64_R15: *valp = (unw_word_t)winContext->R15; break;
+#elif defined(_ARM_)
+ case UNW_ARM_R13: *valp = (unw_word_t)winContext->Sp; break;
+ case UNW_ARM_R14: *valp = (unw_word_t)winContext->Lr; break;
+ case UNW_ARM_R15: *valp = (unw_word_t)winContext->Pc; break;
+ case UNW_ARM_R4: *valp = (unw_word_t)winContext->R4; break;
+ case UNW_ARM_R5: *valp = (unw_word_t)winContext->R5; break;
+ case UNW_ARM_R6: *valp = (unw_word_t)winContext->R6; break;
+ case UNW_ARM_R7: *valp = (unw_word_t)winContext->R7; break;
+ case UNW_ARM_R8: *valp = (unw_word_t)winContext->R8; break;
+ case UNW_ARM_R9: *valp = (unw_word_t)winContext->R9; break;
+ case UNW_ARM_R10: *valp = (unw_word_t)winContext->R10; break;
+ case UNW_ARM_R11: *valp = (unw_word_t)winContext->R11; break;
+#elif defined(_ARM64_)
+ case UNW_REG_IP: *valp = (unw_word_t)winContext->Pc; break;
+ case UNW_REG_SP: *valp = (unw_word_t)winContext->Sp; break;
+ case UNW_AARCH64_X29: *valp = (unw_word_t)winContext->Fp; break;
+ case UNW_AARCH64_X30: *valp = (unw_word_t)winContext->Lr; break;
+ case UNW_AARCH64_X19: *valp = (unw_word_t)winContext->X19; break;
+ case UNW_AARCH64_X20: *valp = (unw_word_t)winContext->X20; break;
+ case UNW_AARCH64_X21: *valp = (unw_word_t)winContext->X21; break;
+ case UNW_AARCH64_X22: *valp = (unw_word_t)winContext->X22; break;
+ case UNW_AARCH64_X23: *valp = (unw_word_t)winContext->X23; break;
+ case UNW_AARCH64_X24: *valp = (unw_word_t)winContext->X24; break;
+ case UNW_AARCH64_X25: *valp = (unw_word_t)winContext->X25; break;
+ case UNW_AARCH64_X26: *valp = (unw_word_t)winContext->X26; break;
+ case UNW_AARCH64_X27: *valp = (unw_word_t)winContext->X27; break;
+ case UNW_AARCH64_X28: *valp = (unw_word_t)winContext->X28; break;
+#else
+#error unsupported architecture
+#endif
+ default:
+ ASSERT("Attempt to read an unknown register\n");
+ return -UNW_EBADREG;
+ }
+ return UNW_ESUCCESS;
+}
+
+static int
+access_fpreg(unw_addr_space_t as, unw_regnum_t regnum, unw_fpreg_t *fpvalp, int write, void *arg)
+{
+ ASSERT("Not supposed to be ever called\n");
+ return -UNW_EINVAL;
+}
+
+static int
+resume(unw_addr_space_t as, unw_cursor_t *cp, void *arg)
+{
+ ASSERT("Not supposed to be ever called\n");
+ return -UNW_EINVAL;
+}
+
+static int
+get_proc_name(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, unw_word_t *offp, void *arg)
+{
+ ASSERT("Not supposed to be ever called\n");
+ return -UNW_EINVAL;
+}
+
+static int
+find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pip, int need_unwind_info, void *arg)
+{
+ const auto *info = (libunwindInfo*)arg;
+ memset(pip, 0, sizeof(*pip));
+
+ Ehdr ehdr;
+ if (!info->ReadMemory((void*)info->BaseAddress, &ehdr, sizeof(ehdr))) {
+ ERROR("ELF: reading ehdr %p\n", info->BaseAddress);
+ return -UNW_EINVAL;
+ }
+ Phdr* phdrAddr = reinterpret_cast<Phdr*>(info->BaseAddress + ehdr.e_phoff);
+ int phnum = ehdr.e_phnum;
+ TRACE("ELF: base %p ip %p e_type %d e_phnum %d e_phoff %p\n", info->BaseAddress, ip, ehdr.e_type, ehdr.e_phnum, ehdr.e_phoff);
+
+ // The eh_frame header
+ Phdr ehPhdr;
+ memset(&ehPhdr, 0, sizeof(ehPhdr));
+
+ // Search for the module's dynamic header and unwind frames
+ Dyn* dynamicAddr = nullptr;
+
+ for (int i = 0; i < phnum; i++, phdrAddr++)
+ {
+ Phdr ph;
+ if (!info->ReadMemory(phdrAddr, &ph, sizeof(ph))) {
+ ERROR("ELF: reading phdrAddr %p\n", phdrAddr);
+ return -UNW_EINVAL;
+ }
+ TRACE("ELF: phdr %p type %d (%x) vaddr %p memsz %016llx paddr %p filesz %016llx offset %p align %016llx\n",
+ phdrAddr, ph.p_type, ph.p_type, ph.p_vaddr, ph.p_memsz, ph.p_paddr, ph.p_filesz, ph.p_offset, ph.p_align);
+
+ switch (ph.p_type)
+ {
+ case PT_DYNAMIC:
+ if (ehdr.e_type == ET_EXEC) {
+ dynamicAddr = reinterpret_cast<Dyn*>(ph.p_vaddr);
+ }
+ if (ehdr.e_type == ET_DYN) {
+ dynamicAddr = reinterpret_cast<Dyn*>(ph.p_vaddr + info->BaseAddress);
+ }
+ break;
+
+ case PT_GNU_EH_FRAME:
+ ehPhdr = ph;
+ break;
+ }
+ }
+
+ if (dynamicAddr != nullptr)
+ {
+ for (;;)
+ {
+ Dyn dyn;
+ if (!info->ReadMemory(dynamicAddr, &dyn, sizeof(dyn))) {
+ ERROR("ELF: reading dynamicAddr %p\n", dynamicAddr);
+ return -UNW_EINVAL;
+ }
+ if (dyn.d_tag == DT_PLTGOT) {
+ TRACE("ELF: dyn %p tag %d (%x) d_ptr %p\n", dynamicAddr, dyn.d_tag, dyn.d_tag, dyn.d_un.d_ptr);
+ pip->gp = dyn.d_un.d_ptr;
+ break;
+ }
+ else if (dyn.d_tag == DT_NULL) {
+ break;
+ }
+ dynamicAddr++;
+ }
+ }
+ unw_word_t ehFrameHdrAddr = ehPhdr.p_offset + info->BaseAddress;
+ eh_frame_hdr ehFrameHdr;
+
+ if (!info->ReadMemory((PVOID)ehFrameHdrAddr, &ehFrameHdr, sizeof(eh_frame_hdr))) {
+ ERROR("ELF: reading ehFrameHdrAddr %p\n", ehFrameHdrAddr);
+ return -UNW_EINVAL;
+ }
+ TRACE("ehFrameHdrAddr %p version %d eh_frame_ptr_enc %d fde_count_enc %d table_enc %d\n",
+ ehFrameHdrAddr, ehFrameHdr.version, ehFrameHdr.eh_frame_ptr_enc, ehFrameHdr.fde_count_enc, ehFrameHdr.table_enc);
+
+ if (ehFrameHdr.version != DW_EH_VERSION) {
+ ASSERT("ehFrameHdr version %x not supported\n", ehFrameHdr.version);
+ return -UNW_EBADVERSION;
+ }
+ unw_word_t addr = ehFrameHdrAddr + sizeof(eh_frame_hdr);
+ unw_word_t ehFrameStart;
+ unw_word_t fdeCount;
+
+ // Decode the eh_frame_hdr info
+ if (!ReadEncodedPointer(info, &addr, ehFrameHdr.eh_frame_ptr_enc, UINTPTR_MAX, &ehFrameStart)) {
+ ERROR("decoding eh_frame_ptr\n");
+ return -UNW_EINVAL;
+ }
+ if (!ReadEncodedPointer(info, &addr, ehFrameHdr.fde_count_enc, UINTPTR_MAX, &fdeCount)) {
+ ERROR("decoding fde_count_enc\n");
+ return -UNW_EINVAL;
+ }
+ TRACE("ehFrameStart %p fdeCount %p ip offset %08x\n", ehFrameStart, fdeCount, (int32_t)(ip - ehFrameHdrAddr));
+
+ // LookupTableEntry assumes this encoding
+ if (ehFrameHdr.table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
+ ASSERT("Table encoding not supported %x\n", ehFrameHdr.table_enc);
+ return -UNW_EINVAL;
+ }
+ // Find the fde using a binary search on the frame table
+ table_entry entry;
+ bool found;
+ if (!LookupTableEntry(info, ip - ehFrameHdrAddr, addr, fdeCount, &entry, &found)) {
+ ERROR("LookupTableEntry\n");
+ return -UNW_EINVAL;
+ }
+ unw_word_t fdeAddr = entry.fde_offset + ehFrameHdrAddr;
+ TRACE("start_ip %08x fde_offset %08x fdeAddr %p found %d\n", entry.start_ip, entry.fde_offset, fdeAddr, found);
+
+ // Unwind info not found
+ if (!found) {
+ return -UNW_ENOINFO;
+ }
+
+ // Now get the unwind info
+ if (!ExtractProcInfoFromFde(info, &fdeAddr, pip, need_unwind_info)) {
+ ERROR("ExtractProcInfoFromFde\n");
+ return -UNW_EINVAL;
+ }
+
+ _ASSERTE(ip >= pip->start_ip && ip <= pip->end_ip);
+ return UNW_ESUCCESS;
+}
+
+static void
+put_unwind_info(unw_addr_space_t as, unw_proc_info_t *pip, void *arg)
+{
+ if (pip->unwind_info != nullptr) {
+ free(pip->unwind_info);
+ pip->unwind_info = nullptr;
+ }
+}
+
+static unw_accessors_t unwind_accessors =
+{
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name
+};
+
+/*++
+Function:
+ PAL_VirtualUnwindOutOfProc
+
+ Unwind the stack given the context for a "remote" target using the
+ provided read memory callback.
+
+ Assumes the IP is in the module of the base address provided (coreclr).
+
+Parameters:
+ context - the start context in the target
+ contextPointers - the context of the next frame
+ baseAddress - base address of the module to find the unwind info
+ readMemoryCallback - reads memory from the target
+--*/
+BOOL
+PALAPI
+PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback)
+{
+ unw_addr_space_t addrSpace = 0;
+ unw_cursor_t cursor;
+ libunwindInfo info;
+ BOOL result = FALSE;
+ int st;
+
+ info.BaseAddress = baseAddress;
+ info.Context = context;
+ info.ReadMemory = readMemoryCallback;
+
+ addrSpace = unw_create_addr_space(&unwind_accessors, 0);
+
+ st = unw_init_remote(&cursor, addrSpace, &info);
+ if (st < 0)
+ {
+ result = FALSE;
+ goto exit;
+ }
+
+ st = unw_step(&cursor);
+ if (st < 0)
+ {
+ result = FALSE;
+ goto exit;
+ }
+
+ UnwindContextToWinContext(&cursor, context);
+
+ if (contextPointers != NULL)
+ {
+ GetContextPointers(&cursor, NULL, contextPointers);
+ }
+ result = TRUE;
+
+exit:
+ if (addrSpace != 0)
+ {
+ unw_destroy_addr_space(addrSpace);
+ }
+ return result;
+}
+
+#else
+
+BOOL
+PALAPI
+PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback)
+{
+ return FALSE;
+}
+
+#endif // defined(_AMD64_) && defined(HAVE_UNW_GET_ACCESSORS)
diff --git a/src/pal/src/exception/seh-unwind.cpp b/src/pal/src/exception/seh-unwind.cpp
index f1f25b01e8..b15ac34c24 100644
--- a/src/pal/src/exception/seh-unwind.cpp
+++ b/src/pal/src/exception/seh-unwind.cpp
@@ -32,14 +32,8 @@ Abstract:
#define UNW_LOCAL_ONLY
#endif // !__linux__
#include <libunwind.h>
-#ifdef __linux__
-#ifdef HAVE_LIBUNWIND_PTRACE
-#include <libunwind-ptrace.h>
-#endif // HAVE_LIBUNWIND_PTRACE
-#endif // __linux__
#endif // HAVE_LIBUNWIND_H
-
//----------------------------------------------------------------------
// Virtual Unwinding
//----------------------------------------------------------------------
@@ -141,7 +135,7 @@ static void WinContextToUnwindCursor(CONTEXT *winContext, unw_cursor_t *cursor)
}
#endif
-static void UnwindContextToWinContext(unw_cursor_t *cursor, CONTEXT *winContext)
+void UnwindContextToWinContext(unw_cursor_t *cursor, CONTEXT *winContext)
{
#if defined(_AMD64_)
unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &winContext->Rip);
@@ -209,7 +203,7 @@ static void GetContextPointer(unw_cursor_t *cursor, unw_context_t *unwContext, i
#endif
}
-static void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
+void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
{
#if defined(_AMD64_)
GetContextPointer(cursor, unwContext, UNW_X86_64_RBP, &contextPointers->Rbp);
@@ -357,248 +351,6 @@ BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextP
#error don't know how to unwind on this platform
#endif
-// These methods are only used on the AMD64 build
-#ifdef _AMD64_
-#ifdef HAVE_UNW_GET_ACCESSORS
-
-static struct LibunwindCallbacksInfoType
-{
- CONTEXT *Context;
- ReadMemoryWordCallback readMemCallback;
-} LibunwindCallbacksInfo;
-
-static int get_dyn_info_list_addr(unw_addr_space_t as, unw_word_t *dilap, void *arg)
-{
- return -UNW_ENOINFO;
-}
-
-static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, int write, void *arg)
-{
- if (write)
- {
- ASSERT("Memory write must never be called by libunwind during stackwalk");
- return -UNW_EINVAL;
- }
-
- // access_mem sometimes gets called by _UPT_find_proc_info, in such cases arg has a pointer to libunwind internal data
- // returned by _UPT_create. It makes it impossible to use arg for passing readMemCallback. That's why we have to use global variable.
- if (LibunwindCallbacksInfo.readMemCallback((SIZE_T)addr, (SIZE_T *)valp))
- {
- return UNW_ESUCCESS;
- }
- else
- {
- return -UNW_EUNSPEC;
- }
-}
-
-static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, int write, void *arg)
-{
- if (write)
- {
- ASSERT("Register write must never be called by libunwind during stackwalk");
- return -UNW_EREADONLYREG;
- }
-
- CONTEXT *winContext = LibunwindCallbacksInfo.Context;
-
- switch (regnum)
- {
-#if defined(_AMD64_)
- case UNW_REG_IP: *valp = (unw_word_t) winContext->Rip; break;
- case UNW_REG_SP: *valp = (unw_word_t) winContext->Rsp; break;
- case UNW_X86_64_RBP: *valp = (unw_word_t) winContext->Rbp; break;
- case UNW_X86_64_RBX: *valp = (unw_word_t) winContext->Rbx; break;
- case UNW_X86_64_R12: *valp = (unw_word_t) winContext->R12; break;
- case UNW_X86_64_R13: *valp = (unw_word_t) winContext->R13; break;
- case UNW_X86_64_R14: *valp = (unw_word_t) winContext->R14; break;
- case UNW_X86_64_R15: *valp = (unw_word_t) winContext->R15; break;
-#elif defined(_ARM_)
- case UNW_ARM_R13: *valp = (unw_word_t) winContext->Sp; break;
- case UNW_ARM_R14: *valp = (unw_word_t) winContext->Lr; break;
- case UNW_ARM_R15: *valp = (unw_word_t) winContext->Pc; break;
- case UNW_ARM_R4: *valp = (unw_word_t) winContext->R4; break;
- case UNW_ARM_R5: *valp = (unw_word_t) winContext->R5; break;
- case UNW_ARM_R6: *valp = (unw_word_t) winContext->R6; break;
- case UNW_ARM_R7: *valp = (unw_word_t) winContext->R7; break;
- case UNW_ARM_R8: *valp = (unw_word_t) winContext->R8; break;
- case UNW_ARM_R9: *valp = (unw_word_t) winContext->R9; break;
- case UNW_ARM_R10: *valp = (unw_word_t) winContext->R10; break;
- case UNW_ARM_R11: *valp = (unw_word_t) winContext->R11; break;
-#elif defined(_ARM64_)
- case UNW_REG_IP: *valp = (unw_word_t) winContext->Pc; break;
- case UNW_REG_SP: *valp = (unw_word_t) winContext->Sp; break;
- case UNW_AARCH64_X29: *valp = (unw_word_t) winContext->Fp; break;
- case UNW_AARCH64_X30: *valp = (unw_word_t) winContext->Lr; break;
- case UNW_AARCH64_X19: *valp = (unw_word_t) winContext->X19; break;
- case UNW_AARCH64_X20: *valp = (unw_word_t) winContext->X20; break;
- case UNW_AARCH64_X21: *valp = (unw_word_t) winContext->X21; break;
- case UNW_AARCH64_X22: *valp = (unw_word_t) winContext->X22; break;
- case UNW_AARCH64_X23: *valp = (unw_word_t) winContext->X23; break;
- case UNW_AARCH64_X24: *valp = (unw_word_t) winContext->X24; break;
- case UNW_AARCH64_X25: *valp = (unw_word_t) winContext->X25; break;
- case UNW_AARCH64_X26: *valp = (unw_word_t) winContext->X26; break;
- case UNW_AARCH64_X27: *valp = (unw_word_t) winContext->X27; break;
- case UNW_AARCH64_X28: *valp = (unw_word_t) winContext->X28; break;
-#else
-#error unsupported architecture
-#endif
- default:
- ASSERT("Attempt to read an unknown register.");
- return -UNW_EBADREG;
- }
- return UNW_ESUCCESS;
-}
-
-static int access_fpreg(unw_addr_space_t as, unw_regnum_t regnum, unw_fpreg_t *fpvalp, int write, void *arg)
-{
- ASSERT("Not supposed to be ever called");
- return -UNW_EINVAL;
-}
-
-static int resume(unw_addr_space_t as, unw_cursor_t *cp, void *arg)
-{
- ASSERT("Not supposed to be ever called");
- return -UNW_EINVAL;
-}
-
-static int get_proc_name(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, unw_word_t *offp, void *arg)
-{
- ASSERT("Not supposed to be ever called");
- return -UNW_EINVAL;
-}
-
-int find_proc_info(unw_addr_space_t as,
- unw_word_t ip, unw_proc_info_t *pip,
- int need_unwind_info, void *arg)
-{
-#ifdef HAVE_LIBUNWIND_PTRACE
- // UNIXTODO: libunwind RPM package on Fedora/CentOS/RedHat doesn't have libunwind-ptrace.so
- // and we can't use it from a shared library like libmscordaccore.so.
- // That's why all calls to ptrace parts of libunwind ifdeffed out for now.
- return _UPT_find_proc_info(as, ip, pip, need_unwind_info, arg);
-#else
- return -UNW_EINVAL;
-#endif
-}
-
-void put_unwind_info(unw_addr_space_t as, unw_proc_info_t *pip, void *arg)
-{
-#ifdef HAVE_LIBUNWIND_PTRACE
- return _UPT_put_unwind_info(as, pip, arg);
-#endif
-}
-
-static unw_accessors_t unwind_accessors =
-{
- .find_proc_info = find_proc_info,
- .put_unwind_info = put_unwind_info,
- .get_dyn_info_list_addr = get_dyn_info_list_addr,
- .access_mem = access_mem,
- .access_reg = access_reg,
- .access_fpreg = access_fpreg,
- .resume = resume,
- .get_proc_name = get_proc_name
-};
-
-BOOL PAL_VirtualUnwindOutOfProc(CONTEXT *context,
- KNONVOLATILE_CONTEXT_POINTERS *contextPointers,
- DWORD pid,
- ReadMemoryWordCallback readMemCallback)
-{
- // This function can be executed only by one thread at a time.
- // The reason for this is that we need to pass context and read mem function to libunwind callbacks
- // but "arg" is already used by the pointer returned from _UPT_create().
- // So we resort to using global variables and a lock.
- struct Lock
- {
- CRITICAL_SECTION cs;
- Lock()
- {
- // ctor of a static variable is a thread-safe way to initialize critical section exactly once (clang,gcc)
- InitializeCriticalSection(&cs);
- }
- };
- struct LockHolder
- {
- CRITICAL_SECTION *cs;
- LockHolder(CRITICAL_SECTION *cs)
- {
- this->cs = cs;
- EnterCriticalSection(cs);
- }
-
- ~LockHolder()
- {
- LeaveCriticalSection(cs);
- cs = NULL;
- }
- };
- static Lock lock;
- LockHolder lockHolder(&lock.cs);
-
- int st;
- unw_cursor_t cursor;
- unw_addr_space_t addrSpace = 0;
- void *libunwindUptPtr = NULL;
- BOOL result = FALSE;
-
- LibunwindCallbacksInfo.Context = context;
- LibunwindCallbacksInfo.readMemCallback = readMemCallback;
-
- addrSpace = unw_create_addr_space(&unwind_accessors, 0);
-#ifdef HAVE_LIBUNWIND_PTRACE
- libunwindUptPtr = _UPT_create(pid);
-#endif
- st = unw_init_remote(&cursor, addrSpace, libunwindUptPtr);
- if (st < 0)
- {
- result = FALSE;
- goto Exit;
- }
-
- st = unw_step(&cursor);
- if (st < 0)
- {
- result = FALSE;
- goto Exit;
- }
-
- UnwindContextToWinContext(&cursor, context);
-
- if (contextPointers != NULL)
- {
- GetContextPointers(&cursor, NULL, contextPointers);
- }
- result = TRUE;
-
-Exit:
-#ifdef HAVE_LIBUNWIND_PTRACE
- if (libunwindUptPtr != NULL)
- {
- _UPT_destroy(libunwindUptPtr);
- }
-#endif
- if (addrSpace != 0)
- {
- unw_destroy_addr_space(addrSpace);
- }
- return result;
-}
-#else // HAVE_UNW_GET_ACCESSORS
-
-BOOL PAL_VirtualUnwindOutOfProc(CONTEXT *context,
- KNONVOLATILE_CONTEXT_POINTERS *contextPointers,
- DWORD pid,
- ReadMemoryWordCallback readMemCallback)
-{
- //UNIXTODO: Implement for Mac flavor of libunwind
- return FALSE;
-}
-
-#endif // !HAVE_UNW_GET_ACCESSORS
-#endif // _AMD64_
-
struct ExceptionRecords
{
CONTEXT ContextRecord;
diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp
index 3ceb16b4f6..3f9e19ed59 100644
--- a/src/pal/src/exception/signal.cpp
+++ b/src/pal/src/exception/signal.cpp
@@ -722,6 +722,42 @@ PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread)
/*++
Function :
+ signal_ignore_handler
+
+ Simple signal handler which does nothing
+
+Parameters :
+ POSIX signal handler parameter list ("man sigaction" for details)
+
+(no return value)
+--*/
+static void signal_ignore_handler(int code, siginfo_t *siginfo, void *context)
+{
+}
+
+
+void PAL_IgnoreProfileSignal(int signalNum)
+{
+#if !HAVE_MACH_EXCEPTIONS
+ // Add a signal handler which will ignore signals
+ // This will allow signal to be used as a marker in perf recording.
+ // This will be used as an aid to synchronize recorded profile with
+ // test cases
+ //
+ // signal(signalNum, SGN_IGN) can not be used here. It will ignore
+ // the signal in kernel space and therefore generate no recordable
+ // event for profiling. Preventing it being used for profile
+ // synchronization
+ //
+ // Since this is only used in rare circumstances no attempt to
+ // restore the old handler will be made
+ handle_signal(signalNum, signal_ignore_handler, 0);
+#endif
+}
+
+
+/*++
+Function :
SEHSetSafeState
specify whether the current thread is in a state where exception handling
diff --git a/src/pal/src/file/disk.cpp b/src/pal/src/file/disk.cpp
deleted file mode 100644
index e5d6f831dd..0000000000
--- a/src/pal/src/file/disk.cpp
+++ /dev/null
@@ -1,180 +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.
-
-/*++
-
-
-
-Module Name:
-
- disk.c
-
-Abstract:
-
- Implementation of the disk information functions.
-
-Revision History:
-
-
-
---*/
-
-#include "pal/palinternal.h"
-#include "pal/dbgmsg.h"
-#include "pal/file.h"
-#include "pal/stackstring.hpp"
-
-#include <sys/param.h>
-#include <sys/mount.h>
-#include <errno.h>
-#if HAVE_STATVFS
-#include <sys/types.h>
-#include <sys/statvfs.h>
-#define statfs statvfs
-#if STATVFS64_PROTOTYPE_BROKEN
-typedef statvfs_t pal_statfs;
-#else // STATVFS64_PROTOTYPE_BROKEN
-typedef struct statvfs pal_statfs;
-#endif // STATVFS64_PROTOTYPE_BROKEN
-#else // HAVE_STATVFS
-typedef struct statfs pal_statfs;
-#endif // HAVE_STATVFS
-
-SET_DEFAULT_DEBUG_CHANNEL(FILE);
-
-/*++
-
-Function:
-
- GetDiskFreeSpaceW
-
-See MSDN doc.
---*/
-PALIMPORT
-BOOL
-PALAPI
-GetDiskFreeSpaceW(
- LPCWSTR lpDirectoryName,
- LPDWORD lpSectorsPerCluster,
- LPDWORD lpBytesPerSector,
- LPDWORD lpNumberOfFreeClusters, /* Caller will ignore output value */
- LPDWORD lpTotalNumberOfClusters) /* Caller will ignore output value */
-{
- BOOL bRetVal = FALSE;
- pal_statfs fsInfoBuffer;
- INT statfsRetVal = 0;
- DWORD dwLastError = NO_ERROR;
- PathCharString dirNameBufferPathString;
- size_t length;
- char * dirNameBuffer;
- const char * dirName;
- int size;
-
- PERF_ENTRY(GetDiskFreeSpaceW);
- ENTRY( "GetDiskFreeSpaceW( lpDirectoryName=%p (%S), lpSectorsPerCluster=%p,"
- "lpBytesPerSector=%p, lpNumberOfFreeClusters=%p, "
- "lpTotalNumberOfClusters=%p )\n", lpDirectoryName ? lpDirectoryName :
- W16_NULLSTRING, lpDirectoryName ? lpDirectoryName :
- W16_NULLSTRING, lpSectorsPerCluster, lpBytesPerSector,
- lpNumberOfFreeClusters, lpTotalNumberOfClusters );
-
- /* Sanity checks. */
- if ( !lpSectorsPerCluster )
- {
- ERROR( "lpSectorsPerCluster cannot be NULL!\n" );
- dwLastError = ERROR_INVALID_PARAMETER;
- goto exit;
- }
- if ( !lpBytesPerSector )
- {
- ERROR( "lpBytesPerSector cannot be NULL!\n" );
- dwLastError = ERROR_INVALID_PARAMETER;
- goto exit;
- }
- if ( lpNumberOfFreeClusters || lpTotalNumberOfClusters )
- {
- TRACE("GetDiskFreeSpaceW is ignoring lpNumberOfFreeClusters"
- " and lpTotalNumberOfClusters\n" );
- }
- if ( lpDirectoryName && PAL_wcslen( lpDirectoryName ) == 0 )
- {
- ERROR( "lpDirectoryName is empty.\n" );
- dwLastError = ERROR_INVALID_PARAMETER;
- goto exit;
- }
-
- /* Fusion uses this API to round file sizes up to their actual size
- on-disk based on the BytesPerSector * SectorsPerCluster.
- The intent is to avoid computing the sum of all file sizes in the
- cache just in bytes and not account for the cluster-sized slop, when
- determining if the cache is too large or not. */
-
- if ( lpDirectoryName )
- {
- length = (PAL_wcslen(lpDirectoryName)+1) * 3;
- dirNameBuffer = dirNameBufferPathString.OpenStringBuffer(length);
- if (NULL == dirNameBuffer)
- {
- dwLastError = ERROR_NOT_ENOUGH_MEMORY;
- goto exit;
- }
-
- size = WideCharToMultiByte( CP_ACP, 0, lpDirectoryName, -1,
- dirNameBuffer,length, 0, 0 );
- dirNameBufferPathString.CloseBuffer(size);
- if ( size != 0 )
- {
- FILEDosToUnixPathA( dirNameBuffer );
- dirName = dirNameBuffer;
- }
- else
- {
- ASSERT( "Unable to convert the lpDirectoryName to multibyte.\n" );
- dwLastError = ERROR_INTERNAL_ERROR;
- goto exit;
- }
- }
- else
- {
- dirName = "/";
- }
-
- statfsRetVal = statfs( dirName, &fsInfoBuffer );
-
- if ( statfsRetVal == 0 )
- {
- *lpBytesPerSector = fsInfoBuffer.f_bsize;
- *lpSectorsPerCluster = 1;
- bRetVal = TRUE;
- }
- else
- {
- if ( errno == ENOTDIR || errno == ENOENT )
- {
- FILEGetProperNotFoundError( dirNameBuffer, &dwLastError );
- goto exit;
- }
- dwLastError = FILEGetLastErrorFromErrno();
- if ( ERROR_INTERNAL_ERROR == dwLastError )
- {
- ASSERT("statfs() not expected to fail with errno:%d (%s)\n",
- errno, strerror(errno));
- }
- else
- {
- TRACE("statfs() failed, errno:%d (%s)\n", errno, strerror(errno));
- }
- }
-
-exit:
- if ( NO_ERROR != dwLastError )
- {
- SetLastError( dwLastError );
- }
-
- LOGEXIT( "GetDiskFreeSpace returning %s.\n", bRetVal == TRUE ? "TRUE" : "FALSE" );
- PERF_EXIT(GetDiskFreeSpaceW);
- return bRetVal;
-}
-
diff --git a/src/pal/src/file/file.cpp b/src/pal/src/file/file.cpp
index feec65531c..33316266f9 100644
--- a/src/pal/src/file/file.cpp
+++ b/src/pal/src/file/file.cpp
@@ -56,6 +56,12 @@ InternalSetFilePointerForUnixFd(
);
void
+CFileProcessLocalDataCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup
+ );
+
+void
FileCleanupRoutine(
CPalThread *pThread,
IPalObject *pObjectToCleanup,
@@ -68,7 +74,10 @@ CObjectType CorUnix::otFile(
FileCleanupRoutine,
NULL, // No initialization routine
0, // No immutable data
+ NULL, // No immutable data copy routine
+ NULL, // No immutable data cleanup routine
sizeof(CFileProcessLocalData),
+ CFileProcessLocalDataCleanupRoutine,
0, // No shared data
GENERIC_READ|GENERIC_WRITE, // Ignored -- no Win32 object security support
CObjectType::SecuritySupported,
@@ -86,6 +95,34 @@ static CSharedMemoryFileLockMgr _FileLockManager;
IFileLockManager *CorUnix::g_pFileLockManager = &_FileLockManager;
void
+CFileProcessLocalDataCleanupRoutine(
+ CPalThread *pThread,
+ IPalObject *pObjectToCleanup
+ )
+{
+ PAL_ERROR palError;
+ CFileProcessLocalData *pLocalData = NULL;
+ IDataLock *pLocalDataLock = NULL;
+
+ palError = pObjectToCleanup->GetProcessLocalData(
+ pThread,
+ ReadLock,
+ &pLocalDataLock,
+ reinterpret_cast<void**>(&pLocalData)
+ );
+
+ if (NO_ERROR != palError)
+ {
+ ASSERT("Unable to obtain data to cleanup file object");
+ return;
+ }
+
+ free(pLocalData->unix_filename);
+
+ pLocalDataLock->ReleaseLock(pThread, FALSE);
+}
+
+void
FileCleanupRoutine(
CPalThread *pThread,
IPalObject *pObjectToCleanup,
@@ -738,10 +775,12 @@ CorUnix::InternalCreateFile(
goto done;
}
- if (strcpy_s(pLocalData->unix_filename, sizeof(pLocalData->unix_filename), lpUnixPath) != SAFECRT_SUCCESS)
+ _ASSERTE(pLocalData->unix_filename == NULL);
+ pLocalData->unix_filename = strdup(lpUnixPath);
+ if (pLocalData->unix_filename == NULL)
{
- palError = ERROR_INSUFFICIENT_BUFFER;
- TRACE("strcpy_s failed!\n");
+ ASSERT("Unable to copy string\n");
+ palError = ERROR_INTERNAL_ERROR;
goto done;
}
@@ -1211,67 +1250,6 @@ done:
/*++
Function:
- MoveFileA
-
-See MSDN doc.
---*/
-BOOL
-PALAPI
-MoveFileA(
- IN LPCSTR lpExistingFileName,
- IN LPCSTR lpNewFileName)
-{
- BOOL bRet;
-
- PERF_ENTRY(MoveFileA);
- ENTRY("MoveFileA(lpExistingFileName=%p (%s), lpNewFileName=%p (%s))\n",
- lpExistingFileName?lpExistingFileName:"NULL",
- lpExistingFileName?lpExistingFileName:"NULL",
- lpNewFileName?lpNewFileName:"NULL",
- lpNewFileName?lpNewFileName:"NULL");
-
- bRet = MoveFileExA( lpExistingFileName,
- lpNewFileName,
- MOVEFILE_COPY_ALLOWED );
-
- LOGEXIT("MoveFileA returns BOOL %d\n", bRet);
- PERF_EXIT(MoveFileA);
- return bRet;
-}
-
-
-/*++
-Function:
- MoveFileW
-
-See MSDN doc.
---*/
-BOOL
-PALAPI
-MoveFileW(
- IN LPCWSTR lpExistingFileName,
- IN LPCWSTR lpNewFileName)
-{
- BOOL bRet;
-
- PERF_ENTRY(MoveFileW);
- ENTRY("MoveFileW(lpExistingFileName=%p (%S), lpNewFileName=%p (%S))\n",
- lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
- lpExistingFileName?lpExistingFileName:W16_NULLSTRING,
- lpNewFileName?lpNewFileName:W16_NULLSTRING,
- lpNewFileName?lpNewFileName:W16_NULLSTRING);
-
- bRet = MoveFileExW( lpExistingFileName,
- lpNewFileName,
- MOVEFILE_COPY_ALLOWED );
-
- LOGEXIT("MoveFileW returns BOOL %d\n", bRet);
- PERF_EXIT(MoveFileW);
- return bRet;
-}
-
-/*++
-Function:
MoveFileExA
See MSDN doc.
@@ -3152,145 +3130,6 @@ FlushFileBuffers(
return NO_ERROR == palError;
}
-PAL_ERROR
-CorUnix::InternalGetFileType(
- CPalThread *pThread,
- HANDLE hFile,
- DWORD *pdwFileType
- )
-{
- PAL_ERROR palError = NO_ERROR;
- IPalObject *pFileObject = NULL;
- CFileProcessLocalData *pLocalData = NULL;
- IDataLock *pLocalDataLock = NULL;
-
- struct stat stat_data;
-
- if (INVALID_HANDLE_VALUE == hFile)
- {
- ERROR( "Invalid file handle\n" );
- palError = ERROR_INVALID_HANDLE;
- goto InternalGetFileTypeExit;
- }
-
- palError = g_pObjectManager->ReferenceObjectByHandle(
- pThread,
- hFile,
- &aotFile,
- GENERIC_READ,
- &pFileObject
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalGetFileTypeExit;
- }
-
- palError = pFileObject->GetProcessLocalData(
- pThread,
- ReadLock,
- &pLocalDataLock,
- reinterpret_cast<void**>(&pLocalData)
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalGetFileTypeExit;
- }
-
- if (pLocalData->open_flags_deviceaccessonly == TRUE)
- {
- ERROR("File open for device access only\n");
- palError = ERROR_ACCESS_DENIED;
- goto InternalGetFileTypeExit;
- }
-
- if (fstat(pLocalData->unix_fd, &stat_data) != 0)
- {
- ERROR("fstat failed of file descriptor %d\n", pLocalData->unix_fd);
- palError = FILEGetLastErrorFromErrno();
- goto InternalGetFileTypeExit;
- }
-
- TRACE("st_mode & S_IFMT = %#x\n", stat_data.st_mode & S_IFMT);
- if (S_ISREG(stat_data.st_mode) || S_ISDIR(stat_data.st_mode))
- {
- *pdwFileType = FILE_TYPE_DISK;
- }
- else if (S_ISCHR(stat_data.st_mode))
- {
- *pdwFileType = FILE_TYPE_CHAR;
- }
- else if (S_ISFIFO(stat_data.st_mode))
- {
- *pdwFileType = FILE_TYPE_PIPE;
- }
- else
- {
- *pdwFileType = FILE_TYPE_UNKNOWN;
- }
-
-
-InternalGetFileTypeExit:
-
- if (NULL != pLocalDataLock)
- {
- pLocalDataLock->ReleaseLock(pThread, FALSE);
- }
-
- if (NULL != pFileObject)
- {
- pFileObject->ReleaseReference(pThread);
- }
-
- return palError;
-
-}
-
-
-/*++
-Function:
- GetFileType
-
-See MSDN doc.
-
---*/
-DWORD
-PALAPI
-GetFileType(
- IN HANDLE hFile)
-{
- PAL_ERROR palError = NO_ERROR;
- CPalThread *pThread;
- DWORD dwFileType;
-
- PERF_ENTRY(GetFileType);
- ENTRY("GetFileType(hFile=%p)\n", hFile);
-
- pThread = InternalGetCurrentThread();
-
- palError = InternalGetFileType(
- pThread,
- hFile,
- &dwFileType
- );
-
- if (NO_ERROR != palError)
- {
- dwFileType = FILE_TYPE_UNKNOWN;
- pThread->SetLastError(palError);
- }
- else if (FILE_TYPE_UNKNOWN == dwFileType)
- {
- pThread->SetLastError(palError);
- }
-
-
- LOGEXIT("GetFileType returns DWORD %#x\n", dwFileType);
- PERF_EXIT(GetFileType);
- return dwFileType;
-}
-
#define ENSURE_UNIQUE_NOT_ZERO \
if ( uUniqueSeed == 0 ) \
{\
@@ -4280,261 +4119,6 @@ CreatePipe(
return NO_ERROR == palError;
}
-PAL_ERROR
-CorUnix::InternalLockFile(
- CPalThread *pThread,
- HANDLE hFile,
- DWORD dwFileOffsetLow,
- DWORD dwFileOffsetHigh,
- DWORD nNumberOfBytesToLockLow,
- DWORD nNumberOfBytesToLockHigh
- )
-{
- PAL_ERROR palError = NO_ERROR;
- IPalObject *pFileObject = NULL;
- CFileProcessLocalData *pLocalData = NULL;
- IDataLock *pLocalDataLock = NULL;
-
- if (INVALID_HANDLE_VALUE == hFile)
- {
- ERROR( "Invalid file handle\n" );
- palError = ERROR_INVALID_HANDLE;
- goto InternalLockFileExit;
- }
-
- palError = g_pObjectManager->ReferenceObjectByHandle(
- pThread,
- hFile,
- &aotFile,
- GENERIC_READ,
- &pFileObject
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalLockFileExit;
- }
-
- palError = pFileObject->GetProcessLocalData(
- pThread,
- ReadLock,
- &pLocalDataLock,
- reinterpret_cast<void**>(&pLocalData)
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalLockFileExit;
- }
-
- if (NULL != pLocalData->pLockController)
- {
- palError = pLocalData->pLockController->CreateFileLock(
- pThread,
- dwFileOffsetLow,
- dwFileOffsetHigh,
- nNumberOfBytesToLockLow,
- nNumberOfBytesToLockHigh,
- IFileLockController::ExclusiveFileLock,
- IFileLockController::FailImmediately
- );
- }
- else
- {
- //
- // This isn't a lockable file (e.g., it may be a pipe)
- //
-
- palError = ERROR_ACCESS_DENIED;
- goto InternalLockFileExit;
- }
-
-InternalLockFileExit:
-
- if (NULL != pLocalDataLock)
- {
- pLocalDataLock->ReleaseLock(pThread, FALSE);
- }
-
- if (NULL != pFileObject)
- {
- pFileObject->ReleaseReference(pThread);
- }
-
- return palError;
-}
-
-
-/*++
-Function:
- LockFile
-
-See MSDN doc.
---*/
-PALIMPORT
-BOOL
-PALAPI
-LockFile(HANDLE hFile,
- DWORD dwFileOffsetLow,
- DWORD dwFileOffsetHigh,
- DWORD nNumberOfBytesToLockLow,
- DWORD nNumberOfBytesToLockHigh)
-{
- CPalThread *pThread;
- PAL_ERROR palError = NO_ERROR;
-
- PERF_ENTRY(LockFile);
- ENTRY("LockFile(hFile:%p, offsetLow:%u, offsetHigh:%u, nbBytesLow:%u,"
- " nbBytesHigh:%u\n", hFile, dwFileOffsetLow, dwFileOffsetHigh,
- nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
-
- pThread = InternalGetCurrentThread();
-
- palError = InternalLockFile(
- pThread,
- hFile,
- dwFileOffsetLow,
- dwFileOffsetHigh,
- nNumberOfBytesToLockLow,
- nNumberOfBytesToLockHigh
- );
-
- if (NO_ERROR != palError)
- {
- pThread->SetLastError(palError);
- }
-
- LOGEXIT("LockFile returns %s\n", NO_ERROR == palError ? "TRUE":"FALSE");
- PERF_EXIT(LockFile);
- return NO_ERROR == palError;
-}
-
-PAL_ERROR
-CorUnix::InternalUnlockFile(
- CPalThread *pThread,
- HANDLE hFile,
- DWORD dwFileOffsetLow,
- DWORD dwFileOffsetHigh,
- DWORD nNumberOfBytesToUnlockLow,
- DWORD nNumberOfBytesToUnlockHigh
- )
-{
- PAL_ERROR palError = NO_ERROR;
- IPalObject *pFileObject = NULL;
- CFileProcessLocalData *pLocalData = NULL;
- IDataLock *pLocalDataLock = NULL;
-
- if (INVALID_HANDLE_VALUE == hFile)
- {
- ERROR( "Invalid file handle\n" );
- palError = ERROR_INVALID_HANDLE;
- goto InternalUnlockFileExit;
- }
-
- palError = g_pObjectManager->ReferenceObjectByHandle(
- pThread,
- hFile,
- &aotFile,
- GENERIC_READ,
- &pFileObject
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalUnlockFileExit;
- }
-
- palError = pFileObject->GetProcessLocalData(
- pThread,
- ReadLock,
- &pLocalDataLock,
- reinterpret_cast<void**>(&pLocalData)
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalUnlockFileExit;
- }
-
- if (NULL != pLocalData->pLockController)
- {
- palError = pLocalData->pLockController->ReleaseFileLock(
- pThread,
- dwFileOffsetLow,
- dwFileOffsetHigh,
- nNumberOfBytesToUnlockLow,
- nNumberOfBytesToUnlockHigh
- );
- }
- else
- {
- //
- // This isn't a lockable file (e.g., it may be a pipe)
- //
-
- palError = ERROR_ACCESS_DENIED;
- goto InternalUnlockFileExit;
- }
-
-InternalUnlockFileExit:
-
- if (NULL != pLocalDataLock)
- {
- pLocalDataLock->ReleaseLock(pThread, FALSE);
- }
-
- if (NULL != pFileObject)
- {
- pFileObject->ReleaseReference(pThread);
- }
-
- return palError;
-}
-
-/*++
-Function:
- UnlockFile
-
-See MSDN doc.
---*/
-PALIMPORT
-BOOL
-PALAPI
-UnlockFile(HANDLE hFile,
- DWORD dwFileOffsetLow,
- DWORD dwFileOffsetHigh,
- DWORD nNumberOfBytesToUnlockLow,
- DWORD nNumberOfBytesToUnlockHigh)
-{
- CPalThread *pThread;
- PAL_ERROR palError = NO_ERROR;
-
- PERF_ENTRY(UnlockFile);
- ENTRY("UnlockFile(hFile:%p, offsetLow:%u, offsetHigh:%u, nbBytesLow:%u,"
- "nbBytesHigh:%u\n", hFile, dwFileOffsetLow, dwFileOffsetHigh,
- nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
-
- pThread = InternalGetCurrentThread();
-
- palError = InternalUnlockFile(
- pThread,
- hFile,
- dwFileOffsetLow,
- dwFileOffsetHigh,
- nNumberOfBytesToUnlockLow,
- nNumberOfBytesToUnlockHigh
- );
-
- if (NO_ERROR != palError)
- {
- pThread->SetLastError(palError);
- }
-
- LOGEXIT("UnlockFile returns %s\n", NO_ERROR == palError ? "TRUE" : "FALSE");
- PERF_EXIT(UnlockFile);
- return NO_ERROR == palError;
-}
-
/*++
init_std_handle [static]
diff --git a/src/pal/src/file/filetime.cpp b/src/pal/src/file/filetime.cpp
index ca36e04d9d..6cc8dacc72 100644
--- a/src/pal/src/file/filetime.cpp
+++ b/src/pal/src/file/filetime.cpp
@@ -148,351 +148,6 @@ CompareFileTime(
}
-
-/*++
-Function:
- SetFileTime
-
-Notes: This function will drop one digit (radix 10) of precision from
-the supplied times, since Unix can set to the microsecond (at most, i.e.
-if the futimes() function is available).
-
-As noted in the file header, there is no analog to "creation time" on Unix
-systems, so the lpCreationTime argument to this function will always be
-ignored, and the inode change time will be set to the current time.
---*/
-BOOL
-PALAPI
-SetFileTime(
- IN HANDLE hFile,
- IN CONST FILETIME *lpCreationTime,
- IN CONST FILETIME *lpLastAccessTime,
- IN CONST FILETIME *lpLastWriteTime)
-{
- CPalThread *pThread;
- PAL_ERROR palError = NO_ERROR;
- const UINT64 MAX_FILETIMEVALUE = 0x8000000000000000LL;
-
- PERF_ENTRY(SetFileTime);
- ENTRY("SetFileTime(hFile=%p, lpCreationTime=%p, lpLastAccessTime=%p, "
- "lpLastWriteTime=%p)\n", hFile, lpCreationTime, lpLastAccessTime,
- lpLastWriteTime);
-
- pThread = InternalGetCurrentThread();
-
- /* validate filetime values */
- if ( (lpCreationTime && (((UINT64)lpCreationTime->dwHighDateTime << 32) +
- lpCreationTime->dwLowDateTime >= MAX_FILETIMEVALUE)) ||
- (lpLastAccessTime && (((UINT64)lpLastAccessTime->dwHighDateTime << 32) +
- lpLastAccessTime->dwLowDateTime >= MAX_FILETIMEVALUE)) ||
- (lpLastWriteTime && (((UINT64)lpLastWriteTime->dwHighDateTime << 32) +
- lpLastWriteTime->dwLowDateTime >= MAX_FILETIMEVALUE)))
- {
- pThread->SetLastError(ERROR_INVALID_HANDLE);
- return FALSE;
- }
-
- palError = InternalSetFileTime(
- pThread,
- hFile,
- lpCreationTime,
- lpLastAccessTime,
- lpLastWriteTime
- );
-
- if (NO_ERROR != palError)
- {
- pThread->SetLastError(palError);
- }
-
- LOGEXIT("SetFileTime returns BOOL %s\n", NO_ERROR == palError ? "TRUE":"FALSE");
- PERF_EXIT(SetFileTime);
- return NO_ERROR == palError;
-}
-
-PAL_ERROR
-CorUnix::InternalSetFileTime(
- CPalThread *pThread,
- IN HANDLE hFile,
- IN CONST FILETIME *lpCreationTime,
- IN CONST FILETIME *lpLastAccessTime,
- IN CONST FILETIME *lpLastWriteTime)
-{
- PAL_ERROR palError = NO_ERROR;
- IPalObject *pFileObject = NULL;
- CFileProcessLocalData *pLocalData = NULL;
- IDataLock *pLocalDataLock = NULL;
- struct timeval Times[2];
- int fd;
- long nsec;
- struct stat stat_buf;
-
- if (INVALID_HANDLE_VALUE == hFile)
- {
- ERROR( "Invalid file handle\n" );
- palError = ERROR_INVALID_HANDLE;
- goto InternalSetFileTimeExit;
- }
-
- palError = g_pObjectManager->ReferenceObjectByHandle(
- pThread,
- hFile,
- &aotFile,
- GENERIC_READ,
- &pFileObject
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalSetFileTimeExit;
- }
-
- palError = pFileObject->GetProcessLocalData(
- pThread,
- ReadLock,
- &pLocalDataLock,
- reinterpret_cast<void**>(&pLocalData)
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalSetFileTimeExit;
- }
-
- if (lpCreationTime)
- {
- palError = ERROR_NOT_SUPPORTED;
- goto InternalSetFileTimeExit;
- }
-
- if( !lpLastAccessTime && !lpLastWriteTime )
- {
- // if both pointers are NULL, the function simply returns.
- goto InternalSetFileTimeExit;
- }
- else if( !lpLastAccessTime || !lpLastWriteTime )
- {
- // if either pointer is NULL, fstat will need to be called.
- fd = pLocalData->unix_fd;
- if ( fd == -1 )
- {
- TRACE("pLocalData = [%p], fd = %d\n", pLocalData, fd);
- palError = ERROR_INVALID_HANDLE;
- goto InternalSetFileTimeExit;
- }
-
- if ( fstat(fd, &stat_buf) != 0 )
- {
- TRACE("fstat failed on file descriptor %d\n", fd);
- palError = FILEGetLastErrorFromErrno();
- goto InternalSetFileTimeExit;
- }
- }
-
- if (lpLastAccessTime)
- {
- Times[0].tv_sec = FILEFileTimeToUnixTime( *lpLastAccessTime, &nsec );
- Times[0].tv_usec = nsec / 1000; /* convert to microseconds */
- }
- else
- {
- Times[0].tv_sec = stat_buf.st_atime;
- Times[0].tv_usec = ST_ATIME_NSEC(&stat_buf) / 1000;
- }
-
- if (lpLastWriteTime)
- {
- Times[1].tv_sec = FILEFileTimeToUnixTime( *lpLastWriteTime, &nsec );
- Times[1].tv_usec = nsec / 1000; /* convert to microseconds */
- }
- else
- {
- Times[1].tv_sec = stat_buf.st_mtime;
- Times[1].tv_usec = ST_MTIME_NSEC(&stat_buf) / 1000;
- }
-
- TRACE("Setting atime = [%ld.%ld], mtime = [%ld.%ld]\n",
- Times[0].tv_sec, Times[0].tv_usec,
- Times[1].tv_sec, Times[1].tv_usec);
-
-#if HAVE_FUTIMES
- if ( futimes(pLocalData->unix_fd, Times) != 0 )
-#elif HAVE_UTIMES
- if ( utimes(pLocalData->unix_filename, Times) != 0 )
-#else
- #error Operating system not supported
-#endif
- {
- palError = FILEGetLastErrorFromErrno();
- }
-
-InternalSetFileTimeExit:
- if (NULL != pLocalDataLock)
- {
- pLocalDataLock->ReleaseLock(pThread, FALSE);
- }
-
- if (NULL != pFileObject)
- {
- pFileObject->ReleaseReference(pThread);
- }
-
- return palError;
-}
-
-
-/*++
-Function:
- GetFileTime
-
-Notes: As noted at the top of this file, there is no analog to "creation
-time" on Unix systems, so the inode change time is used instead. Also, Win32
-LastAccessTime is updated after a write operation, but it is not on Unix.
-To be consistent with Win32, this function returns the greater of mtime and
-atime for LastAccessTime.
---*/
-BOOL
-PALAPI
-GetFileTime(
- IN HANDLE hFile,
- OUT LPFILETIME lpCreationTime,
- OUT LPFILETIME lpLastAccessTime,
- OUT LPFILETIME lpLastWriteTime)
-{
- CPalThread *pThread;
- PAL_ERROR palError = NO_ERROR;
-
- PERF_ENTRY(GetFileTime);
- ENTRY("GetFileTime(hFile=%p, lpCreationTime=%p, lpLastAccessTime=%p, "
- "lpLastWriteTime=%p)\n",
- hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
-
- pThread = InternalGetCurrentThread();
-
- palError = InternalGetFileTime(
- pThread,
- hFile,
- lpCreationTime,
- lpLastAccessTime,
- lpLastWriteTime
- );
-
- if (NO_ERROR != palError)
- {
- pThread->SetLastError(palError);
- }
-
- LOGEXIT("GetFileTime returns BOOL %s\n", NO_ERROR == palError ? "TRUE":"FALSE");
- PERF_EXIT(GetFileTime);
- return NO_ERROR == palError;
-}
-
-PAL_ERROR
-CorUnix::InternalGetFileTime(
- CPalThread *pThread,
- IN HANDLE hFile,
- OUT LPFILETIME lpCreationTime,
- OUT LPFILETIME lpLastAccessTime,
- OUT LPFILETIME lpLastWriteTime)
-{
- PAL_ERROR palError = NO_ERROR;
- IPalObject *pFileObject = NULL;
- CFileProcessLocalData *pLocalData = NULL;
- IDataLock *pLocalDataLock = NULL;
- int Fd = -1;
-
- struct stat StatData;
-
- if (INVALID_HANDLE_VALUE == hFile)
- {
- ERROR( "Invalid file handle\n" );
- palError = ERROR_INVALID_HANDLE;
- goto InternalGetFileTimeExit;
- }
-
- palError = g_pObjectManager->ReferenceObjectByHandle(
- pThread,
- hFile,
- &aotFile,
- GENERIC_READ,
- &pFileObject
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalGetFileTimeExit;
- }
-
- palError = pFileObject->GetProcessLocalData(
- pThread,
- ReadLock,
- &pLocalDataLock,
- reinterpret_cast<void**>(&pLocalData)
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalGetFileTimeExit;
- }
-
- Fd = pLocalData->unix_fd;
-
- if ( Fd == -1 )
- {
- TRACE("pLocalData = [%p], Fd = %d\n", pLocalData, Fd);
- palError = ERROR_INVALID_HANDLE;
- goto InternalGetFileTimeExit;
- }
-
- if ( fstat(Fd, &StatData) != 0 )
- {
- TRACE("fstat failed on file descriptor %d\n", Fd);
- palError = FILEGetLastErrorFromErrno();
- goto InternalGetFileTimeExit;
- }
-
- if ( lpCreationTime )
- {
- *lpCreationTime = FILEUnixTimeToFileTime(StatData.st_ctime,
- ST_CTIME_NSEC(&StatData));
- }
- if ( lpLastWriteTime )
- {
- *lpLastWriteTime = FILEUnixTimeToFileTime(StatData.st_mtime,
- ST_MTIME_NSEC(&StatData));
- }
- if ( lpLastAccessTime )
- {
- *lpLastAccessTime = FILEUnixTimeToFileTime(StatData.st_atime,
- ST_ATIME_NSEC(&StatData));
- /* if Unix mtime is greater than atime, return mtime as the last
- access time */
- if ( lpLastWriteTime &&
- CompareFileTime(lpLastAccessTime, lpLastWriteTime) < 0 )
- {
- *lpLastAccessTime = *lpLastWriteTime;
- }
- }
-
-InternalGetFileTimeExit:
- if (NULL != pLocalDataLock)
- {
- pLocalDataLock->ReleaseLock(pThread, FALSE);
- }
-
- if (NULL != pFileObject)
- {
- pFileObject->ReleaseReference(pThread);
- }
-
- return palError;
-}
-
-
-
-
-
-
/*++
Function:
GetSystemTimeAsFileTime
@@ -711,84 +366,3 @@ BOOL PALAPI FileTimeToSystemTime( CONST FILETIME * lpFileTime,
}
}
-
-
-/**
-Function:
- FileTimeToDosDateTime
-
- Notes due to the difference between how BSD and Windows
- calculates time, this function can only repersent dates between
- 1980 and 2037. 2037 is the upperlimit for the BSD time functions( 1900 -
- 2037 range ).
-
-See msdn for more details.
---*/
-BOOL
-PALAPI
-FileTimeToDosDateTime(
- IN CONST FILETIME *lpFileTime,
- OUT LPWORD lpFatDate,
- OUT LPWORD lpFatTime )
-{
- BOOL bRetVal = FALSE;
-
- PERF_ENTRY(FileTimeToDosDateTime);
- ENTRY( "FileTimeToDosDateTime( lpFileTime=%p, lpFatDate=%p, lpFatTime=%p )\n",
- lpFileTime, lpFatDate, lpFatTime );
-
- /* Sanity checks. */
- if ( !lpFileTime || !lpFatDate || !lpFatTime )
- {
- ERROR( "Incorrect parameters.\n" );
- SetLastError( ERROR_INVALID_PARAMETER );
- }
- else
- {
- /* Do conversion. */
- SYSTEMTIME SysTime;
- if ( FileTimeToSystemTime( lpFileTime, &SysTime ) )
- {
- if ( SysTime.wYear >= 1980 && SysTime.wYear <= 2037 )
- {
- *lpFatDate = 0;
- *lpFatTime = 0;
-
- *lpFatDate |= ( SysTime.wDay & 0x1F );
- *lpFatDate |= ( ( SysTime.wMonth & 0xF ) << 5 );
- *lpFatDate |= ( ( ( SysTime.wYear - 1980 ) & 0x7F ) << 9 );
-
- if ( SysTime.wSecond % 2 == 0 )
- {
- *lpFatTime |= ( ( SysTime.wSecond / 2 ) & 0x1F );
- }
- else
- {
- *lpFatTime |= ( ( SysTime.wSecond / 2 + 1 ) & 0x1F );
- }
-
- *lpFatTime |= ( ( SysTime.wMinute & 0x3F ) << 5 );
- *lpFatTime |= ( ( SysTime.wHour & 0x1F ) << 11 );
-
- bRetVal = TRUE;
- }
- else
- {
- ERROR( "The function can only repersent dates between 1/1/1980"
- " and 12/31/2037\n" );
- SetLastError( ERROR_INVALID_PARAMETER );
- }
- }
- else
- {
- ERROR( "Unable to convert file time to system time.\n" );
- SetLastError( ERROR_INVALID_PARAMETER );
- bRetVal = FALSE;
- }
- }
-
- LOGEXIT( "returning BOOL %d\n", bRetVal );
- PERF_EXIT(FileTimeToDosDateTime);
- return bRetVal;
-}
-
diff --git a/src/pal/src/file/shmfilelockmgr.cpp b/src/pal/src/file/shmfilelockmgr.cpp
index a586abddbd..6deed61d65 100644
--- a/src/pal/src/file/shmfilelockmgr.cpp
+++ b/src/pal/src/file/shmfilelockmgr.cpp
@@ -102,7 +102,7 @@ CSharedMemoryFileLockMgr::GetLockControllerForFile(
)
{
PAL_ERROR palError = NO_ERROR;
- SHMPTR shmFileLocks = SHMNULL;
+ SHMPTR shmFileLocks = NULL;
SHMFILELOCKS* fileLocks = NULL;
CSharedMemoryFileLockController *pController = NULL;
@@ -214,7 +214,7 @@ CSharedMemoryFileLockMgr::GetLockControllerForFile(
// don't attempt to free it below.
//
- shmFileLocks = SHMNULL;
+ shmFileLocks = NULL;
/* set the share mode again, it's possible that the share mode is now more
restrictive than the previous mode set. */
@@ -247,7 +247,7 @@ GetLockControllerForFileExit:
pController->ReleaseController();
}
- if (SHMNULL != shmFileLocks)
+ if (NULL != shmFileLocks)
{
FILECleanUpLockedRgn(
shmFileLocks,
@@ -269,13 +269,13 @@ CSharedMemoryFileLockMgr::GetFileShareModeForFile(
{
PAL_ERROR palError = NO_ERROR;
*pdwShareMode = SHARE_MODE_NOT_INITALIZED;
- SHMPTR shmFileLocks = SHMNULL;
+ SHMPTR shmFileLocks = NULL;
SHMFILELOCKS* fileLocks = NULL;
SHMLock();
palError = FILEGetSHMFileLocks(szFileName, &shmFileLocks, TRUE);
- if (NO_ERROR != palError || shmFileLocks == SHMNULL)
+ if (NO_ERROR != palError || shmFileLocks == NULL)
{
goto GetLockControllerForFileExit;
}
@@ -291,7 +291,7 @@ CSharedMemoryFileLockMgr::GetFileShareModeForFile(
GetLockControllerForFileExit:
- if (SHMNULL != shmFileLocks)
+ if (NULL != shmFileLocks)
{
FILECleanUpLockedRgn(
shmFileLocks,
@@ -425,7 +425,7 @@ CSharedMemoryFileLockController::ReleaseFileLock(
void
CSharedMemoryFileLockController::ReleaseController()
{
- if (SHMNULL != m_shmFileLocks)
+ if (NULL != m_shmFileLocks)
{
FILECleanUpLockedRgn(
m_shmFileLocks,
@@ -667,7 +667,7 @@ FILEUnlockFileRegion(
{
prevLock->next = curLockRgn->next;
}
- SHMfree(shmcurLockRgn);
+ free(shmcurLockRgn);
}
else
{
@@ -735,7 +735,7 @@ FILEGetSHMFileLocks(
TRACE("Create a new entry in the file lock list in SHM\n");
/* Create a new entry in the file lock list in SHM */
- if ((shmPtrRet = SHMalloc(sizeof(SHMFILELOCKS))) == 0)
+ if ((shmPtrRet = malloc(sizeof(SHMFILELOCKS))) == 0)
{
ERROR("Can't allocate SHMFILELOCKS structure\n");
palError = ERROR_NOT_ENOUGH_MEMORY;
@@ -749,7 +749,7 @@ FILEGetSHMFileLocks(
goto CLEANUP1;
}
- filelocksPtr->unix_filename = SHMStrDup(filename);
+ filelocksPtr->unix_filename = strdup(filename);
if (filelocksPtr->unix_filename == 0)
{
ERROR("Can't allocate shared memory for filename\n");
@@ -781,9 +781,9 @@ FILEGetSHMFileLocks(
goto EXIT;
CLEANUP2:
- SHMfree(filelocksPtr->unix_filename);
+ free(filelocksPtr->unix_filename);
CLEANUP1:
- SHMfree(shmPtrRet);
+ free(shmPtrRet);
shmPtrRet = 0;
EXIT:
SHMRelease();
@@ -808,7 +808,7 @@ FILEAddNewLockedRgn(
{
PAL_ERROR palError = NO_ERROR;
SHMFILELOCKRGNS *newLockRgn, *lockRgnPtr;
- SHMPTR shmNewLockRgn = SHMNULL;
+ SHMPTR shmNewLockRgn = NULL;
if ((fileLocks == NULL) || (pvControllerInstance == NULL))
{
@@ -822,7 +822,7 @@ FILEAddNewLockedRgn(
TRACE("Create a new entry for the new lock region (%I64u %I64u)\n",
lockRgnStart, nbBytesToLock);
- if ((shmNewLockRgn = SHMalloc(sizeof(SHMFILELOCKRGNS))) == SHMNULL)
+ if ((shmNewLockRgn = malloc(sizeof(SHMFILELOCKRGNS))) == NULL)
{
ERROR("Can't allocate SHMFILELOCKRGNS structure\n");
palError = ERROR_NOT_ENOUGH_MEMORY;
@@ -897,9 +897,9 @@ FILEAddNewLockedRgn(
EXIT:
- if (NO_ERROR != palError && SHMNULL != shmNewLockRgn)
+ if (NO_ERROR != palError && NULL != shmNewLockRgn)
{
- SHMfree(shmNewLockRgn);
+ free(shmNewLockRgn);
}
SHMRelease();
@@ -950,7 +950,7 @@ FILECleanUpLockedRgn(
{
/* removing the first lock */
fileLocks->fileLockedRgns = curLockRgn->next;
- SHMfree(shmcurLockRgn);
+ free(shmcurLockRgn);
shmcurLockRgn = fileLocks->fileLockedRgns;
if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
{
@@ -961,7 +961,7 @@ FILECleanUpLockedRgn(
else
{
prevLock->next = curLockRgn->next;
- SHMfree(shmcurLockRgn);
+ free(shmcurLockRgn);
shmcurLockRgn = prevLock->next;
if (SHMPTR_TO_TYPED_PTR_BOOL(SHMFILELOCKRGNS, curLockRgn, shmcurLockRgn) == FALSE)
{
@@ -1020,9 +1020,9 @@ FILECleanUpLockedRgn(
}
if (fileLocks->unix_filename)
- SHMfree(fileLocks->unix_filename);
+ free(fileLocks->unix_filename);
- SHMfree(shmFileLocks);
+ free(shmFileLocks);
}
}
EXIT:
diff --git a/src/pal/src/include/pal/context.h b/src/pal/src/include/pal/context.h
index 2c86a03d69..bd6797760d 100644
--- a/src/pal/src/include/pal/context.h
+++ b/src/pal/src/include/pal/context.h
@@ -151,15 +151,21 @@ using asm_sigcontext::_xstate;
#ifdef XSTATE_SUPPORTED
+#if HAVE_FPSTATE_GLIBC_RESERVED1
+#define FPSTATE_RESERVED __glibc_reserved1
+#else
+#define FPSTATE_RESERVED padding
+#endif
+
inline _fpx_sw_bytes *FPREG_FpxSwBytes(const ucontext_t *uc)
{
// Bytes 464..511 in the FXSAVE format are available for software to use for any purpose. In this case, they are used to
// indicate information about extended state.
- _ASSERTE(reinterpret_cast<UINT8 *>(&FPREG_Fpstate(uc)->padding[12]) - reinterpret_cast<UINT8 *>(FPREG_Fpstate(uc)) == 464);
+ _ASSERTE(reinterpret_cast<UINT8 *>(&FPREG_Fpstate(uc)->FPSTATE_RESERVED[12]) - reinterpret_cast<UINT8 *>(FPREG_Fpstate(uc)) == 464);
_ASSERTE(FPREG_Fpstate(uc) != nullptr);
- return reinterpret_cast<_fpx_sw_bytes *>(&FPREG_Fpstate(uc)->padding[12]);
+ return reinterpret_cast<_fpx_sw_bytes *>(&FPREG_Fpstate(uc)->FPSTATE_RESERVED[12]);
}
inline UINT32 FPREG_ExtendedSize(const ucontext_t *uc)
diff --git a/src/pal/src/include/pal/corunix.hpp b/src/pal/src/include/pal/corunix.hpp
index e9e9503ed3..bfdfb6c167 100644
--- a/src/pal/src/include/pal/corunix.hpp
+++ b/src/pal/src/include/pal/corunix.hpp
@@ -173,6 +173,15 @@ namespace CorUnix
void * // pProcessLocalData
);
+ typedef void (*OBJECT_IMMUTABLE_DATA_COPY_ROUTINE) (
+ void *,
+ void *);
+ typedef void (*OBJECT_IMMUTABLE_DATA_CLEANUP_ROUTINE) (
+ void *);
+ typedef void (*OBJECT_PROCESS_LOCAL_DATA_CLEANUP_ROUTINE) (
+ CPalThread *, // pThread
+ IPalObject *);
+
enum PalObjectTypeId
{
otiAutoResetEvent = 0,
@@ -315,7 +324,10 @@ namespace CorUnix
OBJECTCLEANUPROUTINE m_pCleanupRoutine;
OBJECTINITROUTINE m_pInitRoutine;
DWORD m_dwImmutableDataSize;
+ OBJECT_IMMUTABLE_DATA_COPY_ROUTINE m_pImmutableDataCopyRoutine;
+ OBJECT_IMMUTABLE_DATA_CLEANUP_ROUTINE m_pImmutableDataCleanupRoutine;
DWORD m_dwProcessLocalDataSize;
+ OBJECT_PROCESS_LOCAL_DATA_CLEANUP_ROUTINE m_pProcessLocalDataCleanupRoutine;
DWORD m_dwSharedDataSize;
DWORD m_dwSupportedAccessRights;
// Generic access rights mapping
@@ -335,7 +347,10 @@ namespace CorUnix
OBJECTCLEANUPROUTINE pCleanupRoutine,
OBJECTINITROUTINE pInitRoutine,
DWORD dwImmutableDataSize,
+ OBJECT_IMMUTABLE_DATA_COPY_ROUTINE pImmutableDataCopyRoutine,
+ OBJECT_IMMUTABLE_DATA_CLEANUP_ROUTINE pImmutableDataCleanupRoutine,
DWORD dwProcessLocalDataSize,
+ OBJECT_PROCESS_LOCAL_DATA_CLEANUP_ROUTINE pProcessLocalDataCleanupRoutine,
DWORD dwSharedDataSize,
DWORD dwSupportedAccessRights,
SecuritySupport eSecuritySupport,
@@ -352,7 +367,10 @@ namespace CorUnix
m_pCleanupRoutine(pCleanupRoutine),
m_pInitRoutine(pInitRoutine),
m_dwImmutableDataSize(dwImmutableDataSize),
+ m_pImmutableDataCopyRoutine(pImmutableDataCopyRoutine),
+ m_pImmutableDataCleanupRoutine(pImmutableDataCleanupRoutine),
m_dwProcessLocalDataSize(dwProcessLocalDataSize),
+ m_pProcessLocalDataCleanupRoutine(pProcessLocalDataCleanupRoutine),
m_dwSharedDataSize(dwSharedDataSize),
m_dwSupportedAccessRights(dwSupportedAccessRights),
m_eSecuritySupport(eSecuritySupport),
@@ -408,6 +426,38 @@ namespace CorUnix
return m_dwImmutableDataSize;
};
+ void
+ SetImmutableDataCopyRoutine(
+ OBJECT_IMMUTABLE_DATA_COPY_ROUTINE ptr
+ )
+ {
+ m_pImmutableDataCopyRoutine = ptr;
+ };
+
+ OBJECT_IMMUTABLE_DATA_COPY_ROUTINE
+ GetImmutableDataCopyRoutine(
+ void
+ )
+ {
+ return m_pImmutableDataCopyRoutine;
+ };
+
+ void
+ SetImmutableDataCleanupRoutine(
+ OBJECT_IMMUTABLE_DATA_CLEANUP_ROUTINE ptr
+ )
+ {
+ m_pImmutableDataCleanupRoutine = ptr;
+ };
+
+ OBJECT_IMMUTABLE_DATA_CLEANUP_ROUTINE
+ GetImmutableDataCleanupRoutine(
+ void
+ )
+ {
+ return m_pImmutableDataCleanupRoutine;
+ }
+
DWORD
GetProcessLocalDataSize(
void
@@ -415,7 +465,15 @@ namespace CorUnix
{
return m_dwProcessLocalDataSize;
};
-
+
+ OBJECT_PROCESS_LOCAL_DATA_CLEANUP_ROUTINE
+ GetProcessLocalDataCleanupRoutine(
+ void
+ )
+ {
+ return m_pProcessLocalDataCleanupRoutine;
+ }
+
DWORD
GetSharedDataSize(
void
diff --git a/src/pal/src/include/pal/event.hpp b/src/pal/src/include/pal/event.hpp
index 98eeaee5db..21dc478835 100644
--- a/src/pal/src/include/pal/event.hpp
+++ b/src/pal/src/include/pal/event.hpp
@@ -44,15 +44,6 @@ namespace CorUnix
HANDLE hEvent,
BOOL fSetEvent
);
-
- PAL_ERROR
- InternalOpenEvent(
- CPalThread *pThread,
- DWORD dwDesiredAccess,
- BOOL bInheritHandle,
- LPCWSTR lpName,
- HANDLE *phEvent
- );
}
diff --git a/src/pal/src/include/pal/file.hpp b/src/pal/src/include/pal/file.hpp
index 5acccb0a24..b34ad75dc2 100644
--- a/src/pal/src/include/pal/file.hpp
+++ b/src/pal/src/include/pal/file.hpp
@@ -44,7 +44,7 @@ namespace CorUnix
In Windows we can open a file for writing only */
int open_flags; /* stores Unix file creation flags */
BOOL open_flags_deviceaccessonly;
- char unix_filename[MAXPATHLEN];
+ CHAR *unix_filename;
BOOL inheritable;
};
@@ -102,13 +102,6 @@ namespace CorUnix
);
PAL_ERROR
- InternalGetFileType(
- CPalThread *pThread,
- HANDLE hFile,
- DWORD *pdwFileType
- );
-
- PAL_ERROR
InternalCreatePipe(
CPalThread *pThread,
HANDLE *phReadPipe,
@@ -118,26 +111,6 @@ namespace CorUnix
);
PAL_ERROR
- InternalLockFile(
- CPalThread *pThread,
- HANDLE hFile,
- DWORD dwFileOffsetLow,
- DWORD dwFileOffsetHigh,
- DWORD nNumberOfBytesToLockLow,
- DWORD nNumberOfBytesToLockHigh
- );
-
- PAL_ERROR
- InternalUnlockFile(
- CPalThread *pThread,
- HANDLE hFile,
- DWORD dwFileOffsetLow,
- DWORD dwFileOffsetHigh,
- DWORD nNumberOfBytesToUnlockLow,
- DWORD nNumberOfBytesToUnlockHigh
- );
-
- PAL_ERROR
InternalSetFilePointer(
CPalThread *pThread,
HANDLE hFile,
@@ -147,24 +120,6 @@ namespace CorUnix
PLONG lpNewFilePointerLow
);
- PAL_ERROR
- InternalSetFileTime(
- CPalThread *pThread,
- IN HANDLE hFile,
- IN CONST FILETIME *lpCreationTime,
- IN CONST FILETIME *lpLastAccessTime,
- IN CONST FILETIME *lpLastWriteTime
- );
-
- PAL_ERROR
- InternalGetFileTime(
- CPalThread *pThread,
- IN HANDLE hFile,
- OUT LPFILETIME lpCreationTime,
- OUT LPFILETIME lpLastAccessTime,
- OUT LPFILETIME lpLastWriteTime
- );
-
BOOL
RealPathHelper(LPCSTR lpUnixPath, PathCharString& lpBuffer);
/*++
diff --git a/src/pal/src/include/pal/identity.hpp b/src/pal/src/include/pal/identity.hpp
deleted file mode 100644
index bd64a659ac..0000000000
--- a/src/pal/src/include/pal/identity.hpp
+++ /dev/null
@@ -1,57 +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.
-
-/*++
-
-
-
-Module Name:
-
- include/pal/identity.hpp
-
-Abstract:
-
- Header file for identity functions.
-
-
-
---*/
-
-#ifndef _PAL_IDENTITY_HPP_
-#define _PAL_IDENTITY_HPP_
-
-#include "config.h"
-#include "pal/palinternal.h"
-
-/*++
-
-Function:
- IdentityInitialize
-
---*/
-BOOL IdentityInitialize();
-
-/*++
-Function:
- IdentityCleanup
-
---*/
-VOID IdentityCleanup();
-
-#if HAVE_GETPWUID_R
-namespace CorUnix
-{
- int
- InternalGetpwuid_r(
- CPalThread *pPalThread,
- uid_t uid,
- struct passwd *pPasswd,
- char *pchBuffer,
- size_t nBufSize,
- struct passwd **ppResult
- );
-}
-#endif /* HAVE_GETPWUID_R */
-
-#endif /* _PAL_IDENTITY_HPP_ */
diff --git a/src/pal/src/include/pal/init.h b/src/pal/src/include/pal/init.h
index d478ed275b..0f4c672b97 100644
--- a/src/pal/src/include/pal/init.h
+++ b/src/pal/src/include/pal/init.h
@@ -39,6 +39,8 @@ void PALCommonCleanup();
extern Volatile<INT> init_count;
+extern SIZE_T g_defaultStackSize;
+
/*++
MACRO:
PALIsInitialized
diff --git a/src/pal/src/include/pal/map.hpp b/src/pal/src/include/pal/map.hpp
index 854e6c549a..7bcb20a404 100644
--- a/src/pal/src/include/pal/map.hpp
+++ b/src/pal/src/include/pal/map.hpp
@@ -144,7 +144,7 @@ namespace CorUnix
class CFileMappingImmutableData
{
public:
- CHAR szFileName[MAXPATHLEN];
+ CHAR *lpFileName;
UINT MaxSize; // The max size of the file mapping object
DWORD flProtect; // Protection desired for the file view
BOOL bPALCreatedTempFile; // TRUE if it's a PAL created file
@@ -179,15 +179,6 @@ namespace CorUnix
);
PAL_ERROR
- InternalOpenFileMapping(
- CPalThread *pThread,
- DWORD dwDesiredAccess,
- BOOL bInheritHandle,
- LPCWSTR lpName,
- HANDLE *phMapping
- );
-
- PAL_ERROR
InternalMapViewOfFile(
CPalThread *pThread,
HANDLE hFileMappingObject,
diff --git a/src/pal/src/include/pal/palinternal.h b/src/pal/src/include/pal/palinternal.h
index 48e2f3c683..12312a3018 100644
--- a/src/pal/src/include/pal/palinternal.h
+++ b/src/pal/src/include/pal/palinternal.h
@@ -637,6 +637,9 @@ typedef enum _TimeConversionConstants
#ifdef __cplusplus
}
+bool
+ReadMemoryValueFromFile(const char* filename, size_t* val);
+
/* This is duplicated in utilcode.h for CLR, with cooler type-traits */
template <typename T>
inline
diff --git a/src/pal/src/include/pal/semaphore.hpp b/src/pal/src/include/pal/semaphore.hpp
index 2943d61c3d..33cf35bc27 100644
--- a/src/pal/src/include/pal/semaphore.hpp
+++ b/src/pal/src/include/pal/semaphore.hpp
@@ -49,15 +49,6 @@ namespace CorUnix
LONG lReleaseCount,
LPLONG lpPreviousCount
);
-
- PAL_ERROR
- InternalOpenSemaphore(
- CPalThread *pThread,
- DWORD dwDesiredAccess,
- BOOL bInheritHandle,
- LPCWSTR lpName,
- HANDLE *phSemaphore
- );
}
diff --git a/src/pal/src/include/pal/shm.hpp b/src/pal/src/include/pal/shm.hpp
index de1d09e636..36d6fd412f 100644
--- a/src/pal/src/include/pal/shm.hpp
+++ b/src/pal/src/include/pal/shm.hpp
@@ -28,47 +28,7 @@ Abstract:
// isn't considered a pointer type...
//
-#define SHMNULL 0
-
-#ifndef _DEBUG
-
-inline
-void *
-ShmPtrToPtrFast(SHMPTR shmptr)
-{
- void *pv = NULL;
-
- if (SHMNULL != shmptr)
- {
- int segment = shmptr >> 24;
-
- if (segment < shm_numsegments)
- {
- pv = reinterpret_cast<void*>(
- reinterpret_cast<DWORD_PTR>(shm_segment_bases[(uint)segment].Load())
- + (shmptr & 0x00FFFFFF)
- );
- }
- else
- {
- pv = SHMPtrToPtr(shmptr);
- }
- }
-
- return pv;
-}
-
-//
-// We could use a function template here to avoid the cast / macro
-//
-
-#define SHMPTR_TO_TYPED_PTR(type, shmptr) reinterpret_cast<type*>(ShmPtrToPtrFast((shmptr)))
-
-#else
-
-#define SHMPTR_TO_TYPED_PTR(type, shmptr) reinterpret_cast<type*>(SHMPtrToPtr((shmptr)))
-
-#endif
+#define SHMPTR_TO_TYPED_PTR(type, shmptr) reinterpret_cast<type*>(shmptr)
/* Set ptr to NULL if shmPtr == 0, else set ptr to SHMPTR_TO_TYPED_PTR(type, shmptr)
return FALSE if SHMPTR_TO_TYPED_PTR returns NULL ptr from non null shmptr,
@@ -76,8 +36,5 @@ ShmPtrToPtrFast(SHMPTR shmptr)
#define SHMPTR_TO_TYPED_PTR_BOOL(type, ptr, shmptr) \
((shmptr != 0) ? ((ptr = SHMPTR_TO_TYPED_PTR(type, shmptr)) != NULL) : ((ptr = NULL) == NULL))
-
-
-
#endif // _SHM_HPP_
diff --git a/src/pal/src/include/pal/shmemory.h b/src/pal/src/include/pal/shmemory.h
index 5ca848148c..bf12de123a 100644
--- a/src/pal/src/include/pal/shmemory.h
+++ b/src/pal/src/include/pal/shmemory.h
@@ -15,56 +15,7 @@ Abstract:
How to use :
-The SHMalloc function can be used to allocate memory in the shared memory area.
-It returns a value of type SHMPTR, which will be useable in all participating
-processes. The SHMPTR_TO_PTR macro can be used to convert a SHMPTR value into
-an address valid *only* within the current process. Do NOT store pointers in
-shared memory, since those will not be valid for other processes. If you need
-to construct linked lists or other strctures that usually use pointers, use
-SHMPTR values instead of pointers. In addition, Lock/Release functions must be
-used when manipulating data in shared memory, to ensure inter-process synchronization.
-
-Example :
-
-//a simple linked list type
-typedef struct
-{
-int count;
-SHMPTR string;
-SHMPTR next;
-}SHMLIST;
-
-// Allocate a new list item
-SHMPTR new_item = SHMalloc(sizeof(SHMLIST));
-
-// get a pointer to it
-SHMLIST *item_ptr = (SHMLIST *)SHMPTR_TO_PTR(new_item);
-
-// Allocate memory for the "string" member, initialize it
-item_ptr->string = SHMalloc(strlen("string"));
-LPSTR str_ptr = (LPSTR)SHMPTR_TO_PTR(item_ptr->string);
-strcpy(str_ptr, "string");
-
-//Take the shared memory lock to prevent anyone from modifying the linked list
-SHMLock();
-
-//get the list's head from somewhere
-SHMPTR list_head = get_list_head();
-
-//link the list to our new item
-item_ptr->next = list_head
-
-//get a pointer to the list head's structure
-SHMLIST *head_ptr = (SHMLIST *)SHMPTR_TO_PTR(list_head);
-
-//set the new item's count value based on the head's count value
-item_ptr->count = head_ptr->count + 1;
-
-//save the new item as the new head of the list
-set_list_head(new_item);
-
-//We're done modifying the list, release the lock
-SHMRelease
+Lock/Release functions must be used when manipulating data in shared memory, to ensure inter-process synchronization.
@@ -79,86 +30,17 @@ extern "C"
#endif // __cplusplus
/*
-Type for shared memory blocks. use SHMPTR_TO_PTR to get a useable address.
+Type for shared memory blocks
*/
-typedef DWORD_PTR SHMPTR;
-
-#define MAX_SEGMENTS 256
-
+typedef LPVOID SHMPTR;
typedef enum {
- SIID_PROCESS_INFO,/* pointers to PROCESS structures? */
SIID_NAMED_OBJECTS,
SIID_FILE_LOCKS,
SIID_LAST
} SHM_INFO_ID;
-typedef enum
-{
- SHM_NAMED_MAPPINGS, /* structs with map name, file name & flags? */
- SHM_NAMED_EVENTS, /* structs with event names & ThreadWaitingList struct? */
- SHM_NAMED_MUTEXS, /* structs with mutext names, and ThreadWaitingList struct */
-
- SHM_NAMED_LAST
-} SHM_NAMED_OBJECTS_ID;
-
-typedef struct _SMNO
-{
- SHM_NAMED_OBJECTS_ID ObjectType;
- SHMPTR ShmNext;
- SHMPTR ShmObjectName;
- SHMPTR ShmSelf;
-
-}SHM_NAMED_OBJECTS, * PSHM_NAMED_OBJECTS;
-
-
-/*
-SHMPTR_TO_PTR
-
-Macro to convert a SHMPTR value into a valid (for this process) pointer.
-
-In debug builds, we always call the function to do full checks.
-In release builds, check if the segment is known, and if it is, do only minimal
-validation (if segment is unknown, we have to call the function)
- */
-#if _DEBUG
-
-#define SHMPTR_TO_PTR(shmptr) \
- SHMPtrToPtr(shmptr)
-
-#else /* !_DEBUG */
-
-extern int shm_numsegments;
-
-/* array containing the base address of each segment */
-extern Volatile<LPVOID> shm_segment_bases[MAX_SEGMENTS];
-
-#define SHMPTR_TO_PTR(shmptr)\
- ((shmptr)?(((static_cast<int>(shmptr)>>24)<shm_numsegments)?\
- reinterpret_cast<LPVOID>(reinterpret_cast<size_t>(shm_segment_bases[static_cast<int>(shmptr)>>24].Load())+(static_cast<int>(shmptr)&0x00FFFFFF)):\
- SHMPtrToPtr(shmptr)): static_cast<LPVOID>(NULL))
-
-
-#endif /* _DEBUG */
-
-/* Set ptr to NULL if shmPtr == 0, else set ptr to SHMPTR_TO_PTR(shmptr)
- return FALSE if SHMPTR_TO_PTR returns NULL ptr from non null shmptr,
- TRUE otherwise */
-#define SHMPTR_TO_PTR_BOOL(ptr, shmptr) \
- ((shmptr != 0) ? ((ptr = SHMPTR_TO_PTR(shmptr)) != NULL) : ((ptr = NULL) == NULL))
-
-/*++
-SHMPtrToPtr
-
-Convert a SHMPTR value into a useable pointer.
-
-Unlike the macro defined above, this function performs as much validation as
-possible, and can handle cases when the SHMPTR is located in an aread of shared
-memory the process doesn't yet know about.
---*/
-LPVOID SHMPtrToPtr(SHMPTR shmptr);
-
/*++
SHMInitialize
@@ -176,37 +58,6 @@ registered processes, and remove all shared memory files if no process remains
void SHMCleanup(void);
/*++
-SHMalloc
-
-Allocate a block of memory of the specified size
-
-Parameters :
- size_t size : size of block required
-
-Return value :
- A SHMPTR identifying the new block, or 0 on failure. Use SHMPtrToPtr to
- convert a SHMPTR into a useable pointer (but remember to lock the shared
- memory first!)
-
-Notes :
- SHMalloc will fail if the requested size is larger than a certain maximum.
- At the moment, the maximum is 520 bytes (MAX_PATH_FNAME*2).
---*/
-SHMPTR SHMalloc(size_t size);
-
-/*++
-SHMfree
-
-Release a block of shared memory and put it back in the shared memory pool
-
-Parameters :
- SHMPTR shmptr : identifier of block to release
-
-(no return value)
---*/
-void SHMfree(SHMPTR shmptr);
-
-/*++
SHMLock
Restrict shared memory access to the current thread of the current process
@@ -266,63 +117,6 @@ Notes :
--*/
BOOL SHMSetInfo(SHM_INFO_ID element, SHMPTR value);
-
-/********************** Shared memory help functions ********************/
-
-/*++
-SHMStrDup
-
-Duplicates the string in shared memory.
-
-Returns the new address as SHMPTR on success.
-Returns (SHMPTR)NULL on failure.
---*/
-SHMPTR SHMStrDup( LPCSTR string );
-
-/*++
-SHMWStrDup
-
-Duplicates the wide string in shared memory.
-
-Returns the new address as SHMPTR on success.
-Returns (SHMPTR)NULL on failure.
---*/
-SHMPTR SHMWStrDup( LPCWSTR string );
-
-
-/*++
-SHMFindNamedObjectByName
-
-Searches for an object whose name matches the name and ID passed in.
-
-Returns a SHMPTR to its location in shared memory. If no object
-matches the name, the function returns NULL and sets pbNameExists to FALSE.
-If an object matches the name but is of a different type, the function
-returns NULL and sets pbNameExists to TRUE.
-
---*/
-SHMPTR SHMFindNamedObjectByName( LPCWSTR lpName, SHM_NAMED_OBJECTS_ID oid,
- BOOL *pbNameExists );
-
-/*++
-SHMRemoveNamedObject
-
-Removes the specified named object from the list
-
-No return.
-
-note : the caller is reponsible for releasing all associated memory
---*/
-void SHMRemoveNamedObject( SHMPTR shmNamedObject );
-
-/*++ SHMAddNamedObject
-
-Adds the specified named object to the list.
-
-No return.
---*/
-void SHMAddNamedObject( SHMPTR shmNewNamedObject );
-
#ifdef __cplusplus
}
#endif // __cplusplus
diff --git a/src/pal/src/include/pal/synchcache.hpp b/src/pal/src/include/pal/synchcache.hpp
index c172842292..0abc7ddcd3 100644
--- a/src/pal/src/include/pal/synchcache.hpp
+++ b/src/pal/src/include/pal/synchcache.hpp
@@ -252,7 +252,7 @@ namespace CorUnix
SharedID Get(CPalThread * pthrCurrent)
{
- SharedID shridObj = NULLSharedID;
+ SharedID shridObj = NULL;
Get(pthrCurrent, 1, &shridObj);
return shridObj;
@@ -291,8 +291,8 @@ namespace CorUnix
{
for (k=0; k<m_iMaxDepth/PreAllocFactor-n+i; k++)
{
- shridObj = RawSharedObjectAlloc(sizeof(USHRSynchCacheStackNode), DefaultSharedPool);
- if (NULLSharedID == shridObj)
+ shridObj = malloc(sizeof(USHRSynchCacheStackNode));
+ if (NULL == shridObj)
{
Flush(pthrCurrent, true);
break;
@@ -312,8 +312,8 @@ namespace CorUnix
for (j=i;j<n;j++)
{
- shridObj = RawSharedObjectAlloc(sizeof(USHRSynchCacheStackNode), DefaultSharedPool);
- if (NULLSharedID == shridObj)
+ shridObj = malloc(sizeof(USHRSynchCacheStackNode));
+ if (NULL == shridObj)
break;
#ifdef _DEBUG
pvObjRaw = SharedIDToPointer(shridObj);
@@ -333,7 +333,7 @@ namespace CorUnix
void Add(CPalThread * pthrCurrent, SharedID shridObj)
{
- if (NULLSharedID == shridObj)
+ if (NULL == shridObj)
{
return;
}
@@ -360,7 +360,7 @@ namespace CorUnix
}
else
{
- RawSharedObjectFree(shridObj);
+ free(shridObj);
}
Unlock(pthrCurrent);
}
@@ -387,7 +387,7 @@ namespace CorUnix
pTemp = pNode;
pNode = pNode->pointers.pNext;
shridTemp = pTemp->pointers.shrid;
- RawSharedObjectFree(shridTemp);
+ free(shridTemp);
}
}
};
diff --git a/src/pal/src/include/pal/synchobjects.hpp b/src/pal/src/include/pal/synchobjects.hpp
index aa3a8f1aa6..62f4017492 100644
--- a/src/pal/src/include/pal/synchobjects.hpp
+++ b/src/pal/src/include/pal/synchobjects.hpp
@@ -29,13 +29,8 @@ Abstract:
#include <pthread.h>
#define SharedID SHMPTR
-#define SharedPoolId ULONG_PTR
-#define DefaultSharedPool ((ULONG_PTR)0)
-#define NULLSharedID ((SHMPTR)NULL)
#define SharedIDToPointer(shID) SHMPTR_TO_TYPED_PTR(PVOID, shID)
#define SharedIDToTypePointer(TYPE,shID) SHMPTR_TO_TYPED_PTR(TYPE, shID)
-#define RawSharedObjectAlloc(szSize, shPoolId) SHMalloc(szSize)
-#define RawSharedObjectFree(shID) SHMfree(shID)
namespace CorUnix
{
diff --git a/src/pal/src/init/pal.cpp b/src/pal/src/init/pal.cpp
index fea00b5ee6..117117a8b0 100644
--- a/src/pal/src/init/pal.cpp
+++ b/src/pal/src/init/pal.cpp
@@ -99,6 +99,9 @@ Volatile<LONG> g_coreclrInitialized = 0;
static BOOL g_fThreadDataAvailable = FALSE;
static pthread_mutex_t init_critsec_mutex = PTHREAD_MUTEX_INITIALIZER;
+// The default minimum stack size
+SIZE_T g_defaultStackSize = 0;
+
/* critical section to protect access to init_count. This is allocated on the
very first PAL_Initialize call, and is freed afterward. */
static PCRITICAL_SECTION init_critsec = NULL;
@@ -169,6 +172,66 @@ PAL_InitializeDLL()
return Initialize(0, NULL, PAL_INITIALIZE_DLL);
}
+#ifndef __GLIBC__
+/*++
+Function:
+ EnsureStackSize
+
+Abstract:
+ This fixes a problem on MUSL where the initial stack size reported by the
+ pthread_attr_getstack is about 128kB, but this limit is not fixed and
+ the stack can grow dynamically. The problem is that it makes the
+ functions ReflectionInvocation::[Try]EnsureSufficientExecutionStack
+ to fail for real life scenarios like e.g. compilation of corefx.
+ Since there is no real fixed limit for the stack, the code below
+ ensures moving the stack limit to a value that makes reasonable
+ real life scenarios work.
+
+--*/
+__attribute__((noinline,optnone))
+void
+EnsureStackSize(SIZE_T stackSize)
+{
+ volatile uint8_t *s = (uint8_t *)_alloca(stackSize);
+ *s = 0;
+}
+#endif // __GLIBC__
+
+/*++
+Function:
+ InitializeDefaultStackSize
+
+Abstract:
+ Initializes the default stack size.
+
+--*/
+void
+InitializeDefaultStackSize()
+{
+ char* defaultStackSizeStr = getenv("COMPlus_DefaultStackSize");
+ if (defaultStackSizeStr != NULL)
+ {
+ errno = 0;
+ // Like all numeric values specific by the COMPlus_xxx variables, it is a
+ // hexadecimal string without any prefix.
+ long int size = strtol(defaultStackSizeStr, NULL, 16);
+
+ if (errno == 0)
+ {
+ g_defaultStackSize = max(size, PTHREAD_STACK_MIN);
+ }
+ }
+
+#ifndef __GLIBC__
+ if (g_defaultStackSize == 0)
+ {
+ // Set the default minimum stack size for MUSL to the same value as we
+ // use on Windows.
+ g_defaultStackSize = 1536 * 1024;
+ }
+#endif // __GLIBC__
+}
+
/*++
Function:
Initialize
@@ -245,6 +308,12 @@ Initialize(
fFirstTimeInit = true;
+ InitializeDefaultStackSize();
+
+#ifndef __GLIBC__
+ EnsureStackSize(g_defaultStackSize);
+#endif // __GLIBC__
+
// Initialize the TLS lookaside cache
if (FALSE == TLSInitialize())
{
diff --git a/src/pal/src/locale/unicode.cpp b/src/pal/src/locale/unicode.cpp
index 69214735d1..97d4731198 100644
--- a/src/pal/src/locale/unicode.cpp
+++ b/src/pal/src/locale/unicode.cpp
@@ -283,61 +283,6 @@ CharNextExA(
}
-
-/*++
-Function:
-AreFileApisANSI
-
-The AreFileApisANSI function determines whether the file I/O functions
-are using the ANSI or OEM character set code page. This function is
-useful for 8-bit console input and output operations.
-
-Return Values
-
-If the set of file I/O functions is using the ANSI code page, the return value is nonzero.
-
-If the set of file I/O functions is using the OEM code page, the return value is zero.
-
-In the ROTOR version we always return true since there is no concept
-of OEM code pages.
-
---*/
-BOOL
-PALAPI
-AreFileApisANSI(
- VOID)
-{
- PERF_ENTRY(AreFileApisANSI);
- ENTRY("AreFileApisANSI ()\n");
-
- LOGEXIT("AreFileApisANSI returns BOOL TRUE\n");
- PERF_EXIT(AreFileApisANSI);
- return TRUE;
-}
-
-
-/*++
-Function:
-GetConsoleCP
-
-See MSDN doc.
---*/
-UINT
-PALAPI
-GetConsoleCP(
- VOID)
-{
- UINT nRet = 0;
- PERF_ENTRY(GetConsoleCP);
- ENTRY("GetConsoleCP()\n");
-
- nRet = GetACP();
-
- LOGEXIT("GetConsoleCP returns UINT %d\n", nRet );
- PERF_EXIT(GetConsoleCP);
- return nRet;
-}
-
/*++
Function:
GetConsoleOutputCP
diff --git a/src/pal/src/map/map.cpp b/src/pal/src/map/map.cpp
index 60950cee2a..4a435f8cef 100644
--- a/src/pal/src/map/map.cpp
+++ b/src/pal/src/map/map.cpp
@@ -126,12 +126,26 @@ FileMappingInitializationRoutine(
void *pProcessLocalData
);
+void
+CFileMappingImmutableDataCopyRoutine(
+ void *pImmData,
+ void *pImmDataTarget
+ );
+
+void
+CFileMappingImmutableDataCleanupRoutine(
+ void *pImmData
+ );
+
CObjectType CorUnix::otFileMapping(
otiFileMapping,
FileMappingCleanupRoutine,
FileMappingInitializationRoutine,
sizeof(CFileMappingImmutableData),
+ CFileMappingImmutableDataCopyRoutine,
+ CFileMappingImmutableDataCleanupRoutine,
sizeof(CFileMappingProcessLocalData),
+ NULL, // No process local data cleanup routine
0,
PAGE_READWRITE | PAGE_READONLY | PAGE_WRITECOPY,
CObjectType::SecuritySupported,
@@ -147,6 +161,33 @@ CObjectType CorUnix::otFileMapping(
CAllowedObjectTypes aotFileMapping(otiFileMapping);
void
+CFileMappingImmutableDataCopyRoutine(
+ void *pImmData,
+ void *pImmDataTarget
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CFileMappingImmutableData *pImmutableData = (CFileMappingImmutableData *) pImmData;
+ CFileMappingImmutableData *pImmutableDataTarget = (CFileMappingImmutableData *) pImmDataTarget;
+
+ if (NULL != pImmutableData->lpFileName)
+ {
+ pImmutableDataTarget->lpFileName = strdup(pImmutableData->lpFileName);
+ }
+}
+
+void
+CFileMappingImmutableDataCleanupRoutine(
+ void *pImmData
+ )
+{
+ PAL_ERROR palError = NO_ERROR;
+ CFileMappingImmutableData *pImmutableData = (CFileMappingImmutableData *) pImmData;
+
+ free(pImmutableData->lpFileName);
+}
+
+void
FileMappingCleanupRoutine(
CPalThread *pThread,
IPalObject *pObjectToCleanup,
@@ -179,7 +220,7 @@ FileMappingCleanupRoutine(
if (pImmutableData->bPALCreatedTempFile)
{
- unlink(pImmutableData->szFileName);
+ unlink(pImmutableData->lpFileName);
}
}
@@ -240,7 +281,7 @@ FileMappingInitializationRoutine(
reinterpret_cast<CFileMappingProcessLocalData *>(pvProcessLocalData);
pProcessLocalData->UnixFd = InternalOpen(
- pImmutableData->szFileName,
+ pImmutableData->lpFileName,
MAPProtectionToFileOpenFlags(pImmutableData->flProtect) | O_CLOEXEC
);
@@ -496,16 +537,18 @@ CorUnix::InternalCreateFileMapping(
//
/* Anonymous mapped files. */
- if (strcpy_s(pImmutableData->szFileName, sizeof(pImmutableData->szFileName), "/dev/zero") != SAFECRT_SUCCESS)
+ _ASSERTE(pImmutableData->lpFileName == NULL);
+ pImmutableData->lpFileName = strdup("/dev/zero");
+ if (pImmutableData->lpFileName == NULL)
{
- ERROR( "strcpy_s failed!\n" );
+ ASSERT("Unable to copy string\n");
palError = ERROR_INTERNAL_ERROR;
goto ExitInternalCreateFileMapping;
}
#if HAVE_MMAP_DEV_ZERO
- UnixFd = InternalOpen(pImmutableData->szFileName, O_RDWR | O_CLOEXEC);
+ UnixFd = InternalOpen(pImmutableData->lpFileName, O_RDWR | O_CLOEXEC);
if ( -1 == UnixFd )
{
ERROR( "Unable to open the file.\n");
@@ -593,10 +636,12 @@ CorUnix::InternalCreateFileMapping(
}
goto ExitInternalCreateFileMapping;
}
-
- if (strcpy_s(pImmutableData->szFileName, sizeof(pImmutableData->szFileName), pFileLocalData->unix_filename) != SAFECRT_SUCCESS)
+
+ _ASSERTE(pImmutableData->lpFileName == NULL);
+ pImmutableData->lpFileName = strdup(pFileLocalData->unix_filename);
+ if (pImmutableData->lpFileName == NULL)
{
- ERROR( "strcpy_s failed!\n" );
+ ASSERT("Unable to copy string\n");
palError = ERROR_INTERNAL_ERROR;
if (NULL != pFileLocalDataLock)
{
@@ -618,7 +663,7 @@ CorUnix::InternalCreateFileMapping(
/* Create a temporary file on the filesystem in order to be
shared across processes. */
- palError = MAPCreateTempFile(pThread, &UnixFd, pImmutableData->szFileName);
+ palError = MAPCreateTempFile(pThread, &UnixFd, pImmutableData->lpFileName);
if (NO_ERROR != palError)
{
ERROR("Unable to create the temporary file.\n");
@@ -766,7 +811,7 @@ ExitInternalCreateFileMapping:
if (bPALCreatedTempFile)
{
- unlink(pImmutableData->szFileName);
+ unlink(pImmutableData->lpFileName);
}
if (-1 != UnixFd)
@@ -876,63 +921,6 @@ OpenFileMappingW(
return hFileMapping;
}
-PAL_ERROR
-CorUnix::InternalOpenFileMapping(
- CPalThread *pThread,
- DWORD dwDesiredAccess,
- BOOL bInheritHandle,
- LPCWSTR lpName,
- HANDLE *phMapping
- )
-{
- PAL_ERROR palError = NO_ERROR;
- IPalObject *pFileMapping = NULL;
- CPalString sObjectName(lpName);
-
- if ( MAPContainsInvalidFlags( dwDesiredAccess ) )
- {
- ASSERT( "dwDesiredAccess can be one or more of FILE_MAP_READ, "
- "FILE_MAP_WRITE, FILE_MAP_COPY or FILE_MAP_ALL_ACCESS.\n" );
- palError = ERROR_INVALID_PARAMETER;
- goto ExitInternalOpenFileMapping;
- }
-
- palError = g_pObjectManager->LocateObject(
- pThread,
- &sObjectName,
- &aotFileMapping,
- &pFileMapping
- );
-
- if (NO_ERROR != palError)
- {
- goto ExitInternalOpenFileMapping;
- }
-
- palError = g_pObjectManager->ObtainHandleForObject(
- pThread,
- pFileMapping,
- dwDesiredAccess,
- bInheritHandle,
- NULL,
- phMapping
- );
-
- if (NO_ERROR != palError)
- {
- goto ExitInternalOpenFileMapping;
- }
-
-ExitInternalOpenFileMapping:
-
- if (NULL != pFileMapping)
- {
- pFileMapping->ReleaseReference(pThread);
- }
-
- return palError;
-}
-
/*++
Function:
MapViewOfFile
@@ -1053,79 +1041,6 @@ MapViewOfFileEx(
return pvMappedBaseAddress;
}
-/*++
-Function:
- FlushViewOfFile
-
-See MSDN doc.
---*/
-BOOL
-PALAPI
-FlushViewOfFile(
- IN LPVOID lpBaseAddress,
- IN SIZE_T dwNumberOfBytesToFlush)
-{
- PAL_ERROR palError = NO_ERROR;
- CPalThread *pThread = NULL;
- PMAPPED_VIEW_LIST pView = NULL;
- BOOL fResult = TRUE;
-
- PERF_ENTRY(FlushViewOfFile);
- ENTRY("FlushViewOfFile(lpBaseAddress=%p, dwNumberOfBytesToFlush=%u)\n",
- lpBaseAddress, dwNumberOfBytesToFlush);
-
- pThread = InternalGetCurrentThread();
-
- InternalEnterCriticalSection(pThread, &mapping_critsec);
-
- pView = MAPGetViewForAddress(lpBaseAddress);
- if (NULL == pView)
- {
- ERROR("lpBaseAddress has to be the address returned by MapViewOfFile[Ex]");
- palError = ERROR_INVALID_HANDLE;
- goto Exit;
- }
-
- if (dwNumberOfBytesToFlush == 0)
- {
- dwNumberOfBytesToFlush = pView->NumberOfBytesToMap;
- }
-
- // <ROTORTODO>we should only use MS_SYNC if the file has been opened
- // with FILE_FLAG_WRITE_THROUGH
- if (msync(lpBaseAddress, dwNumberOfBytesToFlush, MS_SYNC) == -1)
- {
- if (errno == EINVAL)
- {
- WARN("msync failed; %s\n", strerror(errno));
- palError = ERROR_INVALID_PARAMETER;
- }
- else if (errno == EIO)
- {
- WARN("msync failed; %s\n", strerror(errno));
- palError = ERROR_WRITE_FAULT;
- }
- else
- {
- ERROR("msync failed; %s\n", strerror(errno));
- palError = ERROR_INTERNAL_ERROR;
- }
- }
-
-Exit:
- InternalLeaveCriticalSection(pThread, &mapping_critsec);
-
- if (NO_ERROR != palError)
- {
- fResult = FALSE;
- pThread->SetLastError(palError);
- }
-
- LOGEXIT("FlushViewOfFile returning %d.\n", fResult);
- PERF_EXIT(FlushViewOfFile);
- return fResult;
-}
-
/*++
Function:
diff --git a/src/pal/src/memory/heap.cpp b/src/pal/src/memory/heap.cpp
index 5757da83b7..ccee77ff50 100644
--- a/src/pal/src/memory/heap.cpp
+++ b/src/pal/src/memory/heap.cpp
@@ -63,28 +63,6 @@ RtlMoveMemory(
/*++
Function:
- RtlZeroMemory
-
-See MSDN doc.
---*/
-VOID
-PALAPI
-RtlZeroMemory(
- PVOID Destination,
- SIZE_T Length
-)
-{
- PERF_ENTRY(RtlZeroMemory);
- ENTRY("RtlZeroMemory(Destination:%p, Length:%x)\n", Destination, Length);
-
- memset(Destination, 0, Length);
-
- LOGEXIT("RtlZeroMemory returning.\n");
- PERF_EXIT(RtlZeroMemory);
-}
-
-/*++
-Function:
HeapCreate
See MSDN doc.
diff --git a/src/pal/src/misc/cgroup.cpp b/src/pal/src/misc/cgroup.cpp
index 52059302b5..ec0a0bd5fb 100644
--- a/src/pal/src/misc/cgroup.cpp
+++ b/src/pal/src/misc/cgroup.cpp
@@ -9,7 +9,7 @@ Module Name:
cgroup.cpp
Abstract:
- Read memory limits for the current process
+ Read memory and cpu limits for the current process
--*/
#include "pal/dbgmsg.h"
@@ -22,41 +22,23 @@ SET_DEFAULT_DEBUG_CHANNEL(MISC);
#define PROC_CGROUP_FILENAME "/proc/self/cgroup"
#define PROC_STATM_FILENAME "/proc/self/statm"
#define MEM_LIMIT_FILENAME "/memory.limit_in_bytes"
-
+#define CFS_QUOTA_FILENAME "/cpu.cfs_quota_us"
+#define CFS_PERIOD_FILENAME "/cpu.cfs_period_us"
class CGroup
{
char *m_memory_cgroup_path;
+ char *m_cpu_cgroup_path;
public:
CGroup()
{
- m_memory_cgroup_path = nullptr;
- char* memoryHierarchyMount = nullptr;
- char *cgroup_path_relative_to_mount = nullptr;
- size_t len;
- memoryHierarchyMount = FindMemoryHierarchyMount();
- if (memoryHierarchyMount == nullptr)
- goto done;
-
- cgroup_path_relative_to_mount = FindCGroupPathForMemorySubsystem();
- if (cgroup_path_relative_to_mount == nullptr)
- goto done;
-
- len = strlen(memoryHierarchyMount);
- len += strlen(cgroup_path_relative_to_mount);
- m_memory_cgroup_path = (char*)PAL_malloc(len+1);
- if (m_memory_cgroup_path == nullptr)
- goto done;
-
- strcpy_s(m_memory_cgroup_path, len+1, memoryHierarchyMount);
- strcat_s(m_memory_cgroup_path, len+1, cgroup_path_relative_to_mount);
-
- done:
- PAL_free(memoryHierarchyMount);
- PAL_free(cgroup_path_relative_to_mount);
+ m_memory_cgroup_path = FindCgroupPath(&IsMemorySubsystem);
+ m_cpu_cgroup_path = FindCgroupPath(&IsCpuSubsystem);
}
+
~CGroup()
{
PAL_free(m_memory_cgroup_path);
+ PAL_free(m_cpu_cgroup_path);
}
bool GetPhysicalMemoryLimit(size_t *val)
@@ -79,14 +61,92 @@ public:
PAL_free(mem_limit_filename);
return result;
}
+
+ bool GetCpuLimit(UINT *val)
+ {
+ long long quota;
+ long long period;
+ long long cpu_count;
+
+ quota = ReadCpuCGroupValue(CFS_QUOTA_FILENAME);
+ if (quota <= 0)
+ return false;
+
+ period = ReadCpuCGroupValue(CFS_PERIOD_FILENAME);
+ if (period <= 0)
+ return false;
+
+ // Cannot have less than 1 CPU
+ if (quota <= period)
+ {
+ *val = 1;
+ return true;
+ }
+
+ cpu_count = quota / period;
+ if (cpu_count < UINT_MAX)
+ {
+ *val = cpu_count;
+ }
+ else
+ {
+ *val = UINT_MAX;
+ }
+
+ return true;
+ }
+
private:
- char* FindMemoryHierarchyMount()
+ static bool IsMemorySubsystem(const char *strTok){
+ return strcmp("memory", strTok) == 0;
+ }
+
+ static bool IsCpuSubsystem(const char *strTok){
+ return strcmp("cpu", strTok) == 0;
+ }
+
+ static char* FindCgroupPath(bool (*is_subsystem)(const char *)){
+ char *cgroup_path = nullptr;
+ char *hierarchy_mount = nullptr;
+ char *hierarchy_root = nullptr;
+ char *cgroup_path_relative_to_mount = nullptr;
+ size_t len;
+
+ FindHierarchyMount(is_subsystem, &hierarchy_mount, &hierarchy_root);
+ if (hierarchy_mount == nullptr || hierarchy_root == nullptr)
+ goto done;
+
+ cgroup_path_relative_to_mount = FindCGroupPathForSubsystem(is_subsystem);
+ if (cgroup_path_relative_to_mount == nullptr)
+ goto done;
+
+ len = strlen(hierarchy_mount);
+ len += strlen(cgroup_path_relative_to_mount);
+ cgroup_path = (char*)PAL_malloc(len+1);
+ if (cgroup_path == nullptr)
+ goto done;
+
+ strcpy_s(cgroup_path, len+1, hierarchy_mount);
+ // For a host cgroup, we need to append the relative path.
+ // In a docker container, the root and relative path are the same and we don't need to append.
+ if (strcmp(hierarchy_root, cgroup_path_relative_to_mount) != 0)
+ strcat_s(cgroup_path, len+1, cgroup_path_relative_to_mount);
+
+ done:
+ PAL_free(hierarchy_mount);
+ PAL_free(hierarchy_root);
+ PAL_free(cgroup_path_relative_to_mount);
+ return cgroup_path;
+ }
+
+ static void FindHierarchyMount(bool (*is_subsystem)(const char *), char** pmountpath, char** pmountroot)
{
char *line = nullptr;
size_t lineLen = 0, maxLineLen = 0;
char *filesystemType = nullptr;
char *options = nullptr;
- char* mountpath = nullptr;
+ char *mountpath = nullptr;
+ char *mountroot = nullptr;
FILE *mountinfofile = fopen(PROC_MOUNTINFO_FILENAME, "r");
if (mountinfofile == nullptr)
@@ -106,11 +166,11 @@ private:
goto done;
maxLineLen = lineLen;
}
- char* separatorChar = strchr(line, '-');
+ char* separatorChar = strstr(line, " - ");;
// See man page of proc to get format for /proc/self/mountinfo file
int sscanfRet = sscanf_s(separatorChar,
- "- %s %*s %s",
+ " - %s %*s %s",
filesystemType, lineLen+1,
options, lineLen+1);
if (sscanfRet != 2)
@@ -125,21 +185,26 @@ private:
char* strTok = strtok_s(options, ",", &context);
while (strTok != nullptr)
{
- if (strncmp("memory", strTok, 6) == 0)
+ if (is_subsystem(strTok))
{
mountpath = (char*)PAL_malloc(lineLen+1);
if (mountpath == nullptr)
goto done;
+ mountroot = (char*)PAL_malloc(lineLen+1);
+ if (mountroot == nullptr)
+ goto done;
sscanfRet = sscanf_s(line,
- "%*s %*s %*s %*s %s ",
+ "%*s %*s %*s %s %s ",
+ mountroot, lineLen+1,
mountpath, lineLen+1);
- if (sscanfRet != 1)
- {
- PAL_free(mountpath);
- mountpath = nullptr;
+ if (sscanfRet != 2)
_ASSERTE(!"Failed to parse mount info file contents with sscanf_s.");
- }
+
+ // assign the output arguments and clear the locals so we don't free them.
+ *pmountpath = mountpath;
+ *pmountroot = mountroot;
+ mountpath = mountroot = nullptr;
goto done;
}
strTok = strtok_s(nullptr, ",", &context);
@@ -147,15 +212,16 @@ private:
}
}
done:
+ PAL_free(mountpath);
+ PAL_free(mountroot);
PAL_free(filesystemType);
PAL_free(options);
free(line);
if (mountinfofile)
fclose(mountinfofile);
- return mountpath;
}
- char* FindCGroupPathForMemorySubsystem()
+ static char* FindCGroupPathForSubsystem(bool (*is_subsystem)(const char *))
{
char *line = nullptr;
size_t lineLen = 0;
@@ -198,7 +264,7 @@ private:
char* strTok = strtok_s(subsystem_list, ",", &context);
while (strTok != nullptr)
{
- if (strncmp("memory", strTok, 6) == 0)
+ if (is_subsystem(strTok))
{
result = true;
break;
@@ -221,15 +287,43 @@ private:
bool ReadMemoryValueFromFile(const char* filename, size_t* val)
{
+ return ::ReadMemoryValueFromFile(filename, val);
+ }
+
+ long long ReadCpuCGroupValue(const char* subsystemFilename){
+ char *filename = nullptr;
+ bool result = false;
+ long long val;
+ size_t len;
+
+ if (m_cpu_cgroup_path == nullptr)
+ return -1;
+
+ len = strlen(m_cpu_cgroup_path);
+ len += strlen(subsystemFilename);
+ filename = (char*)PAL_malloc(len + 1);
+ if (filename == nullptr)
+ return -1;
+
+ strcpy_s(filename, len+1, m_cpu_cgroup_path);
+ strcat_s(filename, len+1, subsystemFilename);
+ result = ReadLongLongValueFromFile(filename, &val);
+ PAL_free(filename);
+ if (!result)
+ return -1;
+
+ return val;
+ }
+
+ bool ReadLongLongValueFromFile(const char* filename, long long* val)
+ {
bool result = false;
char *line = nullptr;
size_t lineLen = 0;
- char* endptr = nullptr;
- size_t num = 0, l, multiplier;
-
+
if (val == nullptr)
- return false;
-
+ return false;;
+
FILE* file = fopen(filename, "r");
if (file == nullptr)
goto done;
@@ -238,25 +332,11 @@ private:
goto done;
errno = 0;
- num = strtoull(line, &endptr, 0);
+ *val = atoll(line);
if (errno != 0)
- goto done;
-
- multiplier = 1;
- switch(*endptr)
- {
- case 'g':
- case 'G': multiplier = 1024;
- case 'm':
- case 'M': multiplier = multiplier*1024;
- case 'k':
- case 'K': multiplier = multiplier*1024;
- }
+ goto done;
- *val = num * multiplier;
result = true;
- if (*val/multiplier != num)
- result = false;
done:
if (file)
fclose(file);
@@ -333,3 +413,15 @@ PAL_GetWorkingSetSize(size_t* val)
free(line);
return result;
}
+
+BOOL
+PALAPI
+PAL_GetCpuLimit(UINT* val)
+{
+ CGroup cgroup;
+
+ if (val == nullptr)
+ return FALSE;
+
+ return cgroup.GetCpuLimit(val);
+}
diff --git a/src/pal/src/misc/eventlog.cpp b/src/pal/src/misc/eventlog.cpp
deleted file mode 100644
index 9eb67ffb1c..0000000000
--- a/src/pal/src/misc/eventlog.cpp
+++ /dev/null
@@ -1,423 +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.
-
-/*++
-
-
-
-Module Name:
-
- eventlog.cpp
-
-Abstract:
-
- (Rudimentary) implementation of Event Log.
-
- Defaults to BSD syslog. On Mac OS X, uses the superior asl.
-
- Caviats:
- * Neither are real handles that you can lock. If that is necessary, the
- PAL handle functionality can be used.
- * Neither are kept in a table so that if you ask for the same event source
- twice, you get the same handle.
- * The resource file is not consulted, so we just print out the replacement
- strings. Fortunately, for the CLR, there's the resources just have the single
- replacement string.
-
-Revision History:
-
- 5/21/09 -- initial
-
-
-
---*/
-
-#include "pal/malloc.hpp"
-#include "pal/dbgmsg.h"
-
-#ifdef __APPLE__
-#define USE_ASL
-#endif // __APPLE__
-
-#ifdef USE_ASL
-#include <asl.h>
-#else // USE_ASL
-#include <syslog.h>
-#endif // USE_ASL else
-
-using namespace CorUnix;
-
-SET_DEFAULT_DEBUG_CHANNEL(MISC);
-
-HANDLE
-PALAPI
-RegisterEventSourceA (
- IN OPTIONAL LPCSTR lpUNCServerName,
- IN LPCSTR lpSourceName
- )
-{
- HANDLE hRet = INVALID_HANDLE_VALUE;
-
- PERF_ENTRY(RegisterEventSourceA);
- ENTRY("RegisterEventSourceA(lpUNCServerName=%p (%s), lpSourceName=%p (%s))\n",
- lpUNCServerName, lpUNCServerName?lpUNCServerName:"NULL",
- lpSourceName, lpSourceName?lpSourceName:"NULL");
-
- if (NULL != lpUNCServerName)
- {
- SetLastError(ERROR_NOT_SUPPORTED);
- return hRet;
- }
-
- if (NULL == lpSourceName)
- {
- ERROR("lpSourceName has to be a valid parameter\n");
- SetLastError(ERROR_INVALID_PARAMETER);
- return hRet;
- }
-
-#ifdef USE_ASL
- // In asl parlance, the EventSource handle is an aslclient; it's not
- // guaranteed to be the same as a different call to this with the same
- // source name.
- aslclient asl = asl_open(lpSourceName, NULL /* facility */, 0 /* opts */);
- hRet = (HANDLE)asl;
-#else // USE_ASL
- // In syslog parlance, the EventSource handle is just a string name
- // representing the source.
- size_t sizeSyslogHandle = strlen(lpSourceName) + 1;
- char *syslogHandle = (char *)PAL_malloc(sizeSyslogHandle);
- if (syslogHandle)
- {
- strcpy_s(syslogHandle, sizeSyslogHandle, lpSourceName);
- hRet = (HANDLE)syslogHandle;
- }
-#endif // USE_ASL else
-
- if (INVALID_HANDLE_VALUE == hRet)
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-
- LOGEXIT("RegisterEventSourceA returns %p\n", hRet);
- PERF_EXIT(RegisterEventSourceA);
- return hRet;
-}
-
-HANDLE
-PALAPI
-RegisterEventSourceW (
- IN OPTIONAL LPCWSTR lpUNCServerName,
- IN LPCWSTR lpSourceName
- )
-{
- HANDLE hRet = INVALID_HANDLE_VALUE;
- int size;
- CHAR *inBuff = NULL;
-
- PERF_ENTRY(RegisterEventSourceW);
- ENTRY("RegisterEventSourceW(lpUNCServerName=%p (%S), lpSourceName=%p (%S))\n",
- lpUNCServerName, lpUNCServerName?lpUNCServerName:W16_NULLSTRING,
- lpSourceName, lpSourceName?lpSourceName:W16_NULLSTRING);
-
- if (NULL != lpUNCServerName)
- {
- SetLastError(ERROR_NOT_SUPPORTED);
- return hRet;
- }
-
- size = WideCharToMultiByte(CP_ACP, 0, lpSourceName, -1, NULL, 0, NULL, NULL);
-
- if (0 == size)
- {
- ERROR("lpSourceName has to be a valid parameter\n");
- SetLastError(ERROR_INVALID_PARAMETER);
- return hRet;
- }
- inBuff = (CHAR *)PAL_malloc(size);
- if (NULL == inBuff)
- {
- ERROR("malloc failed\n");
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- return hRet;
- }
-
- if (0 == WideCharToMultiByte(CP_ACP, 0, lpSourceName, -1, inBuff, size, NULL, NULL))
- {
- ASSERT( "WideCharToMultiByte failed!\n" );
- SetLastError(ERROR_INTERNAL_ERROR);
- goto done;
- }
-
- hRet = RegisterEventSourceA(NULL, inBuff);
-
-done:
- PAL_free(inBuff);
-
- LOGEXIT("RegisterEventSourceW returns %p\n", hRet);
- PERF_EXIT(RegisterEventSourceW);
- return hRet;
-}
-
-BOOL
-PALAPI
-DeregisterEventSource (
- IN HANDLE hEventLog
- )
-{
- BOOL bRet = FALSE;
-
- PERF_ENTRY(DeregisterEventSource)
- ENTRY("DeregisterEventSource(hEventLog=%p)\n", hEventLog);
-
- if (INVALID_HANDLE_VALUE == hEventLog ||
- NULL == hEventLog)
- {
- SetLastError(ERROR_INVALID_HANDLE);
- goto done;
- }
-
-#ifdef USE_ASL
- asl_close((aslclient)hEventLog);
-#else // USE_ASL
- PAL_free(hEventLog);
-#endif // USE_ASL else
-
- bRet = TRUE;
-
-done:
-
- LOGEXIT("DeregisterEventSource returns BOOL %d\n", bRet);
- PERF_EXIT(DeregisterEventSource);
- return bRet;
-}
-
-BOOL
-PALAPI
-ReportEventA (
- IN HANDLE hEventLog,
- IN WORD wType,
- IN WORD wCategory,
- IN DWORD dwEventID,
- IN OPTIONAL PSID lpUserSid,
- IN WORD wNumStrings,
- IN DWORD dwDataSize,
- IN OPTIONAL LPCSTR *lpStrings,
- IN OPTIONAL LPVOID lpRawData
- )
-{
- BOOL bRet = FALSE;
-
- PERF_ENTRY(ReportEventA);
- ENTRY("ReportEventA(hEventLog=%p, wType=0x%hx, wCategory=0x%hx, dwEventID=0x%x, "
- "lpUserSid=%p, wNumStrings=%hu, dwDataSize=%u, lpStrings=%p, lpRawData=%p)\n",
- hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize,
- lpStrings, lpRawData);
-
- if (INVALID_HANDLE_VALUE == hEventLog)
- {
- ERROR("hEventLog has to be a valid parameter\n");
- SetLastError(ERROR_INVALID_PARAMETER);
- return bRet;
- }
-
- if (wNumStrings > 0 && NULL == lpStrings)
- {
- ERROR("lpStrings has to be a valid parameter if wNumStrings is non-zero\n");
- SetLastError(ERROR_INVALID_PARAMETER);
- return bRet;
- }
-
- if (NULL != lpUserSid || 0 != dwDataSize || 1 != wNumStrings)
- {
- SetLastError(ERROR_NOT_SUPPORTED);
- return bRet;
- }
-
-#ifdef USE_ASL
- int level;
- switch (wType)
- {
- case EVENTLOG_SUCCESS:
- case EVENTLOG_AUDIT_SUCCESS:
- level = ASL_LEVEL_NOTICE;
- break;
-
- case EVENTLOG_INFORMATION_TYPE:
- level = ASL_LEVEL_INFO;
- break;
-
- case EVENTLOG_ERROR_TYPE:
- case EVENTLOG_AUDIT_FAILURE:
- level = ASL_LEVEL_ERR;
- break;
-
- case EVENTLOG_WARNING_TYPE:
- level = ASL_LEVEL_WARNING;
- break;
-
- default:
- ERROR("Unknown RecordEvent type.\n");
- SetLastError(ERROR_INVALID_PARAMETER);
- return bRet;
- }
-
- aslmsg msg;
- msg = asl_new(ASL_TYPE_MSG);
- int aslRet;
-
- if (msg)
- {
- char szNumber[11];
-
- sprintf_s(szNumber, sizeof(szNumber) / sizeof(*szNumber), "%hu", wCategory);
- aslRet = asl_set(msg, "Category", szNumber);
- if (aslRet != 0)
- WARN("Could not set Category %s on aslmsg (%p)", szNumber, msg);
- sprintf_s(szNumber, sizeof(szNumber) / sizeof(*szNumber), "%u", dwEventID);
- aslRet = asl_set(msg, "EventID", szNumber);
- if (aslRet != 0)
- WARN("Could not set EventID %s on aslmsg (%p)", szNumber, msg);
-
- aslRet = asl_log((aslclient)hEventLog, msg, level, "%s", lpStrings[0]);
-
- asl_free(msg);
- }
- else
- {
- // Yikes, fall back to worse syslog behavior due to low mem or asl issue.
- aslRet = asl_log((aslclient)hEventLog, NULL, level, "[%hx:%x] %s", wCategory, dwEventID, lpStrings[0]);
- }
-
- if (aslRet != 0)
- SetLastError(ERROR_INTERNAL_ERROR);
- else
- bRet = TRUE;
-#else // USE_ASL
- int priority;
- switch (wType)
- {
- case EVENTLOG_SUCCESS:
- case EVENTLOG_AUDIT_SUCCESS:
- case EVENTLOG_INFORMATION_TYPE:
- priority = LOG_INFO;
- break;
-
- case EVENTLOG_ERROR_TYPE:
- case EVENTLOG_AUDIT_FAILURE:
- priority = LOG_ERR;
- break;
-
- case EVENTLOG_WARNING_TYPE:
- priority = LOG_WARNING;
- break;
-
- default:
- ERROR("Unknown RecordEvent type.\n");
- SetLastError(ERROR_INVALID_PARAMETER);
- return bRet;
- }
-
- openlog((char *)hEventLog, LOG_CONS | LOG_PID, LOG_USER);
-
- syslog(priority, "[%hx:%x] %s", wCategory, dwEventID, lpStrings[0]);
-
- closelog();
-
- bRet = TRUE;
-#endif // USE_ASL else
-
- LOGEXIT("ReportEventA returns BOOL %d\n", bRet);
- PERF_EXIT(ReportEventA);
- return bRet;
-}
-
-BOOL
-PALAPI
-ReportEventW (
- IN HANDLE hEventLog,
- IN WORD wType,
- IN WORD wCategory,
- IN DWORD dwEventID,
- IN OPTIONAL PSID lpUserSid,
- IN WORD wNumStrings,
- IN DWORD dwDataSize,
- IN OPTIONAL LPCWSTR *lpStrings,
- IN OPTIONAL LPVOID lpRawData
- )
-{
- BOOL bRet = FALSE;
- LPCSTR *lpMBStrings = NULL;
-
- PERF_ENTRY(ReportEventW);
- ENTRY("ReportEventW(hEventLog=%p, wType=0x%hx, wCategory=0x%hx, dwEventID=0x%x, "
- "lpUserSid=%p, wNumStrings=%hu, dwDataSize=%u, lpStrings=%p, lpRawData=%p)\n",
- hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize,
- lpStrings, lpRawData);
-
- if (wNumStrings > 0 && NULL == lpStrings)
- {
- ERROR("lpStrings has to be a valid parameter if wNumStrings is non-zero\n");
- SetLastError(ERROR_INVALID_PARAMETER);
- return bRet;
- }
-
- if (wNumStrings > 0)
- {
- lpMBStrings = (LPCSTR *)PAL_malloc(wNumStrings * sizeof(CHAR *));
- if (!lpMBStrings)
- {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- return bRet;
- }
-
- for (WORD iString = 0; iString < wNumStrings; iString++)
- {
- int size;
- bool fConverted;
- CHAR *sz;
-
- size = WideCharToMultiByte(CP_ACP, 0, lpStrings[iString], -1, NULL, 0, NULL, NULL);
- if (0 == size)
- {
- ERROR("lpStrings[%d] has to be a valid parameter\n", iString);
- SetLastError(ERROR_INVALID_PARAMETER);
- wNumStrings = iString; // so that free only frees earlier converted lpStrings.
- goto done;
- }
-
- sz = (LPSTR)PAL_malloc(size);
- if (!sz)
- {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- wNumStrings = iString; // so that free only frees earlier converted lpStrings.
- goto done;
- }
- fConverted = (0 != WideCharToMultiByte(CP_ACP, 0, lpStrings[iString], -1,
- sz, size, NULL, NULL));
- lpMBStrings[iString] = sz; // no const-cast needed.
- if (!fConverted)
- {
- ASSERT("WideCharToMultiByte failed!\n");
- SetLastError(ERROR_INTERNAL_ERROR);
- wNumStrings = iString + 1; // so that free only frees earlier converted lpStrings.
- goto done;
- }
- }
- }
-
- bRet = ReportEventA(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings,
- dwDataSize, lpMBStrings, lpRawData);
-
-done:
-
- if (wNumStrings > 0)
- {
- for (WORD iString = 0; iString < wNumStrings; iString++)
- PAL_free((PVOID)lpMBStrings[iString]);
- PAL_free(lpMBStrings);
- }
-
- LOGEXIT("ReportEventW returns BOOL %d\n", bRet);
- PERF_EXIT(ReportEventW);
- return bRet;
-}
diff --git a/src/pal/src/misc/identity.cpp b/src/pal/src/misc/identity.cpp
deleted file mode 100644
index e983b1265c..0000000000
--- a/src/pal/src/misc/identity.cpp
+++ /dev/null
@@ -1,410 +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.
-
-/*++
-
-
-
-Module Name:
-
-misc/identity.cpp
-
-Abstract:
-
-Implementation of GetComputerNameW and GetUserNameW functions.
-
-
-
---*/
-
-#include "pal/palinternal.h"
-#include "pal/dbgmsg.h"
-#include "pal/misc.h"
-#include "pal/thread.hpp"
-#include "pal/identity.hpp"
-#include "pal/malloc.hpp"
-
-#include <pwd.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-
-#if HAVE_SYSCONF && defined(_SC_GETPW_R_SIZE_MAX)
-#include <limits.h> // for INT_MAX
-#endif
-
-using namespace CorUnix;
-
-SET_DEFAULT_DEBUG_CHANNEL(MISC);
-
-#if HAVE_GETPWUID_R
-
-#define DEFAULT_PASSWORD_BUFFER_SIZE 1024
-static DWORD dwInitialPasswdBufferSize = DEFAULT_PASSWORD_BUFFER_SIZE;
-
-#else // HAVE_GETPWUID_R
-
-static CRITICAL_SECTION identity_critsec;
-
-#endif // HAVE_GETPWUID_R
-
-
-/*++
-Function:
- IdentityInitialize
-
-Intitialization function called from PAL_Initialize.
-Initializes the critical section for the case when thread-safe
-getpwuid_r is not available.
-
---*/
-BOOL
-IdentityInitialize(void)
-{
-#if HAVE_GETPWUID_R
-
-#if HAVE_SYSCONF && defined(_SC_GETPW_R_SIZE_MAX)
- long lBufferSize = 0;
- lBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX);
-
- if ((long)INT_MAX < lBufferSize)
- {
- ERROR("sysconf(_SC_GETPW_R_SIZE_MAX) returns %ld which is > INT_MAX (%u)\n",
- lBufferSize, INT_MAX);
- return FALSE;
- }
-
- if (0 >= (int)(lBufferSize))
- {
- WARN("sysconf(_SC_GETPW_R_SIZE_MAX) returns %ld, using %u as the buffer size instead\n",
- lBufferSize, dwInitialPasswdBufferSize);
- }
- else
- {
- TRACE("sysconf(_SC_GETPW_R_SIZE_MAX) returns %ld\n", lBufferSize);
- dwInitialPasswdBufferSize = (DWORD)lBufferSize;
- }
-
-#endif // HAVE_SYSCONF && _SC_GETPW_R_SIZE_MAX
-
-#else // HAVE_GETPWUID_R
-
- InternalInitializeCriticalSection(&identity_critsec);
-
-#endif // HAVE_GETPWUID_R
-
- return TRUE;
-}
-
-
-/*++
-Function:
- IdentityCleanup
-
-Termination function called from PAL_Terminate.
-Deletes the critical section for the case when thread-safe
-getpwuid_r is not available.
-
---*/
-void
-IdentityCleanup(void)
-{
-#if !HAVE_GETPWUID_R
- InternalDeleteCriticalSection(&identity_critsec);
-#endif
-}
-
-/*++
-Function:
- GetUserNameW
-
-Uses getpwuid_r to get the user name and if it's not available uses
-getpwuid (with the safety of a critical section). See MSDN for functional spec.
-
---*/
-PALIMPORT
-BOOL
-PALAPI
-GetUserNameW(
- OUT LPWSTR lpBuffer, // address of name buffer
- IN OUT LPDWORD nSize ) // address of size of name buffer
-{
- BOOL fRet = FALSE;
- struct passwd *pPasswd = NULL;
- char *szUserName = NULL;
- DWORD cwchLen = 0;
- int iEuid = -1;
- int iRet = -1;
- CPalThread *pPalThread = InternalGetCurrentThread();
-
-#if HAVE_GETPWUID_R
-
- char *pchBuffer = NULL;
- DWORD dwBufLen = 0;
- struct passwd sPasswd;
-
-#endif // HAVE_GETPWUID_R
-
- PERF_ENTRY(GetUserNameW);
- ENTRY("GetUserNameW(lpBuffer = %p, nSize = %p (%d)\n",
- lpBuffer, nSize, nSize?*nSize:0);
-
- iEuid = geteuid();
-
- if (NULL == lpBuffer || NULL == nSize)
- {
- ERROR("lpBuffer == NULL or nSize == NULL");
- pPalThread->SetLastError(ERROR_INVALID_PARAMETER);
- goto done;
- }
-
-#if HAVE_GETPWUID_R
-
- dwBufLen = dwInitialPasswdBufferSize;
-
- while (NULL == pPasswd)
- {
- pchBuffer = (char*) PAL_malloc(sizeof(pchBuffer[0]) * dwBufLen);
- if (NULL == pchBuffer)
- {
- pPalThread->SetLastError(ERROR_OUTOFMEMORY);
- goto done;
- }
-
- iRet = InternalGetpwuid_r(pPalThread, iEuid, &sPasswd, pchBuffer, dwBufLen, &pPasswd);
- if (0 != iRet)
- {
- WARN("getpwuid_r(%d) returns %d for a buffer size of %d, error string is %s\n",
- iEuid, iRet, dwBufLen, strerror(iRet));
-
- if (ERANGE == iRet) // need a bigger buffer
- {
- PAL_free(pchBuffer);
- pchBuffer = NULL;
- pPasswd = NULL;
- dwBufLen *= 2; // double the buffer
- continue; // try again
- }
-
- pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
- goto done;
- }
-
- // Unfortunately, HPUX returns success and result = NULL even when the buffer size is small
- // (instead of returning ERANGE error). But, since we are using either
- // sysconf(_SC_GETPW_R_SIZE_MAX) or the HP recommended value of 1024, buffer should always
- // be big enough for getpwuid_r.
-
- if (NULL == pPasswd || NULL == pPasswd->pw_name)
- {
- // No matching entry found! something failed somewhere.
- ERROR("getpwuid_r(%d) returned %p with name NULL!\n", iEuid, pPasswd);
- pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
- goto done;
- }
- }
-
- szUserName = pPasswd->pw_name;
-
-#else // HAVE_GETPWUID_R
-
- InternalEnterCriticalSection(pPalThread, &identity_critsec);
- pPasswd = getpwuid(iEuid);
-
- if ((NULL == pPasswd) || (NULL == pPasswd->pw_name))
- {
- InternalLeaveCriticalSection(pPalThread, &identity_critsec);
- ERROR("getpwuid(%d) returned %p with name NULL! error (%d) is %s\n",
- iEuid, pPasswd, errno, strerror(errno));
- pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
- goto done;
- }
-
- // make a copy so that we can modify it
- szUserName = strdup(pPasswd->pw_name);
- if (NULL == szUserName)
- {
- InternalLeaveCriticalSection(pPalThread, &identity_critsec);
- pPalThread->SetLastError(ERROR_OUTOFMEMORY);
- goto done;
- }
-
- InternalLeaveCriticalSection(pPalThread, &identity_critsec);
-#endif // HAVE_GETPWUID_R
-
- // truncate the user name if it exceeds the maximum allowed limit
- if (strlen(szUserName) > UNLEN)
- {
- szUserName[UNLEN] = '\0';
- }
-
- // Copy from pPasswd->pw_name
- cwchLen = MultiByteToWideChar(CP_ACP, 0, szUserName, -1, lpBuffer, *nSize);
- if (0 == cwchLen)
- {
- ERROR ("MultiByteToWideChar failed with error %d when trying to convert the username "
- "%s to wide char\n", pPalThread->GetLastError(), szUserName);
- if (ERROR_INSUFFICIENT_BUFFER == pPalThread->GetLastError())
- {
- // Find the required size (including NULL)
- cwchLen = MultiByteToWideChar(CP_ACP, 0, szUserName, -1, NULL, 0);
- if (0 == cwchLen)
- {
- ERROR ("MultiByteToWideChar failed with error %d when trying to find the size of "
- "%s in wide chars\n", pPalThread->GetLastError(), szUserName);
- pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
- }
- else
- {
- // Update the required size
- *nSize = cwchLen;
- pPalThread->SetLastError(ERROR_MORE_DATA);
- }
- }
- goto done;
- }
-
- *nSize = cwchLen;
- fRet = TRUE;
-
-done:
-#if HAVE_GETPWUID_R
- if (NULL != pchBuffer)
- {
- PAL_free(pchBuffer);
- }
-#else // HAVE_GETPWUID_R
- if (NULL != szUserName)
- {
- PAL_free(szUserName);
- }
-#endif // HAVE_GETPWUID_R
-
- LOGEXIT("GetUserNameW returning BOOL %d\n", fRet);
- PERF_EXIT(GetUserNameW);
- return fRet;
-}
-
-#ifndef MAXHOSTNAMELEN
-// AIX doesn't have MAXHOSTNAMELEN, it recommends using 256
-#define MAXHOSTNAMELEN 256
-#endif // MAXHOSTNAMELEN
-
-/*++
-Function:
- GetComputerNameW
-
-Uses gethostname to get the computer name. See MSDN for functional spec.
-
---*/
-PALIMPORT
-BOOL
-PALAPI
-GetComputerNameW(
- OUT LPWSTR lpBuffer, // address of name buffer
- IN OUT LPDWORD nSize) // address of size of name buffer
-{
- BOOL fRet = FALSE;
- char szHostName[MAXHOSTNAMELEN+1];
- char *pchDot = NULL;
- DWORD cwchLen = 0;
- CPalThread *pPalThread = InternalGetCurrentThread();
-
- PERF_ENTRY(GetComputerNameW);
- ENTRY("GetComputerNameW(lpBuffer = %p, nSize = %p (%d)\n",
- lpBuffer, nSize, nSize?*nSize:0);
-
- if (NULL == lpBuffer || NULL == nSize)
- {
- ERROR("lpBuffer == NULL or nSize == NULL");
- pPalThread->SetLastError(ERROR_INVALID_PARAMETER);
- goto done;
- }
-
- if (0 != gethostname(szHostName, sizeof(szHostName)/sizeof(szHostName[0])))
- {
- ERROR("gethostname failed with error (%d) %s\n", errno, strerror(errno));
- pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
- goto done;
- }
-
- // Null terminate the string
- szHostName[sizeof(szHostName)/sizeof(szHostName[0])-1] = '\0';
-
- // some OSes return the hostname with the domain name included.
- // We want to return only the host part of the name (see the spec for
- // more details
- pchDot = strchr(szHostName, '.');
- if (NULL != pchDot)
- {
- *pchDot = '\0'; // remove the domain name info
- }
-
- // copy the hostname (including NULL character)
- cwchLen = MultiByteToWideChar(CP_ACP, 0, szHostName, -1, lpBuffer, *nSize);
- if (0 == cwchLen)
- {
- ERROR ("MultiByteToWideChar failed with error %d when trying to convert the hostname "
- "%s to wide char\n", pPalThread->GetLastError(), szHostName);
- if (ERROR_INSUFFICIENT_BUFFER == pPalThread->GetLastError())
- {
- // Find the required size (including NULL)
- cwchLen = MultiByteToWideChar(CP_ACP, 0, szHostName, -1, NULL, 0);
- if (0 == cwchLen)
- {
- ERROR ("MultiByteToWideChar failed with error %d when trying to find the size of "
- "%s in wide chars\n", pPalThread->GetLastError(), szHostName);
- pPalThread->SetLastError(ERROR_INTERNAL_ERROR);
- }
- else
- {
- // Update the required size
- *nSize = cwchLen - 1; // don't include the NULL
- pPalThread->SetLastError(ERROR_BUFFER_OVERFLOW);
- }
- }
- goto done;
- }
-
- *nSize = cwchLen - 1; // don't include the NULL
- fRet = TRUE;
-
-done:
- LOGEXIT("GetComputerNameW returning BOOL %d\n", fRet);
- PERF_EXIT(GetComputerNameW);
- return fRet;
-}
-
-#if HAVE_GETPWUID_R
-/*++
-Function:
- InternalGetpwuid_r
-
-Suspension safe wrapper for getpwuid_r
---*/
-int
-CorUnix::InternalGetpwuid_r(
- CPalThread *pPalThread,
- uid_t uid,
- struct passwd *pPasswd,
- char *pchBuffer,
- size_t nBufSize,
- struct passwd **ppResult
- )
-{
- int iError = 0;
-
- iError = getpwuid_r(uid, pPasswd, pchBuffer, nBufSize, ppResult);
-
-#if GETPWUID_R_SETS_ERRNO
- if (0 != iError)
- {
- iError = errno; // some systems (AIX) sets errno instead of returning error
- }
-#endif // GETPWUID_R_SETS_ERRNO
-
- return iError;
-}
-#endif // HAVE_GETPWUID_R
diff --git a/src/pal/src/misc/sysinfo.cpp b/src/pal/src/misc/sysinfo.cpp
index e1785688dc..a06f4b75cb 100644
--- a/src/pal/src/misc/sysinfo.cpp
+++ b/src/pal/src/misc/sysinfo.cpp
@@ -384,6 +384,52 @@ PAL_HasGetCurrentProcessorNumber()
return HAVE_SCHED_GETCPU;
}
+bool
+ReadMemoryValueFromFile(const char* filename, size_t* val)
+{
+ bool result = false;
+ char *line = nullptr;
+ size_t lineLen = 0;
+ char* endptr = nullptr;
+ size_t num = 0, l, multiplier;
+
+ if (val == nullptr)
+ return false;
+
+ FILE* file = fopen(filename, "r");
+ if (file == nullptr)
+ goto done;
+
+ if (getline(&line, &lineLen, file) == -1)
+ goto done;
+
+ errno = 0;
+ num = strtoull(line, &endptr, 0);
+ if (errno != 0)
+ goto done;
+
+ multiplier = 1;
+ switch(*endptr)
+ {
+ case 'g':
+ case 'G': multiplier = 1024;
+ case 'm':
+ case 'M': multiplier = multiplier*1024;
+ case 'k':
+ case 'K': multiplier = multiplier*1024;
+ }
+
+ *val = num * multiplier;
+ result = true;
+ if (*val/multiplier != num)
+ result = false;
+done:
+ if (file)
+ fclose(file);
+ free(line);
+ return result;
+}
+
size_t
PALAPI
PAL_GetLogicalProcessorCacheSizeFromOS()
@@ -403,5 +449,48 @@ PAL_GetLogicalProcessorCacheSizeFromOS()
cacheSize = max(cacheSize, sysconf(_SC_LEVEL4_CACHE_SIZE));
#endif
+#if defined(_ARM64_)
+ if(cacheSize == 0)
+ {
+ size_t size;
+
+ if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index0/size", &size))
+ cacheSize = max(cacheSize, size);
+ if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index1/size", &size))
+ cacheSize = max(cacheSize, size);
+ if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index2/size", &size))
+ cacheSize = max(cacheSize, size);
+ if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index3/size", &size))
+ cacheSize = max(cacheSize, size);
+ if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index4/size", &size))
+ cacheSize = max(cacheSize, size);
+ }
+
+ if(cacheSize == 0)
+ {
+ // It is currently expected to be missing cache size info
+ //
+ // _SC_LEVEL*_*CACHE_SIZE is not yet present. Work is in progress to enable this for arm64
+ //
+ // /sys/devices/system/cpu/cpu*/cache/index*/ is also not yet present in most systems.
+ // Arm64 patch is in Linux kernel tip.
+ //
+ // midr_el1 is available in "/sys/devices/system/cpu/cpu0/regs/identification/midr_el1",
+ // but without an exhaustive list of ARM64 processors any decode of midr_el1
+ // Would likely be incomplete
+
+ // Published information on ARM64 architectures is limited.
+ // If we use recent high core count chips as a guide for state of the art, we find
+ // total L3 cache to be 1-2MB/core. As always, there are exceptions.
+
+ // Estimate cache size based on CPU count
+ // Assume lower core count are lighter weight parts which are likely to have smaller caches
+ // Assume L3$/CPU grows linearly from 256K to 1.5M/CPU as logicalCPUs grows from 2 to 12 CPUs
+ DWORD logicalCPUs = PAL_GetLogicalCpuCountFromOS();
+
+ cacheSize = logicalCPUs*min(1536, max(256, logicalCPUs*128))*1024;
+ }
+#endif
+
return cacheSize;
}
diff --git a/src/pal/src/objmgr/palobjbase.cpp b/src/pal/src/objmgr/palobjbase.cpp
index 27842f6d97..0f226b9eb0 100644
--- a/src/pal/src/objmgr/palobjbase.cpp
+++ b/src/pal/src/objmgr/palobjbase.cpp
@@ -314,6 +314,16 @@ CPalObjectBase::ReleaseReference(
);
}
+ if (NULL != m_pot->GetImmutableDataCleanupRoutine())
+ {
+ (*m_pot->GetImmutableDataCleanupRoutine())(m_pvImmutableData);
+ }
+
+ if (NULL != m_pot->GetProcessLocalDataCleanupRoutine())
+ {
+ (*m_pot->GetProcessLocalDataCleanupRoutine())(pthr, static_cast<IPalObject*>(this));
+ }
+
InternalDelete(this);
pthr->ReleaseThreadReference();
diff --git a/src/pal/src/objmgr/shmobject.cpp b/src/pal/src/objmgr/shmobject.cpp
index 1435d5d734..17ef3e43df 100644
--- a/src/pal/src/objmgr/shmobject.cpp
+++ b/src/pal/src/objmgr/shmobject.cpp
@@ -180,7 +180,7 @@ CSharedMemoryObject::InitializeFromExistingSharedData(
m_ObjectDomain = SharedObject;
- _ASSERTE(SHMNULL != m_shmod);
+ _ASSERTE(NULL != m_shmod);
psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, m_shmod);
if (NULL == psmod)
@@ -236,12 +236,19 @@ CSharedMemoryObject::InitializeFromExistingSharedData(
goto InitializeFromExistingSharedDataExit;
}
- if (SHMNULL != psmod->shmObjImmutableData)
+ if (NULL != psmod->shmObjImmutableData)
{
VOID *pv = SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjImmutableData);
if (NULL != pv)
{
memcpy(m_pvImmutableData, pv, m_pot->GetImmutableDataSize());
+ if (NULL != psmod->pCopyRoutine)
+ {
+ (*psmod->pCopyRoutine)(pv, m_pvImmutableData);
+ }
+
+ m_pot->SetImmutableDataCopyRoutine(psmod->pCopyRoutine);
+ m_pot->SetImmutableDataCleanupRoutine(psmod->pCleanupRoutine);
}
else
{
@@ -251,7 +258,7 @@ CSharedMemoryObject::InitializeFromExistingSharedData(
}
}
- if (SHMNULL != psmod->shmObjSharedData)
+ if (NULL != psmod->shmObjSharedData)
{
m_pvSharedData = SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjSharedData);
if (NULL == m_pvSharedData)
@@ -301,7 +308,7 @@ CSharedMemoryObject::AllocateSharedDataItems(
)
{
PAL_ERROR palError = NO_ERROR;
- SHMPTR shmod = SHMNULL;
+ SHMPTR shmod = NULL;
SHMObjData *psmod = NULL;
_ASSERTE(NULL != pshmObjData);
@@ -321,8 +328,8 @@ CSharedMemoryObject::AllocateSharedDataItems(
SHMLock();
- shmod = SHMalloc(sizeof(SHMObjData));
- if (SHMNULL == shmod)
+ shmod = malloc(sizeof(SHMObjData));
+ if (NULL == shmod)
{
ERROR("Unable to allocate m_shmod for new object\n");
palError = ERROR_OUTOFMEMORY;
@@ -339,9 +346,19 @@ CSharedMemoryObject::AllocateSharedDataItems(
if (0 != m_oa.sObjectName.GetStringLength())
{
+ LPCWSTR str = m_oa.sObjectName.GetString();
+ _ASSERTE(str);
+
psmod->dwNameLength = m_oa.sObjectName.GetStringLength();
- psmod->shmObjName = SHMWStrDup(m_oa.sObjectName.GetString());
- if (SHMNULL == psmod->shmObjName)
+
+ UINT length = (PAL_wcslen(str) + 1) * sizeof(WCHAR);
+ psmod->shmObjName = malloc(length);
+
+ if (psmod->shmObjName != 0)
+ {
+ memcpy(psmod->shmObjName, str, length);
+ }
+ else
{
ERROR("Unable to allocate psmod->shmObjName for new object\n");
palError = ERROR_OUTOFMEMORY;
@@ -356,8 +373,8 @@ CSharedMemoryObject::AllocateSharedDataItems(
// by CSharedMemoryObjectManager::RegisterObject or PromoteSharedData
//
- psmod->shmObjImmutableData = SHMalloc(m_pot->GetImmutableDataSize());
- if (SHMNULL == psmod->shmObjImmutableData)
+ psmod->shmObjImmutableData = malloc(m_pot->GetImmutableDataSize());
+ if (NULL == psmod->shmObjImmutableData)
{
ERROR("Unable to allocate psmod->shmObjImmutableData for new object\n");
palError = ERROR_OUTOFMEMORY;
@@ -367,8 +384,8 @@ CSharedMemoryObject::AllocateSharedDataItems(
if (0 != m_pot->GetSharedDataSize())
{
- psmod->shmObjSharedData = SHMalloc(m_pot->GetSharedDataSize());
- if (SHMNULL == psmod->shmObjSharedData)
+ psmod->shmObjSharedData = malloc(m_pot->GetSharedDataSize());
+ if (NULL == psmod->shmObjSharedData)
{
ERROR("Unable to allocate psmod->shmObjSharedData for new object\n");
palError = ERROR_OUTOFMEMORY;
@@ -381,7 +398,7 @@ CSharedMemoryObject::AllocateSharedDataItems(
AllocateSharedDataItemsExit:
- if (NO_ERROR != palError && SHMNULL != shmod)
+ if (NO_ERROR != palError && NULL != shmod)
{
FreeSharedDataAreas(shmod);
}
@@ -412,7 +429,7 @@ CSharedMemoryObject::FreeSharedDataAreas(
{
SHMObjData *psmod;
- _ASSERTE(SHMNULL != shmObjData);
+ _ASSERTE(NULL != shmObjData);
ENTRY("CSharedMemoryObject::FreeSharedDataAreas"
"(shmObjData = %p)\n",
@@ -424,22 +441,26 @@ CSharedMemoryObject::FreeSharedDataAreas(
psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, shmObjData);
_ASSERTE(NULL != psmod);
- if (SHMNULL != psmod->shmObjImmutableData)
+ if (NULL != psmod->shmObjImmutableData)
{
- SHMfree(psmod->shmObjImmutableData);
+ if (NULL != psmod->pCleanupRoutine)
+ {
+ (*psmod->pCleanupRoutine)(psmod->shmObjImmutableData);
+ }
+ free(psmod->shmObjImmutableData);
}
- if (SHMNULL != psmod->shmObjSharedData)
+ if (NULL != psmod->shmObjSharedData)
{
- SHMfree(psmod->shmObjSharedData);
+ free(psmod->shmObjSharedData);
}
- if (SHMNULL != psmod->shmObjName)
+ if (NULL != psmod->shmObjName)
{
- SHMfree(psmod->shmObjName);
+ free(psmod->shmObjName);
}
- SHMfree(shmObjData);
+ free(shmObjData);
SHMRelease();
@@ -448,159 +469,6 @@ CSharedMemoryObject::FreeSharedDataAreas(
/*++
Function:
- CSharedMemoryObject::PromoteShjaredData
-
- Copies the object's state into the passed-in shared data structures
-
-Parameters:
- shmObjData -- shared memory pointer for the shared memory object data
- psmod -- locally-mapped pointer for the shared memory object data
---*/
-
-void
-CSharedMemoryObject::PromoteSharedData(
- SHMPTR shmObjData,
- SHMObjData *psmod
- )
-{
- _ASSERTE(SHMNULL != shmObjData);
- _ASSERTE(NULL != psmod);
-
- ENTRY("CSharedMemoryObject::PromoteSharedData"
- "(this = %p, shmObjData = %p, psmod = %p)\n",
- this,
- shmObjData,
- psmod);
-
- //
- // psmod has been zero-inited, so we don't need to worry about
- // shmPrevObj, shmNextObj, fAddedToList, shmObjName, dwNameLength,
- // or pvSynchData
- //
-
- psmod->lProcessRefCount = 1;
- psmod->eTypeId = m_pot->GetId();
-
- if (0 != m_pot->GetImmutableDataSize())
- {
- void *pvImmutableData;
-
- pvImmutableData = SHMPTR_TO_TYPED_PTR(void, psmod->shmObjImmutableData);
- _ASSERTE(NULL != pvImmutableData);
-
- CopyMemory(
- pvImmutableData,
- m_pvImmutableData,
- m_pot->GetImmutableDataSize()
- );
- }
-
- if (0 != m_pot->GetSharedDataSize())
- {
- void *pvSharedData;
-
- pvSharedData = SHMPTR_TO_TYPED_PTR(void, psmod->shmObjSharedData);
- _ASSERTE(NULL != pvSharedData);
-
- CopyMemory(
- pvSharedData,
- m_pvSharedData,
- m_pot->GetSharedDataSize()
- );
-
- free(m_pvSharedData);
- m_pvSharedData = pvSharedData;
- }
-
- m_shmod = shmObjData;
-
- LOGEXIT("CSharedMemoryObject::PromoteSharedData\n");
-}
-
-/*++
-Function:
- CSharedMemoryObject::EnsureObjectIsShared
-
- If this object is not yet in the shared domain allocate the necessary
- shared memory structures for it and copy the object's data into those
- structures
-
-Parameters:
- pthr -- thread data for the calling thread
---*/
-
-PAL_ERROR
-CSharedMemoryObject::EnsureObjectIsShared(
- CPalThread *pthr
- )
-{
- PAL_ERROR palError = NO_ERROR;
- IDataLock *pDataLock = NULL;
- SHMPTR shmObjData;
- SHMObjData *psmod;
-
- _ASSERTE(NULL != pthr);
-
- ENTRY("CSharedMemoryObject::EnsureObjectIsShared"
- "(this = %p, pthr = %p)\n",
- this,
- pthr
- );
-
- //
- // Grab the shared memory lock and check if the object is already
- // shared
- //
-
- SHMLock();
-
- if (SharedObject == m_ObjectDomain)
- {
- goto EnsureObjectIsSharedExit;
- }
-
- //
- // Grab the local shared data lock, if necessary
- //
-
- if (0 != m_pot->GetSharedDataSize())
- {
- m_sdlSharedData.AcquireLock(pthr, &pDataLock);
- }
-
- //
- // Allocate the necessary shared data areas
- //
-
- palError = AllocateSharedDataItems(&shmObjData, &psmod);
- if (NO_ERROR != palError)
- {
- goto EnsureObjectIsSharedExit;
- }
-
- //
- // Promote the object's data and set the domain to shared
- //
-
- PromoteSharedData(shmObjData, psmod);
- m_ObjectDomain = SharedObject;
-
-EnsureObjectIsSharedExit:
-
- if (NULL != pDataLock)
- {
- pDataLock->ReleaseLock(pthr, TRUE);
- }
-
- SHMRelease();
-
- LOGEXIT("CSharedMemoryObject::EnsureObjectIsShared returns %d\n", palError);
-
- return palError;
-}
-
-/*++
-Function:
CSharedMemoryObject::CleanupForProcessShutdown
Cleanup routine called by the object manager when shutting down
@@ -636,6 +504,16 @@ CSharedMemoryObject::CleanupForProcessShutdown(
);
}
+ if (NULL != m_pot->GetImmutableDataCleanupRoutine())
+ {
+ (*m_pot->GetImmutableDataCleanupRoutine())(m_pvImmutableData);
+ }
+
+ if (NULL != m_pot->GetProcessLocalDataCleanupRoutine())
+ {
+ (*m_pot->GetProcessLocalDataCleanupRoutine())(pthr, static_cast<IPalObject*>(this));
+ }
+
//
// We need to do two things with the calling thread data here:
// 1) store it in m_pthrCleanup so it is available to the destructors
@@ -760,7 +638,7 @@ CSharedMemoryObject::DereferenceSharedData()
if (!fSharedDataAlreadDereferenced)
{
- if (SHMNULL != m_shmod)
+ if (NULL != m_shmod)
{
SHMObjData *psmod;
@@ -789,7 +667,7 @@ CSharedMemoryObject::DereferenceSharedData()
_ASSERTE(0 != psmod->dwNameLength);
- if (SHMNULL != psmod->shmPrevObj)
+ if (NULL != psmod->shmPrevObj)
{
SHMObjData *psmodPrevious = SHMPTR_TO_TYPED_PTR(SHMObjData, psmod->shmPrevObj);
_ASSERTE(NULL != psmodPrevious);
@@ -809,7 +687,7 @@ CSharedMemoryObject::DereferenceSharedData()
}
}
- if (SHMNULL != psmod->shmNextObj)
+ if (NULL != psmod->shmNextObj)
{
SHMObjData *psmodNext = SHMPTR_TO_TYPED_PTR(SHMObjData, psmod->shmNextObj);
_ASSERTE(NULL != psmodNext);
@@ -820,8 +698,8 @@ CSharedMemoryObject::DereferenceSharedData()
#if _DEBUG
else
{
- _ASSERTE(SHMNULL == psmod->shmPrevObj);
- _ASSERTE(SHMNULL == psmod->shmNextObj);
+ _ASSERTE(NULL == psmod->shmPrevObj);
+ _ASSERTE(NULL == psmod->shmNextObj);
}
#endif
}
@@ -871,7 +749,7 @@ CSharedMemoryObject::~CSharedMemoryObject()
{
free(m_pvSharedData);
}
- else if (SHMNULL != m_shmod && m_fDeleteSharedData)
+ else if (NULL != m_shmod && m_fDeleteSharedData)
{
FreeSharedDataAreas(m_shmod);
}
@@ -1178,126 +1056,6 @@ InitializeExit:
/*++
Function:
- CSharedMemoryWaitableObject::EnsureObjectIsShared
-
- If this object is not yet in the shared domain allocate the necessary
- shared memory structures for it and copy the object's data into those
- structures
-
-Parameters:
- pthr -- thread data for the calling thread
---*/
-
-PAL_ERROR
-CSharedMemoryWaitableObject::EnsureObjectIsShared(
- CPalThread *pthr
- )
-{
- PAL_ERROR palError = NO_ERROR;
- IDataLock *pDataLock = NULL;
- SHMPTR shmObjData = SHMNULL;
- SHMObjData *psmod;
- VOID *pvSharedSynchData;
-
- _ASSERTE(NULL != pthr);
-
- ENTRY("CSharedMemoryWaitableObject::EnsureObjectIsShared"
- "(this = %p, pthr = %p)\n",
- this,
- pthr
- );
-
- //
- // First, grab the process synchronization lock and check
- // if the object is already shared
- //
-
- g_pSynchronizationManager->AcquireProcessLock(pthr);
-
- if (SharedObject == m_ObjectDomain)
- {
- goto EnsureObjectIsSharedExitNoSHMLockRelease;
- }
-
- //
- // Grab the necessary locks
- //
-
- SHMLock();
-
- if (0 != m_pot->GetSharedDataSize())
- {
- m_sdlSharedData.AcquireLock(pthr, &pDataLock);
- }
-
- //
- // Allocate the necessary shared data areas
- //
-
- palError = AllocateSharedDataItems(&shmObjData, &psmod);
- if (NO_ERROR != palError)
- {
- goto EnsureObjectIsSharedExit;
- }
-
- //
- // Promote the object's synchronization data
- //
-
- palError = g_pSynchronizationManager->PromoteObjectSynchData(
- pthr,
- m_pvSynchData,
- &pvSharedSynchData
- );
-
- if (NO_ERROR != palError)
- {
- goto EnsureObjectIsSharedExit;
- }
-
- m_pvSynchData = pvSharedSynchData;
- psmod->pvSynchData = pvSharedSynchData;
-
- //
- // Promote the object's data and set the domain to shared
- //
-
- PromoteSharedData(shmObjData, psmod);
- m_ObjectDomain = SharedObject;
-
-EnsureObjectIsSharedExit:
-
- if (NULL != pDataLock)
- {
- pDataLock->ReleaseLock(pthr, TRUE);
- }
-
- SHMRelease();
-
-EnsureObjectIsSharedExitNoSHMLockRelease:
-
- g_pSynchronizationManager->ReleaseProcessLock(pthr);
-
- if (NO_ERROR != palError && SHMNULL != shmObjData)
- {
- //
- // Since shmObjdData is local to this function there's no
- // need to continue to hold the promotion locks when
- // freeing the allocated data on error
- //
-
- FreeSharedDataAreas(shmObjData);
- }
-
- LOGEXIT("CSharedMemoryWaitableObject::EnsureObjectIsShared returns %d\n",
- palError
- );
-
- return palError;
-}
-
-/*++
-Function:
CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject
Destructor; should only be called from ReleaseReference
diff --git a/src/pal/src/objmgr/shmobject.hpp b/src/pal/src/objmgr/shmobject.hpp
index addfda52cc..9d55f90cab 100644
--- a/src/pal/src/objmgr/shmobject.hpp
+++ b/src/pal/src/objmgr/shmobject.hpp
@@ -65,6 +65,9 @@ namespace CorUnix
SHMPTR shmObjImmutableData;
SHMPTR shmObjSharedData;
+ OBJECT_IMMUTABLE_DATA_COPY_ROUTINE pCopyRoutine;
+ OBJECT_IMMUTABLE_DATA_CLEANUP_ROUTINE pCleanupRoutine;
+
LONG lProcessRefCount;
DWORD dwNameLength;
@@ -121,8 +124,7 @@ namespace CorUnix
// m_fSharedDataDereferenced will be TRUE if DereferenceSharedData
// has already been called. (N.B. -- this is a LONG instead of a bool
// because it is passed to InterlockedExchange). If the shared data blob
- // should be freed in the object's destructor (i.e., SHMfree should be
- // called on the appropriate SHMPTRs) DereferenceSharedData will
+ // should be freed in the object's destructor DereferenceSharedData will
// set m_fDeleteSharedData to TRUE.
//
@@ -141,12 +143,6 @@ namespace CorUnix
SHMPTR shmObjData
);
- void
- PromoteSharedData(
- SHMPTR shmObjData,
- SHMObjData *psmod
- );
-
bool
DereferenceSharedData();
@@ -178,7 +174,7 @@ namespace CorUnix
:
CPalObjectBase(pot),
m_pcsObjListLock(pcsObjListLock),
- m_shmod(SHMNULL),
+ m_shmod(NULL),
m_pvSharedData(NULL),
m_ObjectDomain(ProcessLocalObject),
m_fSharedDataDereferenced(FALSE),
@@ -229,12 +225,6 @@ namespace CorUnix
CObjectAttributes *poa
);
- virtual
- PAL_ERROR
- EnsureObjectIsShared(
- CPalThread *pthr
- );
-
void
CleanupForProcessShutdown(
CPalThread *pthr
@@ -354,12 +344,6 @@ namespace CorUnix
CObjectAttributes *poa
);
- virtual
- PAL_ERROR
- EnsureObjectIsShared(
- CPalThread *pthr
- );
-
//
// IPalObject routines
//
diff --git a/src/pal/src/objmgr/shmobjectmanager.cpp b/src/pal/src/objmgr/shmobjectmanager.cpp
index 42754216f1..90caa655e3 100644
--- a/src/pal/src/objmgr/shmobjectmanager.cpp
+++ b/src/pal/src/objmgr/shmobjectmanager.cpp
@@ -277,7 +277,7 @@ CSharedMemoryObjectManager::RegisterObject(
if (0 != poa->sObjectName.GetStringLength())
{
- SHMPTR shmObjectListHead = SHMNULL;
+ SHMPTR shmObjectListHead = NULL;
//
// The object must be shared
@@ -352,7 +352,7 @@ CSharedMemoryObjectManager::RegisterObject(
}
shmObjectListHead = SHMGetInfo(SIID_NAMED_OBJECTS);
- if (SHMNULL != shmObjectListHead)
+ if (NULL != shmObjectListHead)
{
SHMObjData *psmodListHead;
@@ -418,6 +418,14 @@ CSharedMemoryObjectManager::RegisterObject(
pvImmutableData,
potObj->GetImmutableDataSize()
);
+
+ if (NULL != potObj->GetImmutableDataCopyRoutine())
+ {
+ (*potObj->GetImmutableDataCopyRoutine())(pvImmutableData, pvSharedImmutableData);
+ }
+
+ psmod->pCopyRoutine = potObj->GetImmutableDataCopyRoutine();
+ psmod->pCleanupRoutine = potObj->GetImmutableDataCleanupRoutine();
}
else
{
@@ -505,8 +513,8 @@ CSharedMemoryObjectManager::LocateObject(
{
PAL_ERROR palError = NO_ERROR;
IPalObject *pobjExisting = NULL;
- SHMPTR shmSharedObjectData = SHMNULL;
- SHMPTR shmObjectListEntry = SHMNULL;
+ SHMPTR shmSharedObjectData = NULL;
+ SHMPTR shmObjectListEntry = NULL;
SHMObjData *psmod = NULL;
LPWSTR pwsz = NULL;
@@ -598,7 +606,7 @@ CSharedMemoryObjectManager::LocateObject(
SHMLock();
shmObjectListEntry = SHMGetInfo(SIID_NAMED_OBJECTS);
- while (SHMNULL != shmObjectListEntry)
+ while (NULL != shmObjectListEntry)
{
psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, shmObjectListEntry);
if (NULL != psmod)
@@ -634,7 +642,7 @@ CSharedMemoryObjectManager::LocateObject(
}
}
- if (SHMNULL != shmSharedObjectData)
+ if (NULL != shmSharedObjectData)
{
CSharedMemoryObject *pshmobj = NULL;
CObjectAttributes oa(pwsz, NULL);
@@ -1094,7 +1102,7 @@ CSharedMemoryObjectManager::ImportSharedObjectIntoProcess(
_ASSERTE(NULL != pthr);
_ASSERTE(NULL != pot);
_ASSERTE(NULL != poa);
- _ASSERTE(SHMNULL != shmSharedObjectData);
+ _ASSERTE(NULL != shmSharedObjectData);
_ASSERTE(NULL != psmod);
_ASSERTE(NULL != ppshmobj);
@@ -1174,335 +1182,6 @@ static CAllowedObjectTypes aotRemotable(
/*++
Function:
- PAL_LocalHandleToRemote
-
- Returns a "remote handle" that may be passed to another process.
-
-Parameters:
- hLocal -- the handle to generate a "remote handle" for
---*/
-
-PALIMPORT
-RHANDLE
-PALAPI
-PAL_LocalHandleToRemote(IN HANDLE hLocal)
-{
- PAL_ERROR palError = NO_ERROR;
- CPalThread *pthr;
- IPalObject *pobj = NULL;
- CSharedMemoryObject *pshmobj;
- SHMObjData *psmod = NULL;
- RHANDLE hRemote = reinterpret_cast<RHANDLE>(INVALID_HANDLE_VALUE);
-
- PERF_ENTRY(PAL_LocalHandleToRemote);
- ENTRY("PAL_LocalHandleToRemote( hLocal=0x%lx )\n", hLocal);
-
- pthr = InternalGetCurrentThread();
-
- if (!HandleIsSpecial(hLocal))
- {
- palError = g_pObjectManager->ReferenceObjectByHandle(
- pthr,
- hLocal,
- &aotRemotable,
- 0,
- &pobj
- );
-
- if (NO_ERROR != palError)
- {
- goto PAL_LocalHandleToRemoteExitNoLockRelease;
- }
- }
- else if (hPseudoCurrentProcess == hLocal)
- {
- pobj = g_pobjProcess;
- pobj->AddReference();
- }
- else
- {
- ASSERT("Invalid special handle type passed to PAL_LocalHandleToRemote\n");
- palError = ERROR_INVALID_HANDLE;
- goto PAL_LocalHandleToRemoteExitNoLockRelease;
- }
-
- pshmobj = static_cast<CSharedMemoryObject*>(pobj);
-
- //
- // Make sure that the object is shared
- //
-
- palError = pshmobj->EnsureObjectIsShared(pthr);
- if (NO_ERROR != palError)
- {
- ERROR("Failure %d promoting object\n", palError);
- goto PAL_LocalHandleToRemoteExitNoLockRelease;
- }
-
- SHMLock();
-
- psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, pshmobj->GetShmObjData());
- if (NULL != psmod)
- {
- //
- // Bump up the process ref count by 1. The receiving process will not
- // increase the ref count when it converts the remote handle to
- // local.
- //
-
- psmod->lProcessRefCount += 1;
-
- //
- // The remote handle is simply the SHMPTR for the SHMObjData
- //
-
- hRemote = reinterpret_cast<RHANDLE>(pshmobj->GetShmObjData());
- }
- else
- {
- ASSERT("Unable to map shared object data\n");
- palError = ERROR_INTERNAL_ERROR;
- goto PAL_LocalHandleToRemoteExit;
- }
-
-PAL_LocalHandleToRemoteExit:
-
- SHMRelease();
-
-PAL_LocalHandleToRemoteExitNoLockRelease:
-
- if (NULL != pobj)
- {
- pobj->ReleaseReference(pthr);
- }
-
- if (NO_ERROR != palError)
- {
- pthr->SetLastError(palError);
- }
-
- LOGEXIT("PAL_LocalHandleToRemote returns RHANDLE 0x%lx\n", hRemote);
- PERF_EXIT(PAL_LocalHandleToRemote);
- return hRemote;
-}
-
-/*++
-Function:
- CSharedMemoryObjectManager::ConvertRemoteHandleToLocal
-
- Given a "remote handle" creates a local handle that refers
- to the desired object. (Unlike PAL_RemoteHandleToLocal this method
- needs to access internal object manager state, so it's a member function.)
-
-Parameters:
- pthr -- thread data for calling thread
- rhRemote -- the remote handle
- phLocal -- on success, receives the local handle
---*/
-
-PAL_ERROR
-CSharedMemoryObjectManager::ConvertRemoteHandleToLocal(
- CPalThread *pthr,
- RHANDLE rhRemote,
- HANDLE *phLocal
- )
-{
- PAL_ERROR palError = NO_ERROR;
- SHMObjData *psmod;
- CSharedMemoryObject *pshmobj = NULL;
- PLIST_ENTRY pleObjectList;
-
- _ASSERTE(NULL != pthr);
- _ASSERTE(NULL != phLocal);
-
- ENTRY("CSharedMemoryObjectManager::ConvertRemoteHandleToLocal "
- "(this=%p, pthr=%p, rhRemote=%p, phLocal=%p)\n",
- this,
- pthr,
- rhRemote,
- phLocal
- );
-
- if (rhRemote == NULL || rhRemote == INVALID_HANDLE_VALUE)
- {
- palError = ERROR_INVALID_HANDLE;
- goto ConvertRemoteHandleToLocalExitNoLockRelease;
- }
-
- InternalEnterCriticalSection(pthr, &m_csListLock);
- SHMLock();
-
- //
- // The remote handle is really a shared memory pointer to the
- // SHMObjData for the object.
- //
-
- psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, reinterpret_cast<SHMPTR>(rhRemote));
- if (NULL == psmod)
- {
- ERROR("Invalid remote handle\n");
- palError = ERROR_INVALID_HANDLE;
- goto ConvertRemoteHandleToLocalExit;
- }
-
- //
- // Check to see if a local reference for this object already
- // exists
- //
-
- if (0 != psmod->dwNameLength)
- {
- pleObjectList = &m_leNamedObjects;
- }
- else
- {
- pleObjectList = &m_leAnonymousObjects;
- }
-
- for (PLIST_ENTRY ple = pleObjectList->Flink;
- ple != pleObjectList;
- ple = ple->Flink)
- {
- pshmobj = CSharedMemoryObject::GetObjectFromListLink(ple);
-
- if (SharedObject == pshmobj->GetObjectDomain()
- && reinterpret_cast<SHMPTR>(rhRemote) == pshmobj->GetShmObjData())
- {
- TRACE("Object for remote handle already present in this process\n");
-
- //
- // PAL_LocalHandleToRemote bumped up the process refcount on the
- // object. Since this process already had a reference to the object
- // we need to decrement that reference now...
- //
-
- psmod->lProcessRefCount -= 1;
- _ASSERTE(0 < psmod->lProcessRefCount);
-
- //
- // We also need to add a reference to the object (since ReleaseReference
- // gets called below)
- //
-
- pshmobj->AddReference();
-
- break;
- }
-
- pshmobj = NULL;
- }
-
- if (NULL == pshmobj)
- {
- CObjectType *pot;
- CObjectAttributes oa;
-
- //
- // Get the local instance of the CObjectType
- //
-
- pot = CObjectType::GetObjectTypeById(psmod->eTypeId);
- if (NULL == pot)
- {
- ASSERT("Invalid object type ID in shared memory info\n");
- goto ConvertRemoteHandleToLocalExit;
- }
-
- //
- // Create the local state for the shared object
- //
-
- palError = ImportSharedObjectIntoProcess(
- pthr,
- pot,
- &oa,
- reinterpret_cast<SHMPTR>(rhRemote),
- psmod,
- FALSE,
- &pshmobj
- );
-
- if (NO_ERROR != palError)
- {
- goto ConvertRemoteHandleToLocalExit;
- }
- }
-
- //
- // Finally, allocate a local handle for the object
- //
-
- palError = ObtainHandleForObject(
- pthr,
- pshmobj,
- 0,
- FALSE,
- NULL,
- phLocal
- );
-
-ConvertRemoteHandleToLocalExit:
-
- SHMRelease();
- InternalLeaveCriticalSection(pthr, &m_csListLock);
-
-ConvertRemoteHandleToLocalExitNoLockRelease:
-
- if (NULL != pshmobj)
- {
- pshmobj->ReleaseReference(pthr);
- }
-
- LOGEXIT("CSharedMemoryObjectManager::ConvertRemoteHandleToLocal returns %d\n", palError);
-
- return palError;
-}
-
-/*++
-Function:
- PAL_RemoteHandleToLocal
-
- Given a "remote handle", return a local handle that refers to the
- specified process. Calls
- SharedMemoryObjectManager::ConvertRemoteHandleToLocal to do the actual
- work
-
-Parameters:
- rhRemote -- the "remote handle" to convert to a local handle
---*/
-
-PALIMPORT
-HANDLE
-PALAPI
-PAL_RemoteHandleToLocal(IN RHANDLE rhRemote)
-{
- PAL_ERROR palError = NO_ERROR;
- CPalThread *pthr;
- HANDLE hLocal = INVALID_HANDLE_VALUE;
-
- PERF_ENTRY(PAL_RemoteHandleToLocal);
- ENTRY("PAL_RemoteHandleToLocal( hRemote=0x%lx )\n", rhRemote);
-
- pthr = InternalGetCurrentThread();
-
- palError = static_cast<CSharedMemoryObjectManager*>(g_pObjectManager)->ConvertRemoteHandleToLocal(
- pthr,
- rhRemote,
- &hLocal
- );
-
- if (NO_ERROR != palError)
- {
- pthr->SetLastError(palError);
- }
-
- LOGEXIT("PAL_RemoteHandleToLocal returns HANDLE 0x%lx\n", hLocal);
- PERF_EXIT(PAL_RemoteHandleToLocal);
- return hLocal;
-}
-
-/*++
-Function:
CheckObjectTypeAndRights
Helper routine that determines if:
diff --git a/src/pal/src/objmgr/shmobjectmanager.hpp b/src/pal/src/objmgr/shmobjectmanager.hpp
index fbde872eeb..6e11b2095e 100644
--- a/src/pal/src/objmgr/shmobjectmanager.hpp
+++ b/src/pal/src/objmgr/shmobjectmanager.hpp
@@ -71,13 +71,6 @@ namespace CorUnix
CPalThread *pthr
);
- PAL_ERROR
- ConvertRemoteHandleToLocal(
- CPalThread *pthr,
- RHANDLE rhRemote,
- HANDLE *phLocal
- );
-
//
// IPalObjectManager routines
//
diff --git a/src/pal/src/shmemory/shmemory.cpp b/src/pal/src/shmemory/shmemory.cpp
index 35dadd6b3a..a12bd29c84 100644
--- a/src/pal/src/shmemory/shmemory.cpp
+++ b/src/pal/src/shmemory/shmemory.cpp
@@ -14,165 +14,14 @@ Abstract:
Implementation of shared memory infrastructure for IPC
-Issues :
-
- Interprocess synchronization
-
-
-There doesn't seem to be ANY synchronization mechanism that will work
-inter-process AND be pthread-safe. FreeBSD's pthread implementation has no
-support for inter-process synchronization (PTHREAD_PROCESS_SHARED);
-"traditionnal" inter-process syncronization functions, on the other hand, are
-not pthread-aware, and thus will block entire processes instead of only the
-calling thread.
-
-From suggestions and information obtained on the freebsd-hackers mailing list,
-I have come up with 2 possible strategies to ensure serialized access to our
-shared memory region
-
-Note that the estimates of relative efficiency are wild guesses; my assumptions
-are that blocking entire processes is least efficient, busy wait somewhat
-better, and anything that does neither is preferable. However, the overhead of
-complex solutions is likely to have an important impact on performance
-
-Option 1 : very simple; possibly less efficient. in 2 words : "busy wait"
-Basically,
-
-while(InterlockedCompareExchange(spinlock_in_shared_memory, 1, 0)
- sched_yield();
-
-In other words, if a value is 0, set it to 1; otherwise, try again until we
-succeed. use shed_yield to give the system a chance to schedule other threads
-while we wait. (once a thread succeeds at this, it does its work, then sets
-the value back to 0)
-One inconvenient : threads will not unblock in the order they are blocked;
-once a thread releases the mutex, whichever waiting thread is scheduled next
-will be unblocked. This is what is called the "thundering herd" problem, and in
-extreme cases, can lead to starvation
-Update : we'll set the spinlock to our PID instead of 1, that way we can find
-out if the lock is held by a dead process.
-
-Option 2 : possibly more efficient, much more complex, borders on
-"over-engineered". I'll explain it in stages, in the same way I deduced it.
-
-Option 2.1 : probably less efficient, reasonably simple. stop at step 2)
-
-1) The minimal, original idea was to use SysV semaphores for synchronization.
-This didn't work, because semaphores block the entire process, which can easily
-lead to deadlocks (thread 1 takes sem, thread 2 tries to take sem, blocks
-process, thread 1 is blocked and never releases sem)
-
-2) (this is option 2.1) Protect the use of the semaphores in critical sections.
-Enter the critical section before taking the semaphore, leave the section after
-releasing the semaphore. This ensures that 2 threads of the same process will
-never try to acquire the semaphore at the same time, which avoids deadlocks.
-However, the entire process still blocks if another process has the semaphore.
-Here, unblocking order should match blocking order (assuming the semaphores work
-properly); therefore, no risk of starvation.
-
-3) This is where it gets complicated. To avoid blocking whole processes, we
-can't use semaphores. One suggestion I got was to use multi-ended FIFOs, here's
-how it would work.
-
--as in option 1, use InterlockedCompareExchange on a value in shared memory.
--if this was not succesful (someone else has locked the shared memory), then :
- -open a special FIFO for reading; try to read 1 byte. This will block until
- someone writes to it, and *should* only block the current thread. (note :
- more than one thread/process can open the same FIFO and block on read(),
- in this case, only one gets woken up when someone writes to it.
- *which* one is, again, not predictable; this may lead to starvation)
- -once we are unblocked, we have the lock.
--once we have the lock (either from Interlocked...() or from read()),
- we can do our work
--once the work is done, we open the FIFO for writing. this will fail if no one
- is listening.
--if no one is listening, release the lock by setting the shared memory value
- back to 0
--if someone is listening, write 1 byte to the FIFO to wake someone, then close
- the FIFO. the value in shared memory will remain nonzero until a thread tries
- to wake the next one and sees no one is listening.
-
-problem with this option : it is possible for a thread to call Interlocked...()
-BETWEEN the failed "open for write" attempt and the subsequent restoration of
-the SHM value back to zero. In this case, that thread will go to sleep and will
-not wake up until *another* thread asks for the lock, takes it and releases it.
-
-so to fix that, we come to step
-
-4) Instead of using InterlockedCompareExchange, use a SysV semaphore :
--when taking the lock :
- -take the semaphore
- -try to take the lock (check if value is zero, change it to 1 if it is)
- -if we fail : open FIFO for reading, release the semaphore, read() and block
- -if we succeed : release the semaphore
--when releasing the lock :
- -take the semaphore
- -open FIFO for write
- -if we succeed, release semaphore, then write value
- -if we fail, reset SHM value to 0, then release semaphore.
-
-Yes, using a SysV semaphore will block the whole process, but for a very short
-time (unlike option 2.1)
-problem with this : again, we get deadlocks if 2 threads from a single process
-try to take the semaphore. So like in option 2.1, we ave to wrap the semaphore
-usage in a critical section. (complex enough yet?)
-
-so the locking sequence becomes EnterCriticalSection - take semaphore - try to
- lock - open FIFO - release semaphore - LeaveCriticalSection - read
-and the unlocking sequence becomes EnterCS - take sem - open FIFO - release
- sem - LeaveCS - write
-
-Once again, the unblocking order probably won't match the blocking order.
-This could be fixed by using multiple FIFOs : waiting thread open their own
-personal FIFO, write the ID of their FIFO to another FIFO. The thread that wants
-to release the lock reads ID from that FIFO, determines which FIFO to open for
-writing and writes a byte to it. This way, whoever wrote its ID to the FIFO
-first will be first to awake. How's that for complexity?
-
-So to summarize, the options are
-1 - busy wait
-2.1 - semaphores + critical sections (whole process blocks)
-2 - semaphores + critical sections + FIFOs (minimal process blocking)
-2.2 - option 2 with multiple FIFOs (minimal process blocking, order preserved)
-
-Considering the overhead involved in options 2 & 2.2, it is our guess that
-option 1 may in fact be more efficient, and this is how we'll implement it for
-the moment. Note that other platforms may not present the same difficulties
-(i.e. other pthread implementations may support inter-process mutexes), and may
-be able to use a simpler, more efficient approach.
-
-B] Reliability.
-It is important for the shared memory implementation to be as foolproof as
-possible. Since more than one process will be able to modify the shared data,
-it becomes possible for one unstable process to destabilize the others. The
-simplest example is a process that dies while modifying shared memory : if
-it doesn't release its lock, we're in trouble. (this case will be taken care
-of by using PIDs in the spinlock; this we we can check if the locking process
-is still alive).
-
--*/
-#include "config.h"
-#include "pal/palinternal.h"
#include "pal/dbgmsg.h"
#include "pal/shmemory.h"
#include "pal/critsect.h"
-#include "pal/shmemory.h"
-#include "pal/init.h"
#include "pal/process.h"
-#include "pal/misc.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <string.h>
-#include <sched.h>
-#include <pthread.h>
#if HAVE_YIELD_SYSCALL
#include <sys/syscall.h>
@@ -180,155 +29,25 @@ is still alive).
SET_DEFAULT_DEBUG_CHANNEL(SHMEM);
-/* Macro-definitions **********************************************************/
-
-/* rounds 'val' up to be divisible by 'r'. 'r' must be a power of two. */
-#ifndef roundup
-#define roundup(val, r) ( ((val)+(r)-1) & ~( (r)-1 ) )
-#endif
-
-#define SEGMENT_NAME_SUFFIX_LENGTH 10
-
-/*
-SHMPTR structure :
-High byte is SHM segment number
-Low bytes are offset in the segment
- */
-#define SHMPTR_SEGMENT(shmptr) \
- (((shmptr)>>24)&0xFF)
-
-#define SHMPTR_OFFSET(shmptr) \
- ((shmptr)&0x00FFFFFF)
-
-#define MAKE_SHMPTR(segment,offset) \
- ((SHMPTR)((((segment)&0xFF)<<24)|((offset)&0x00FFFFFF)))
-
-/*#define MAX_SEGMENTS 256*//*definition is now in shmemory.h*/
-
-/* Use MAP_NOSYNC to improve performance if it's available */
-#if defined(MAP_NOSYNC)
-#define MAPFLAGS MAP_NOSYNC|MAP_SHARED
-#else
-#define MAPFLAGS MAP_SHARED
-#endif
-
-
/* Type definitions ***********************************************************/
-enum SHM_POOL_SIZES
-{
- SPS_16 = 0, /* 16 bytes */
- SPS_32, /* 32 bytes */
- SPS_64, /* 64 bytes */
- SPS_MAXPATHx2, /* 520 bytes, for long Unicode paths */
-
- SPS_LAST
-};
-/* Block size associated to each SPS identifier */
-static const int block_sizes[SPS_LAST] = {16,32,64,roundup((MAX_LONGPATH+1)*2, sizeof(INT64))};
-
-/*
-SHM_POOL_INFO
-Description of a shared memory pool for a specific block size.
-
-Note on pool structure :
-first_free identifies the first available SHMPTR in the block. Free blocks are
-arranged in a linked list, each free block indicating the location of the next
-one. To walk the list, do something like this :
-SHMPTR *shmptr_ptr=(SHMPTR *)SHMPTR_TO_PTR(pool->first_free)
-while(shm_ptr)
-{
- SHMPTR next = *shmptr_ptr;
- shmptr_ptr = (SHMPTR *)SHMPTR_TO_PTR(next)
-}
- */
-typedef struct
-{
- int item_size; /* size of 1 block, in bytes */
- int num_items; /* total number of blocks in the pool */
- int free_items; /* number of unused items in the pool */
- SHMPTR first_free; /* location of first available block in the pool */
-}SHM_POOL_INFO;
-
/*
-SHM_SEGMENT_HEADER
-Description of a single shared memory segment
-
-Notes on segment names :
-next_semgent contains the string generated by mkstemp() when a new segment is
-generated. This allows processes to map segment files created by other
-processes. To get the file name of a segment file, concatenate
-"segment_name_prefix" and "next_segment".
-
-Notes on pool segments :
-Each segment is divided into one pool for each defined block size (SPS_*).
-These pools are linked with pools in other segment to form one large pool for
-each block size, so that SHMAlloc() doesn't have to search each segment to find
-an available block.
-the first_ and last_pool_blocks indicate the first and last block in a single
-segment for each block size. This allows SHMFree() to determine the size of a
-block by comparing its value with these boundaries. (note that within each
-segment, each pool is composed of a single contiguous block of memory)
-*/
-typedef struct
-{
- Volatile<SHMPTR> first_pool_blocks[SPS_LAST];
- Volatile<SHMPTR> last_pool_blocks[SPS_LAST];
-} SHM_SEGMENT_HEADER;
-
-/*
-SHM_FIRST_HEADER
+SHM_HEADER
Global information about the shared memory system
-In addition to the standard SHM_SEGGMENT_HEADER, the first segment contains some
-information required to properly use the shared memory system.
The spinlock is used to ensure that only one process accesses shared memory at
the same time. A process can only take the spinlock if its contents is 0, and
it takes the spinlock by placing its PID in it. (this allows a process to catch
the special case where it tries to take a spinlock it already owns.
-
-The first_* members will contain the location of the first element in the
-various linked lists of shared information
- */
-
-#ifdef TRACK_SHMLOCK_OWNERSHIP
-
-#define SHMLOCK_OWNERSHIP_HISTORY_ARRAY_SIZE 5
-
-#define CHECK_CANARIES(header) \
- _ASSERTE(HeadSignature == header->dwHeadCanaries[0]); \
- _ASSERTE(HeadSignature == header->dwHeadCanaries[1]); \
- _ASSERTE(TailSignature == header->dwTailCanaries[0]); \
- _ASSERTE(TailSignature == header->dwTailCanaries[1])
-
-typedef struct _pid_and_tid
-{
- Volatile<pid_t> pid;
- Volatile<pthread_t> tid;
-} pid_and_tid;
-
-const DWORD HeadSignature = 0x48454144;
-const DWORD TailSignature = 0x5441494C;
-
-#endif // TRACK_SHMLOCK_OWNERSHIP
+*/
typedef struct
{
- SHM_SEGMENT_HEADER header;
-#ifdef TRACK_SHMLOCK_OWNERSHIP
- Volatile<DWORD> dwHeadCanaries[2];
-#endif // TRACK_SHMLOCK_OWNERSHIP
Volatile<pid_t> spinlock;
-#ifdef TRACK_SHMLOCK_OWNERSHIP
- Volatile<DWORD> dwTailCanaries[2];
- pid_and_tid pidtidCurrentOwner;
- pid_and_tid pidtidOwners[SHMLOCK_OWNERSHIP_HISTORY_ARRAY_SIZE];
- Volatile<ULONG> ulOwnersIdx;
-#endif // TRACK_SHMLOCK_OWNERSHIP
- SHM_POOL_INFO pools[SPS_LAST]; /* information about each memory pool */
Volatile<SHMPTR> shm_info[SIID_LAST]; /* basic blocks of shared information.*/
-} SHM_FIRST_HEADER;
+} SHM_HEADER;
+static SHM_HEADER shm_header;
/* Static variables ***********************************************************/
@@ -343,12 +62,6 @@ memory. Rationale :
*/
static CRITICAL_SECTION shm_critsec;
-/* number of segments the current process knows about */
-int shm_numsegments;
-
-/* array containing the base address of each segment */
-Volatile<LPVOID> shm_segment_bases[MAX_SEGMENTS];
-
/* number of locks the process currently holds (SHMLock calls without matching
SHMRelease). Because we take the critical section while inside a
SHMLock/SHMRelease pair, this is actually the number of locks held by a single
@@ -359,24 +72,6 @@ static Volatile<LONG> lock_count;
SHMGet/SetInfo will verify that the calling thread holds the lock */
static Volatile<HANDLE> locking_thread;
-/* Constants ******************************************************************/
-
-/* size of a single segment : 256KB */
-static const int segment_size = 0x40000;
-
-/* Static function prototypes *************************************************/
-
-static SHMPTR SHMInitPool(SHMPTR first, int block_size, int pool_size,
- SHM_POOL_INFO *pool);
-static SHMPTR SHMLinkPool(SHMPTR first, int block_size, int num_blocks);
-static BOOL SHMMapUnknownSegments(void);
-static BOOL SHMAddSegment(void);
-
-
-#define init_waste()
-#define log_waste(x,y)
-#define save_waste()
-
/* Public function implementations ********************************************/
/*++
@@ -390,94 +85,18 @@ BOOL SHMInitialize(void)
{
InternalInitializeCriticalSection(&shm_critsec);
- init_waste();
-
- int size;
- SHM_FIRST_HEADER *header;
- SHMPTR pool_start;
- SHMPTR pool_end;
- enum SHM_POOL_SIZES sps;
-
TRACE("Now initializing global shared memory system\n");
-
- // Not really shared in CoreCLR; we don't try to talk to other CoreCLRs.
- shm_segment_bases[0] = mmap(NULL, segment_size,PROT_READ|PROT_WRITE,
- MAP_ANON|MAP_PRIVATE, -1, 0);
- if(shm_segment_bases[0] == MAP_FAILED)
- {
- ERROR("mmap() failed; error is %d (%s)\n", errno, strerror(errno));
- return FALSE;
- }
- TRACE("Mapped first SHM segment at %p\n",shm_segment_bases[0].Load());
-
- /* Initialize first segment's header */
- header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
- InterlockedExchange((LONG *)&header->spinlock, 0);
-
-#ifdef TRACK_SHMLOCK_OWNERSHIP
- header->dwHeadCanaries[0] = HeadSignature;
- header->dwHeadCanaries[1] = HeadSignature;
- header->dwTailCanaries[0] = TailSignature;
- header->dwTailCanaries[1] = TailSignature;
-
- // Check spinlock size
- _ASSERTE(sizeof(DWORD) == sizeof(header->spinlock));
- // Check spinlock alignment
- _ASSERTE(0 == ((DWORD_PTR)&header->spinlock % (DWORD_PTR)sizeof(void *)));
-#endif // TRACK_SHMLOCK_OWNERSHIP
-
-#ifdef TRACK_SHMLOCK_OWNERSHIP
- header->pidtidCurrentOwner.pid = 0;
- header->pidtidCurrentOwner.tid = 0;
- memset((void *)header->pidtidOwners, 0, sizeof(header->pidtidOwners));
- header->ulOwnersIdx = 0;
-#endif // TRACK_SHMLOCK_OWNERSHIP
+ InterlockedExchange((LONG *)&shm_header.spinlock, 0);
/* SHM information array starts with NULLs */
- memset((void *)header->shm_info, 0, SIID_LAST*sizeof(SHMPTR));
-
- /* Initialize memory pools */
-
- /* first pool starts right after header */
- pool_start = roundup(sizeof(SHM_FIRST_HEADER), sizeof(INT64));
-
- /* Same size for each pool, ensuring alignment is correct */
- size = ((segment_size-pool_start)/SPS_LAST) & ~(sizeof(INT64)-1);
-
- for (sps = static_cast<SHM_POOL_SIZES>(0); sps < SPS_LAST;
- sps = static_cast<SHM_POOL_SIZES>(sps + 1))
- {
- pool_end = SHMInitPool(pool_start, block_sizes[sps], size,
- (SHM_POOL_INFO *)&header->pools[sps]);
-
- if(pool_end ==0)
- {
- ERROR("SHMInitPool failed.\n");
- munmap(shm_segment_bases[0],segment_size);
- return FALSE;
- }
- /* save first and last element of each pool for this segment */
- header->header.first_pool_blocks[sps] = pool_start;
- header->header.last_pool_blocks[sps] = pool_end;
-
- /* next pool starts immediately after this one */
- pool_start +=size;
- }
+ memset((void *)shm_header.shm_info, 0, SIID_LAST*sizeof(SHMPTR));
TRACE("Global shared memory initialization complete.\n");
- shm_numsegments = 1;
lock_count = 0;
locking_thread = 0;
- /* hook into all SHM segments */
- if(!SHMMapUnknownSegments())
- {
- ERROR("Error while mapping segments!\n");
- SHMCleanup();
- return FALSE;
- }
return TRUE;
}
@@ -494,7 +113,6 @@ in PALCommonCleanup.
--*/
void SHMCleanup(void)
{
- SHM_FIRST_HEADER *header;
pid_t my_pid;
TRACE("Starting shared memory cleanup\n");
@@ -505,9 +123,8 @@ void SHMCleanup(void)
/* We should not be holding the spinlock at this point. If we are, release
the spinlock. by setting it to 0 */
my_pid = gPID;
- header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
- _ASSERT_MSG(header->spinlock != my_pid,
+ _ASSERT_MSG(shm_header.spinlock != my_pid,
"SHMCleanup called while the current process still owns the lock "
"[owner thread=%u, current thread: %u]\n",
locking_thread.Load(), THREADSilentGetCurrentThreadId());
@@ -515,237 +132,10 @@ void SHMCleanup(void)
/* Now for the interprocess stuff. */
DeleteCriticalSection(&shm_critsec);
-
- /* Unmap memory segments */
- while(shm_numsegments)
- {
- shm_numsegments--;
- if ( -1 == munmap( shm_segment_bases[ shm_numsegments ],
- segment_size ) )
- {
- ASSERT( "munmap() failed; errno is %d (%s).\n",
- errno, strerror( errno ) );
- }
- }
-
- save_waste();
TRACE("SHMCleanup complete!\n");
}
/*++
-SHMalloc
-
-Allocate a block of memory of the specified size
-
-Parameters :
- size_t size : size of block required
-
-Return value :
- A SHMPTR identifying the new block, or 0 on failure. Use SHMPTR_TO_PTR to
- convert a SHMPTR into a useable pointer (but remember to lock the shared
- memory first!)
-
-Notes :
- SHMalloc will fail if the requested size is larger than a certain maximum.
- At the moment, the maximum is 520 bytes (MAX_LONGPATH*2).
---*/
-SHMPTR SHMalloc(size_t size)
-{
- enum SHM_POOL_SIZES sps;
- SHMPTR first_free;
- SHMPTR next_free;
- SHM_FIRST_HEADER *header;
- SHMPTR *shmptr_ptr;
-
- TRACE("SHMalloc() called; requested size is %u\n", size);
-
- if(0 == size)
- {
- WARN("Got a request for a 0-byte block! returning 0\n");
- return 0;
- }
-
- /* Find the first block size >= requested size */
- for (sps = static_cast<SHM_POOL_SIZES>(0); sps < SPS_LAST;
- sps = static_cast<SHM_POOL_SIZES>(sps + 1))
- {
- if (size <= static_cast<size_t>(block_sizes[sps]))
- {
- break;
- }
- }
-
- /* If no block size is found, requested size was too large. */
- if( SPS_LAST == sps )
- {
- ASSERT("Got request for shared memory block of %u bytes; maximum block "
- "size is %d.\n", size, block_sizes[SPS_LAST-1]);
- return 0;
- }
-
- TRACE("Best block size is %d (%d bytes wasted)\n",
- block_sizes[sps], block_sizes[sps]-size );
-
- log_waste(sps, block_sizes[sps]-size);
-
- SHMLock();
- header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
-
- /* If there are no free items of the specified size left, it's time to
- allocate a new shared memory segment.*/
- if(header->pools[sps].free_items == 0)
- {
- TRACE("No blocks of %d bytes left; allocating new segment.\n",
- block_sizes[sps]);
- if(!SHMAddSegment())
- {
- ERROR("Unable to allocate new shared memory segment!\n");
- SHMRelease();
- return 0;
- }
- }
-
- /* Remove the first free block from the pool */
- first_free = header->pools[sps].first_free;
- shmptr_ptr = static_cast<SHMPTR*>(SHMPTR_TO_PTR(first_free));
-
- if( 0 == first_free )
- {
- ASSERT("First free block in %d-byte pool (%08x) was invalid!\n",
- block_sizes[sps], first_free);
- SHMRelease();
- return 0;
- }
-
- /* the block "first_free" is the head of a linked list of free blocks;
- take the next link in the list and set it as new head of list. */
- next_free = *shmptr_ptr;
- header->pools[sps].first_free = next_free;
- header->pools[sps].free_items--;
-
- /* make sure we're still in a sane state */
- if(( 0 == header->pools[sps].free_items && 0 != next_free) ||
- ( 0 != header->pools[sps].free_items && 0 == next_free))
- {
- ASSERT("free block count is %d, but next free block is %#x\n",
- header->pools[sps].free_items, next_free);
- /* assume all remaining blocks in the pool are corrupt */
- header->pools[sps].first_free = 0;
- header->pools[sps].free_items = 0;
- }
- else if (0 != next_free && 0 == SHMPTR_TO_PTR(next_free) )
- {
- ASSERT("Next free block (%#x) in %d-byte pool is invalid!\n",
- next_free, block_sizes[sps]);
- /* assume all remaining blocks in the pool are corrupt */
- header->pools[sps].first_free = 0;
- header->pools[sps].free_items = 0;
- }
-
- SHMRelease();
-
- TRACE("Allocation successful; %d blocks of %d bytes left. Returning %08x\n",
- header->pools[sps].free_items, block_sizes[sps], first_free);
- return first_free;
-}
-
-/*++
-SHMfree
-
-Release a block of shared memory and put it back in the shared memory pool
-
-Parameters :
- SHMPTR shmptr : identifier of block to release
-
-(no return value)
---*/
-void SHMfree(SHMPTR shmptr)
-{
- int segment;
- int offset;
- SHM_SEGMENT_HEADER *header;
- SHM_FIRST_HEADER *first_header;
- enum SHM_POOL_SIZES sps;
- SHMPTR *shmptr_ptr;
-
- if(0 == shmptr)
- {
- WARN("can't SHMfree() a NULL SHMPTR!\n");
- return;
- }
- SHMLock();
-
- TRACE("Releasing SHMPTR 0x%08x\n", shmptr);
-
- shmptr_ptr = static_cast<SHMPTR*>(SHMPTR_TO_PTR(shmptr));
-
- if(!shmptr_ptr)
- {
- ASSERT("Tried to free an invalid shared memory pointer 0x%08x\n", shmptr);
- SHMRelease();
- return;
- }
-
- /* note : SHMPTR_TO_PTR has already validated the segment/offset pair */
- segment = SHMPTR_SEGMENT(shmptr);
- header = (SHM_SEGMENT_HEADER *)shm_segment_bases[segment].Load();
-
- /* Find out the size of this block. Each segment tells where are its first
- and last blocks for each block size, so we simply need to check in which
- interval the block fits */
- for (sps = static_cast<SHM_POOL_SIZES>(0); sps < SPS_LAST;
- sps = static_cast<SHM_POOL_SIZES>(sps + 1))
- {
- if(header->first_pool_blocks[sps]<=shmptr &&
- header->last_pool_blocks[sps]>=shmptr)
- {
- break;
- }
- }
-
- /* If we didn't find an interval, then the block doesn't really belong in
- this segment (shouldn't happen, the offset check in SHMPTR_TO_PTR should
- have caught this.) */
- if(sps == SPS_LAST)
- {
- ASSERT("Shared memory pointer 0x%08x is out of bounds!\n", shmptr);
- SHMRelease();
- return;
- }
-
- TRACE("SHMPTR 0x%08x is a %d-byte block located in segment %d\n",
- shmptr, block_sizes[sps], segment);
-
- /* Determine the offset of this block (in bytes) relative to the first
- block of the same size in this segment */
- offset = shmptr - header->first_pool_blocks[sps];
-
- /* Make sure that the offset is a multiple of the block size; otherwise,
- this isn't a real SHMPTR */
- if( 0 != ( offset % block_sizes[sps] ) )
- {
- ASSERT("Shared memory pointer 0x%08x is misaligned!\n", shmptr);
- SHMRelease();
- return;
- }
-
- /* Put the SHMPTR back in its pool. */
- first_header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
-
- /* first_free is the head of a linked list of free SHMPTRs. All we need to
- do is make shmptr point to first_free, and set shmptr as the new head
- of the list. */
- *shmptr_ptr = first_header->pools[sps].first_free;
- first_header->pools[sps].first_free = shmptr;
- first_header->pools[sps].free_items++;
-
- TRACE("SHMPTR 0x%08x released; there are now %d blocks of %d bytes "
- "available\n", shmptr, first_header->pools[sps].free_items,
- block_sizes[sps]);
- SHMRelease();
-}
-
-/*++
SHMLock
Restrict shared memory access to the current thread of the current process
@@ -769,17 +159,11 @@ int SHMLock(void)
if(lock_count == 0)
{
- SHM_FIRST_HEADER *header;
pid_t my_pid, tmp_pid;
int spincount = 1;
-#ifdef TRACK_SHMLOCK_OWNERSHIP
- ULONG ulIdx;
-#endif // TRACK_SHMLOCK_OWNERSHIP
TRACE("First-level SHM lock : taking spinlock\n");
- header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
-
// Store the id of the current thread as the (only) one that is
// trying to grab the spinlock from the current process
locking_thread = (HANDLE)pthread_self();
@@ -788,21 +172,10 @@ int SHMLock(void)
while(TRUE)
{
-#ifdef TRACK_SHMLOCK_OWNERSHIP
- _ASSERTE(0 != my_pid);
- _ASSERTE(getpid() == my_pid);
- _ASSERTE(my_pid != header->spinlock);
- CHECK_CANARIES(header);
-#endif // TRACK_SHMLOCK_OWNERSHIP
-
//
// Try to grab the spinlock
//
- tmp_pid = InterlockedCompareExchange((LONG *) &header->spinlock, my_pid,0);
-
-#ifdef TRACK_SHMLOCK_OWNERSHIP
- CHECK_CANARIES(header);
-#endif // TRACK_SHMLOCK_OWNERSHIP
+ tmp_pid = InterlockedCompareExchange((LONG *) &shm_header.spinlock, my_pid,0);
if (0 == tmp_pid)
{
@@ -821,7 +194,7 @@ int SHMLock(void)
TRACE("SHM spinlock owner (%08x) is dead; releasing its lock\n",
tmp_pid);
- InterlockedCompareExchange((LONG *) &header->spinlock, 0, tmp_pid);
+ InterlockedCompareExchange((LONG *) &shm_header.spinlock, 0, tmp_pid);
}
else
{
@@ -856,31 +229,15 @@ int SHMLock(void)
spincount++;
}
- _ASSERT_MSG(my_pid == header->spinlock,
+ _ASSERT_MSG(my_pid == shm_header.spinlock,
"\n(my_pid = %u) != (header->spinlock = %u)\n"
"tmp_pid = %u\n"
"spincount = %d\n"
"locking_thread = %u\n",
- (DWORD)my_pid, (DWORD)header->spinlock,
+ (DWORD)my_pid, (DWORD)shm_header.spinlock,
(DWORD)tmp_pid,
(int)spincount,
(HANDLE)locking_thread);
-
-#ifdef TRACK_SHMLOCK_OWNERSHIP
- _ASSERTE(0 == header->pidtidCurrentOwner.pid);
- _ASSERTE(0 == header->pidtidCurrentOwner.tid);
-
- header->pidtidCurrentOwner.pid = my_pid;
- header->pidtidCurrentOwner.tid = locking_thread;
-
- ulIdx = header->ulOwnersIdx % (sizeof(header->pidtidOwners) / sizeof(header->pidtidOwners[0]));
-
- header->pidtidOwners[ulIdx].pid = my_pid;
- header->pidtidOwners[ulIdx].tid = locking_thread;
-
- header->ulOwnersIdx += 1;
-#endif // TRACK_SHMLOCK_OWNERSHIP
-
}
lock_count++;
@@ -919,32 +276,15 @@ int SHMRelease(void)
set the spinlock back to 0. */
if(lock_count == 0)
{
- SHM_FIRST_HEADER *header;
pid_t my_pid, tmp_pid;
TRACE("Releasing first-level SHM lock : resetting spinlock\n");
my_pid = gPID;
-
- header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
-
-#ifdef TRACK_SHMLOCK_OWNERSHIP
- CHECK_CANARIES(header);
- _ASSERTE(0 != my_pid);
- _ASSERTE(getpid() == my_pid);
- _ASSERTE(my_pid == header->spinlock);
- _ASSERTE(header->pidtidCurrentOwner.pid == my_pid);
- _ASSERTE(pthread_self() == header->pidtidCurrentOwner.tid);
- _ASSERTE((pthread_t)locking_thread == header->pidtidCurrentOwner.tid);
-
- header->pidtidCurrentOwner.pid = 0;
- header->pidtidCurrentOwner.tid = 0;
-#endif // TRACK_SHMLOCK_OWNERSHIP
-
/* Make sure we don't touch the spinlock if we don't own it. We're
supposed to own it if we get here, but just in case... */
- tmp_pid = InterlockedCompareExchange((LONG *) &header->spinlock, 0, my_pid);
+ tmp_pid = InterlockedCompareExchange((LONG *) &shm_header.spinlock, 0, my_pid);
if (tmp_pid != my_pid)
{
@@ -956,10 +296,6 @@ int SHMRelease(void)
/* indicate no thread (in this process) holds the SHM lock */
locking_thread = 0;
-
-#ifdef TRACK_SHMLOCK_OWNERSHIP
- CHECK_CANARIES(header);
-#endif // TRACK_SHMLOCK_OWNERSHIP
}
TRACE("SHM lock level is now %d\n", lock_count.Load());
@@ -974,99 +310,6 @@ int SHMRelease(void)
}
/*++
-SHMPtrToPtr
-
-Convert a SHMPTR value to a valid pointer within the address space of the
-current process
-
-Parameters :
- SHMPTR shmptr : SHMPTR value to convert into a pointer
-
-Return value :
- Address corresponding to the given SHMPTR, valid for the current process
-
-Notes :
-(see notes for SHMPTR_SEGMENT macro for details on SHMPTR structure)
-
-It is possible for the segment index to be greater than the known total number
-of segments (shm_numsegments); this means that the SHMPTR points to a memory
-block in a shared memory segment this process doesn't know about. In this case,
-we must obtain an address for that new segment and add it to our array
-(see SHMMapUnknownSegments for details)
-
-In the simplest case (no need to map new segments), there is no need to hold
-the lock, since we don't access any information that can change
---*/
-LPVOID SHMPtrToPtr(SHMPTR shmptr)
-{
- void *retval;
- int segment;
- int offset;
-
- TRACE("Converting SHMPTR 0x%08x to a valid pointer...\n", shmptr);
- if(!shmptr)
- {
- WARN("Got SHMPTR \"0\"; returning NULL pointer\n");
- return NULL;
- }
-
- segment = SHMPTR_SEGMENT(shmptr);
-
- /* If segment isn't known, it may have been added by another process. We
- need to map all new segments into our address space. */
- if(segment>= shm_numsegments)
- {
- TRACE("SHMPTR is in segment %d, we know only %d. We must now map all "
- "unknowns.\n", segment, shm_numsegments);
- SHMMapUnknownSegments();
-
- /* if segment is still unknown, then it doesn't exist */
- if(segment>=shm_numsegments)
- {
- ASSERT("Segment %d still unknown; returning NULL\n", segment);
- return NULL;
- }
- TRACE("Segment %d found; continuing\n", segment);
- }
-
- /* Make sure the offset doesn't point outside the segment */
- offset = SHMPTR_OFFSET(shmptr);
- if(offset>=segment_size)
- {
- ASSERT("Offset %d is larger than segment size (%d)! returning NULL\n",
- offset, segment_size);
- return NULL;
-
- }
-
- /* Make sure the offset doesn't point in the segment's header */
- if(segment == 0)
- {
- if (static_cast<size_t>(offset) < roundup(sizeof(SHM_FIRST_HEADER), sizeof(INT64)))
- {
- ASSERT("Offset %d is in segment header! returning NULL\n", offset);
- return NULL;
- }
- }
- else
- {
- if (static_cast<size_t>(offset) < sizeof(SHM_SEGMENT_HEADER))
- {
- ASSERT("Offset %d is in segment header! returning NULL\n", offset);
- return NULL;
- }
- }
-
- retval = shm_segment_bases[segment];
- retval = static_cast<BYTE*>(retval) + offset;
-
- TRACE("SHMPTR %#x is at offset %d in segment %d; maps to address %p\n",
- shmptr, offset, segment, retval);
- return retval;
-}
-
-
-/*++
Function :
SHMGetInfo
@@ -1083,7 +326,6 @@ Notes :
--*/
SHMPTR SHMGetInfo(SHM_INFO_ID element)
{
- SHM_FIRST_HEADER *header = NULL;
SHMPTR retval = 0;
if(element < 0 || element >= SIID_LAST)
@@ -1099,9 +341,7 @@ SHMPTR SHMGetInfo(SHM_INFO_ID element)
ASSERT("SHMGetInfo called while thread does not hold the SHM lock!\n");
}
- header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
-
- retval = header->shm_info[element];
+ retval = shm_header.shm_info[element];
TRACE("SHM info element %d is %08x\n", element, retval );
return retval;
@@ -1126,8 +366,6 @@ Notes :
--*/
BOOL SHMSetInfo(SHM_INFO_ID element, SHMPTR value)
{
- SHM_FIRST_HEADER *header;
-
if(element < 0 || element >= SIID_LAST)
{
ASSERT("Invalid SHM info element %d\n", element);
@@ -1141,558 +379,10 @@ BOOL SHMSetInfo(SHM_INFO_ID element, SHMPTR value)
ASSERT("SHMGetInfo called while thread does not hold the SHM lock!\n");
}
- header = (SHM_FIRST_HEADER*)shm_segment_bases[0].Load();
-
TRACE("Setting SHM info element %d to %08x; used to be %08x\n",
- element, value, header->shm_info[element].Load() );
+ element, value, shm_header.shm_info[element].Load() );
- header->shm_info[element] = value;
+ shm_header.shm_info[element] = value;
return TRUE;
-}
-
-
-/* Static function implementations ********************************************/
-
-/*++
-SHMInitPool
-
-Perform one-time initialization for a shared memory pool.
-
-Parameters :
- SHMPTR first : SHMPTR of first memory block in the pool
- int block_size : size (in bytes) of a memory block in this pool
- int pool_size : total size (in bytes) of this pool
- SHM_POOL_INFO *pool : pointer to initialize with information about the pool
-
-Return value :
- SHMPTR of last memory block in the pool
-
-Notes :
-This function is used to initialize the memory pools of the first SHM segment.
-In addition to creating a linked list of SHMPTRs, it initializes the given
-SHM_POOL_INFO based on the given information.
---*/
-static SHMPTR SHMInitPool(SHMPTR first, int block_size, int pool_size,
- SHM_POOL_INFO *pool)
-{
- int num_blocks;
- SHMPTR last;
-
- TRACE("Initializing SHM pool for %d-byte blocks\n", block_size);
-
- /* Number of memory blocks of size "block_size" that can fit in "pool_size"
- bytes (rounded down) */
- num_blocks = pool_size/block_size;
-
- /* Create the initial linked list of free blocks */
- last = SHMLinkPool(first, block_size, num_blocks);
- if( 0 == last )
- {
- ERROR("Failed to create linked list of free blocks!\n");
- return 0;
- }
-
- /* Initialize SHM_POOL_INFO */
- pool->first_free = first;
- pool->free_items = num_blocks;
- pool->item_size = block_size;
- pool->num_items = num_blocks;
-
- TRACE("New SHM pool extends from SHMPTR 0x%08x to 0x%08x\n", first, last);
- return last;
-}
-
-/*++
-SHMLinkPool
-
-Joins contiguous blocks of memory into a linked list..
-
-Parameters :
- SHMPTR first : First SHMPTR in the memory pool; first link in the list
- int block_size : size (in bytes) of the memory blocks
- int num_blocks : number of contiguous blocks to link
-
-Return value :
- SHMPTR of last memory block in the pool
-
-Notes :
-The linked list is created by saving the value of the next SHMPTR in the list
-in the memory location corresponding to the previous SHMPTR :
-*(SHMPTR *)SHMPTR_TO_PTR(previous) = previous + block_size
---*/
-static SHMPTR SHMLinkPool(SHMPTR first, int block_size, int num_blocks)
-{
- LPBYTE item_ptr;
- SHMPTR *shmptr_ptr;
- SHMPTR next_shmptr;
- int i;
-
- TRACE("Linking %d blocks of %d bytes, starting at 0x%08x\n",
- num_blocks, block_size, first);
-
- item_ptr = static_cast<LPBYTE>(
- static_cast<LPBYTE>(shm_segment_bases[SHMPTR_SEGMENT(first)].Load()) +
- (SHMPTR_OFFSET(first)));
- next_shmptr = first/*+block_size*/;
-
- /* Link blocks together */
- for(i=0; i<num_blocks; i++)
- {
- next_shmptr += block_size;
-
- /* item_ptr is char * (so we can increment with +=blocksize), we cast
- it to a SHMPTR * and set its content to the next SHMPTR in the list*/
- shmptr_ptr = (SHMPTR *)item_ptr;
- *shmptr_ptr = next_shmptr;
-
- item_ptr+=block_size;
- }
- /* Last SHMPTR in the list must point to NULL */
- item_ptr-=block_size;
- shmptr_ptr = (SHMPTR *)item_ptr;
- *shmptr_ptr = 0;
-
- /* Return SHMPTR of last element in the list */
- next_shmptr -= block_size;
-
- TRACE("New linked pool goes from 0x%08x to 0x%08x\n", first, next_shmptr);
- return next_shmptr;
-}
-
-/*++
-SHMMapUnknownSegments
-
-Map into this process all SHM segments not yet mapped
-
-(no parameters)
-
-Return value :
- TRUE on success, FALSE in case of error
---*/
-static BOOL SHMMapUnknownSegments(void)
-{
- return TRUE;
-}
-
-/*++
-SHMAddSegment
-
-Create a new SHM segment, map it into this process, initialize it, then link it
-to the other SHM segments
-
-(no parameters)
-
-Return value :
- TRUE on success, FALSE in case of error
-
-Notes :
- This function assumes the SHM lock is held.
---*/
-static BOOL SHMAddSegment(void)
-{
- LPVOID segment_base;
- SHM_SEGMENT_HEADER *header;
- SHM_FIRST_HEADER *first_header;
- SHMPTR first_shmptr;
- SHMPTR *shmptr_ptr;
- int sps;
- int used_size;
- int new_size;
- int current_pool_size;
- int used_pool_size;
- int new_pool_size;
- int num_new_items;
-
- /* Map all segments this process doesn't yet know about, so we link the new
- segment at the right place */
- if(!SHMMapUnknownSegments())
- {
- ERROR("SHMMapUnknownSegments failed!\n");
- return FALSE;
- }
-
- /* Avoid overflowing */
- if(shm_numsegments == MAX_SEGMENTS)
- {
- ERROR("Can't map more segments : maximum number (%d) reached!\n",
- MAX_SEGMENTS);
- return FALSE;
- }
-
- TRACE("Creating SHM segment #%d\n", shm_numsegments);
-
- segment_base = mmap(NULL, segment_size, PROT_READ|PROT_WRITE,
- MAP_ANON|MAP_PRIVATE,-1, 0);
-
- if(segment_base == MAP_FAILED)
- {
- ERROR("mmap() failed! error is %d (%s)\n", errno, strerror(errno));
- return FALSE;
- }
-
- shm_segment_bases[shm_numsegments] = segment_base;
-
- /* Save name (well, suffix) of new segment in the header of the old last
- segment, so that other processes know where it is. */
- header = (SHM_SEGMENT_HEADER *)shm_segment_bases[shm_numsegments-1].Load();
-
- /* Indicate that the new segment is the last one */
- header = (SHM_SEGMENT_HEADER *)segment_base;
-
- /* We're now ready to update our memory pools */
-
- first_header = (SHM_FIRST_HEADER *)shm_segment_bases[0].Load();
-
- /* Calculate total amount of used memory (in bytes) */
- used_size = 0;
- for(sps = 0; sps<SPS_LAST;sps++)
- {
- /* Add total size of this pool */
- used_size += first_header->pools[sps].num_items*block_sizes[sps];
-
- /* Remove unused size of this pool */
- used_size -= first_header->pools[sps].free_items*block_sizes[sps];
- }
-
- /* Determine how to divide the new segment between the pools for the
- different block sizes, then update the pool inforamtion accordingly
- Allocation strategy :
- 1) Calculate the proportion of used memory used by each pool
- 2) Allocate this proportion of the new segment to each pool
- */
-
- /* Add the new segment to the total amount of SHM memory */
- new_size = segment_size-roundup(sizeof(SHM_SEGMENT_HEADER), sizeof(INT64));
-
- /* Calculate value of first SHMPTR in the new segment : segment is
- shm_numsegments (not yet incremented); offset is the first byte after
- the segment header */
- first_shmptr = MAKE_SHMPTR(shm_numsegments,roundup(sizeof(SHM_SEGMENT_HEADER), sizeof(INT64)));
-
- TRACE("Updating SHM pool information; Total memory used is %d bytes; "
- "we are adding %d bytes\n", used_size, new_size);
-
- /* We want to allocate at least 1 block of each size (to avoid adding
- special cases everywhere). We remove the required space for these blocks
- from the size used in the calculations, then add 1 to each block count */
- for(sps=0;sps<SPS_LAST;sps++)
- new_size -= block_sizes[sps];
-
- /* Loop through all block sizes */
- for(sps=0; sps<SPS_LAST; sps++)
- {
- TRACE("Now processing block size \"%d\"...\n", block_sizes[sps]);
- /* amount of memory currently reserved for this block size */
- current_pool_size = first_header->pools[sps].num_items*block_sizes[sps];
-
- /* how much of that is actually used? */
- used_pool_size = current_pool_size -
- first_header->pools[sps].free_items*block_sizes[sps];
-
- DBGOUT("%d bytes of %d bytes used (%d%%)\n", used_pool_size,
- current_pool_size, (used_pool_size*100)/current_pool_size);
-
- /* amount of memory we want to add to the pool for this block size :
- amount used by this pool/total amount used * new segment's size */
- new_pool_size = (((LONGLONG)used_pool_size)*new_size)/used_size;
-
- DBGOUT("Allocating %d bytes of %d to %d-byte pool\n",
- new_pool_size, new_size, block_sizes[sps]);
-
- /* determine the number of blocks that can fit in the chosen amount */
- num_new_items = new_pool_size/block_sizes[sps];
-
- /* make sure we allocate at least 1 block of each size */
- num_new_items +=1;
-
- DBGOUT("Adding %d new blocks\n", num_new_items);
-
- /* Save the first and last block of the current block size in the new
- segment; join all blocks in between in a linked list */
- header->first_pool_blocks[sps] = first_shmptr;
- header->last_pool_blocks[sps] = SHMLinkPool(first_shmptr,
- block_sizes[sps],
- num_new_items);
-
- /* Link the last block in the new linked list to the first block of the
- old global linked list. We don't use SHMPTR_TO_PTR because the pool
- data isn't updated yet */
- shmptr_ptr = reinterpret_cast<SHMPTR*>(
- static_cast<LPBYTE>(shm_segment_bases[SHMPTR_SEGMENT(header->last_pool_blocks[sps])].Load()) +
- SHMPTR_OFFSET(header->last_pool_blocks[sps]));
-
- *shmptr_ptr = first_header->pools[sps].first_free;
-
- /* Save the first block of the new linked list as the new beginning of
- the global linked list; the global list now contains all new blocks
- AND all blocks that were already free */
- first_header->pools[sps].first_free = header->first_pool_blocks[sps];
-
- /* Update block counts to include new blocks */
- first_header->pools[sps].free_items+=num_new_items;
- first_header->pools[sps].num_items+=num_new_items;
-
- DBGOUT("There are now %d %d-byte blocks, %d are free\n",
- first_header->pools[sps].num_items, block_sizes[sps],
- first_header->pools[sps].free_items);
-
- /* Update first_shmptr to first byte after the new pool */
- first_shmptr+=num_new_items*block_sizes[sps];
- }
- shm_numsegments++;
-
- return TRUE;
-}
-
-/*++
-SHMStrDup
-
-Duplicates the string in shared memory.
-
-Returns the new address as SHMPTR on success.
-Returns (SHMPTR)NULL on failure.
---*/
-SHMPTR SHMStrDup( LPCSTR string )
-{
- UINT length = 0;
- SHMPTR retVal = 0;
-
- if ( string )
- {
- length = strlen( string );
-
- retVal = SHMalloc( ++length );
-
- if ( retVal != 0 )
- {
- LPVOID ptr = SHMPTR_TO_PTR( retVal );
- _ASSERT_MSG(ptr != NULL, "SHMPTR_TO_PTR returned NULL.\n");
- if (ptr != NULL)
- {
- memcpy( ptr, string, length );
- }
- else
- {
- // This code should never be reached. If a valid pointer
- // is passed to SHMPTR_TO_PTR and NULL is returned, then
- // there's a problem in either the macro, or the underlying
- // call to SHMPtrToPtr. In case the impossible happens,
- // though, free the memory and return NULL rather than
- // returning uninitialized memory.
- SHMfree( retVal );
- retVal = NULL;
- }
- }
- }
- return retVal;
-}
-
-/*++
-SHMWStrDup
-
-Duplicates the wide string in shared memory.
-
-Returns the new address as SHMPTR on success.
-Returns (SHMPTR)NULL on failure.
---*/
-SHMPTR SHMWStrDup( LPCWSTR string )
-{
- UINT length = 0;
- SHMPTR retVal = 0;
-
- if ( string )
- {
- length = ( PAL_wcslen( string ) + 1 ) * sizeof( WCHAR );
-
- retVal = SHMalloc( length );
-
- if ( retVal != 0 )
- {
- LPVOID ptr = SHMPTR_TO_PTR(retVal);
- _ASSERT_MSG(ptr != NULL, "SHMPTR_TO_PTR returned NULL.\n");
- if (ptr != NULL)
- {
- memcpy( ptr, string, length );
- }
- else
- {
- // This code should never be reached. If a valid pointer
- // is passed to SHMPTR_TO_PTR and NULL is returned, then
- // there's a problem in either the macro, or the underlying
- // call to SHMPtrToPtr. In case the impossible happens,
- // though, free the memory and return NULL rather than
- // returning uninitialized memory.
- SHMfree( retVal );
- retVal = NULL;
- }
- }
- }
- return retVal;
-}
-
-
-
-/*++
-SHMFindNamedObjectByName
-
-Searches for an object whose name matches the name and ID passed in.
-
-Returns a SHMPTR to its location in shared memory. If no object
-matches the name, the function returns NULL and sets pbNameExists to FALSE.
-If an object matches the name but is of a different type, the function
-returns NULL and sets pbNameExists to TRUE.
-
---*/
-SHMPTR SHMFindNamedObjectByName( LPCWSTR lpName, SHM_NAMED_OBJECTS_ID oid,
- BOOL *pbNameExists )
-{
- PSHM_NAMED_OBJECTS pNamedObject = NULL;
- SHMPTR shmNamedObject = 0;
- LPWSTR object_name = NULL;
-
- if(oid==SHM_NAMED_LAST)
- {
- ASSERT("Invalid named object type.\n");
- return 0;
- }
-
- if (pbNameExists == NULL)
- {
- ASSERT("pbNameExists must be non-NULL.\n");
- }
-
- SHMLock();
-
- *pbNameExists = FALSE;
- shmNamedObject = SHMGetInfo( SIID_NAMED_OBJECTS );
-
- TRACE( "Entering SHMFindNamedObjectByName looking for %S .\n",
- lpName?lpName:W16_NULLSTRING );
-
- while ( shmNamedObject )
- {
- pNamedObject = (PSHM_NAMED_OBJECTS)SHMPTR_TO_PTR( shmNamedObject );
- if(NULL == pNamedObject)
- {
- ASSERT("Got invalid SHMPTR value; list of named objects is "
- "corrupted.\n");
- break;
- }
-
- if ( pNamedObject->ShmObjectName )
- {
- object_name = (LPWSTR)SHMPTR_TO_PTR( pNamedObject->ShmObjectName );
- }
-
- if ( object_name &&
- PAL_wcscmp( lpName, object_name ) == 0 )
- {
- if(oid == pNamedObject->ObjectType)
- {
- TRACE( "Returning the kernel object %p.\n", pNamedObject );
- }
- else
- {
- shmNamedObject = 0;
- *pbNameExists = TRUE;
- }
- goto Exit;
- }
- shmNamedObject = pNamedObject->ShmNext;
- }
-
- shmNamedObject = 0;
- TRACE( "No matching kernel object was found.\n" );
-
-Exit:
- SHMRelease();
- return shmNamedObject;
-
-}
-
-/*++
-SHMRemoveNamedObject
-
-Removes the specified named object from the list
-
-No return.
-
-note : the caller is reponsible for releasing all associated memory
---*/
-void SHMRemoveNamedObject( SHMPTR shmNamedObject )
-{
- PSHM_NAMED_OBJECTS pshmLast = 0;
- PSHM_NAMED_OBJECTS pshmCurrent = 0;
-
- TRACE( "Entered SHMDeleteNamedObject shmNamedObject = %d\n", shmNamedObject );
- SHMLock();
-
- pshmCurrent =
- (PSHM_NAMED_OBJECTS)SHMPTR_TO_PTR( SHMGetInfo( SIID_NAMED_OBJECTS ) );
- pshmLast = pshmCurrent;
-
- while ( pshmCurrent )
- {
- if ( pshmCurrent->ShmSelf == shmNamedObject )
- {
- TRACE( "Patching the list.\n" );
-
- /* Patch the list, and delete the object. */
- if ( pshmLast->ShmSelf == pshmCurrent->ShmSelf )
- {
- /* Either the first element or no elements left. */
- SHMSetInfo( SIID_NAMED_OBJECTS, pshmCurrent->ShmNext );
- }
- else if ( (PSHM_NAMED_OBJECTS)SHMPTR_TO_PTR( pshmCurrent->ShmNext ) )
- {
- pshmLast->ShmNext = pshmCurrent->ShmNext;
- }
- else
- {
- /* Only one left. */
- pshmLast->ShmNext = 0;
- }
-
- break;
- }
- else
- {
- pshmLast = pshmCurrent;
- pshmCurrent = (PSHM_NAMED_OBJECTS)SHMPTR_TO_PTR( pshmCurrent->ShmNext );
- }
- }
-
- SHMRelease();
- return;
-}
-
-/*++ SHMAddNamedObject
-
-Adds the specified named object to the list.
-
-No return.
---*/
-void SHMAddNamedObject( SHMPTR shmNewNamedObject )
-{
- PSHM_NAMED_OBJECTS pshmNew = 0;
-
- pshmNew = (PSHM_NAMED_OBJECTS)SHMPTR_TO_PTR( shmNewNamedObject );
-
- if ( pshmNew == NULL )
- {
- ASSERT( "pshmNew should not be NULL\n" );
- }
-
- SHMLock();
-
- pshmNew->ShmNext = SHMGetInfo( SIID_NAMED_OBJECTS );
-
- if ( !SHMSetInfo( SIID_NAMED_OBJECTS, shmNewNamedObject ) )
- {
- ASSERT( "Unable to add the mapping object to shared memory.\n" );
- }
-
- SHMRelease();
- return;
-}
+} \ No newline at end of file
diff --git a/src/pal/src/synchmgr/synchcontrollers.cpp b/src/pal/src/synchmgr/synchcontrollers.cpp
index f7df5ea364..68fe429462 100644
--- a/src/pal/src/synchmgr/synchcontrollers.cpp
+++ b/src/pal/src/synchmgr/synchcontrollers.cpp
@@ -268,7 +268,7 @@ namespace CorUnix
PAL_ERROR palErr = NO_ERROR;
WaitingThreadsListNode * pwtlnNewNode = NULL;
- SharedID shridNewNode = NULLSharedID;
+ SharedID shridNewNode = NULL;
ThreadWaitInfo * ptwiWaitInfo;
DWORD * pdwWaitState;
bool fSharedObject = (SharedObject == m_odObjectDomain);
@@ -299,7 +299,7 @@ namespace CorUnix
if (!pwtlnNewNode)
{
- if (fSharedObject && (NULLSharedID != shridNewNode))
+ if (fSharedObject && (NULL != shridNewNode))
{
ASSERT("Bad Shared Memory ptr %p\n", shridNewNode);
palErr = ERROR_INTERNAL_ERROR;
@@ -335,7 +335,7 @@ namespace CorUnix
}
}
- pwtlnNewNode->shridSHRThis = NULLSharedID;
+ pwtlnNewNode->shridSHRThis = NULL;
pwtlnNewNode->ptwiWaitInfo = ptwiWaitInfo;
pwtlnNewNode->dwObjIndex = dwIndex;
pwtlnNewNode->dwProcessId = gPID;
@@ -442,7 +442,7 @@ namespace CorUnix
{
m_psdSynchData->Release(m_pthrOwner);
}
- if ((fSharedObject) && (NULLSharedID != shridNewNode))
+ if ((fSharedObject) && (NULL != shridNewNode))
{
pSynchManager->CacheAddSharedWTListNode(m_pthrOwner, shridNewNode);
}
@@ -781,7 +781,7 @@ namespace CorUnix
CPalSynchronizationManager::GetInstance();
bool fSharedObject = (SharedObject == m_odObjectDomain);
- _ASSERT_MSG((fSharedObject && (NULLSharedID == m_ptrWTLHead.shrid)) ||
+ _ASSERT_MSG((fSharedObject && (NULL == m_ptrWTLHead.shrid)) ||
(!fSharedObject && (NULL == m_ptrWTLHead.ptr)),
"Final Release on CSynchData with threads still in "
"the waiting list\n");
@@ -1082,7 +1082,7 @@ namespace CorUnix
bool fDelegatedSignaling = false;
DWORD * pdwWaitState;
DWORD dwObjIdx;
- SharedID shridItem = NULLSharedID, shridNextItem = NULLSharedID;
+ SharedID shridItem = NULL, shridNextItem = NULL;
WaitingThreadsListNode * pwtlnItem, * pwtlnNextItem;
DWORD dwPid = gPID;
CPalSynchronizationManager * pSynchManager =
@@ -1400,7 +1400,7 @@ namespace CorUnix
bool fSharedObject = (SharedObject == GetObjectDomain());
DWORD * pdwWaitState;
DWORD dwObjIdx;
- SharedID shridItem = NULLSharedID, shridNextItem = NULLSharedID;
+ SharedID shridItem = NULL, shridNextItem = NULL;
WaitingThreadsListNode * pwtlnItem, * pwtlnNextItem;
DWORD dwPid = gPID;
CPalSynchronizationManager * pSynchManager =
@@ -1893,14 +1893,14 @@ namespace CorUnix
VALIDATEOBJECT(pwtlnNewNode);
- pwtlnNewNode->ptrNext.shrid = NULLSharedID;
+ pwtlnNewNode->ptrNext.shrid = NULL;
if (NULL == pwtlnCurrLast)
{
- _ASSERT_MSG(NULLSharedID == m_ptrWTLHead.shrid,
+ _ASSERT_MSG(NULL == m_ptrWTLHead.shrid,
"Corrupted waiting list on shared CSynchData at "
"{shrid=%p, p=%p}\n", m_shridThis, this);
- pwtlnNewNode->ptrPrev.shrid = NULLSharedID;
+ pwtlnNewNode->ptrPrev.shrid = NULL;
m_ptrWTLHead.shrid = shridNewNode;
m_ptrWTLTail.shrid = shridNewNode;
}
diff --git a/src/pal/src/synchmgr/synchmanager.cpp b/src/pal/src/synchmgr/synchmanager.cpp
index 73b5644dbd..a683255a3e 100644
--- a/src/pal/src/synchmgr/synchmanager.cpp
+++ b/src/pal/src/synchmgr/synchmanager.cpp
@@ -949,7 +949,7 @@ namespace CorUnix
if (SharedObject == odObjectDomain)
{
SharedID shridSynchData = m_cacheSHRSynchData.Get(pthrCurrent);
- if (NULLSharedID == shridSynchData)
+ if (NULL == shridSynchData)
{
ERROR("Unable to allocate shared memory\n");
return ERROR_NOT_ENOUGH_MEMORY;
@@ -962,8 +962,8 @@ namespace CorUnix
_ASSERT_MSG(NULL != psdSynchData, "Bad shared memory pointer\n");
// Initialize waiting list pointers
- psdSynchData->SetWTLHeadShrPtr(NULLSharedID);
- psdSynchData->SetWTLTailShrPtr(NULLSharedID);
+ psdSynchData->SetWTLHeadShrPtr(NULL);
+ psdSynchData->SetWTLTailShrPtr(NULL);
// Store shared pointer to this object
psdSynchData->SetSharedThis(shridSynchData);
@@ -984,7 +984,7 @@ namespace CorUnix
psdSynchData->SetWTLTailPtr(NULL);
// Set shared this pointer to NULL
- psdSynchData->SetSharedThis(NULLSharedID);
+ psdSynchData->SetSharedThis(NULL);
*ppvSynchData = static_cast<void *>(psdSynchData);
}
@@ -2019,7 +2019,7 @@ namespace CorUnix
if (SynchWorkerCmdRemoteSignal == swcWorkerCmd ||
SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd)
{
- SharedID shridMarshaledId = NULLSharedID;
+ SharedID shridMarshaledId = NULL;
TRACE("Received %s cmd\n",
(swcWorkerCmd == SynchWorkerCmdRemoteSignal) ?
@@ -2499,7 +2499,7 @@ namespace CorUnix
WaitingThreadsListNode * pWLNode = SharedIDToTypePointer(WaitingThreadsListNode, shridWLNode);
_ASSERT_MSG(gPID != pWLNode->dwProcessId, "WakeUpRemoteThread called on local thread\n");
- _ASSERT_MSG(NULLSharedID != shridWLNode, "NULL shared identifier\n");
+ _ASSERT_MSG(NULL != shridWLNode, "NULL shared identifier\n");
_ASSERT_MSG(NULL != pWLNode, "Bad shared wait list node identifier (%p)\n", (VOID*)shridWLNode);
_ASSERT_MSG(MsgSize <= PIPE_BUF, "Message too long [MsgSize=%d PIPE_BUF=%d]\n", MsgSize, (int)PIPE_BUF);
@@ -2556,7 +2556,7 @@ namespace CorUnix
SharedIDToTypePointer(CSynchData, shridSynchData);
_ASSERT_MSG(gPID != dwTargetProcessId, " called on local thread\n");
- _ASSERT_MSG(NULLSharedID != shridSynchData, "NULL shared identifier\n");
+ _ASSERT_MSG(NULL != shridSynchData, "NULL shared identifier\n");
_ASSERT_MSG(NULL != psdSynchData, "Bad shared SynchData identifier (%p)\n", (VOID*)shridSynchData);
_ASSERT_MSG(MsgSize <= PIPE_BUF, "Message too long [MsgSize=%d PIPE_BUF=%d]\n", MsgSize, (int)PIPE_BUF);
@@ -3737,7 +3737,7 @@ namespace CorUnix
PAL_ERROR palError = NO_ERROR;
CSynchData *psdLocal = reinterpret_cast<CSynchData *>(pvLocalSynchData);
CSynchData *psdShared = NULL;
- SharedID shridSynchData = NULLSharedID;
+ SharedID shridSynchData = NULL;
SharedID *rgshridWTLNodes = NULL;
CObjectType *pot = NULL;
ULONG ulcWaitingThreads;
@@ -3759,7 +3759,7 @@ namespace CorUnix
//
shridSynchData = m_cacheSHRSynchData.Get(pthrCurrent);
- if (NULLSharedID == shridSynchData)
+ if (NULL == shridSynchData)
{
ERROR("Unable to allocate shared memory\n");
palError = ERROR_NOT_ENOUGH_MEMORY;
@@ -3837,8 +3837,8 @@ namespace CorUnix
// for the waiting threads
//
- psdShared->SetWTLHeadShrPtr(NULLSharedID);
- psdShared->SetWTLTailShrPtr(NULLSharedID);
+ psdShared->SetWTLHeadShrPtr(NULL);
+ psdShared->SetWTLTailShrPtr(NULL);
if (0 < ulcWaitingThreads)
{
@@ -4020,7 +4020,7 @@ namespace CorUnix
CThreadSynchronizationInfo::CThreadSynchronizationInfo() :
m_tsThreadState(TS_IDLE),
- m_shridWaitAwakened(NULLSharedID),
+ m_shridWaitAwakened(NULL),
m_lLocalSynchLockCount(0),
m_lSharedSynchLockCount(0),
m_ownedNamedMutexListHead(nullptr)
@@ -4037,9 +4037,9 @@ namespace CorUnix
CThreadSynchronizationInfo::~CThreadSynchronizationInfo()
{
DeleteCriticalSection(&m_ownedNamedMutexListLock);
- if (NULLSharedID != m_shridWaitAwakened)
+ if (NULL != m_shridWaitAwakened)
{
- RawSharedObjectFree(m_shridWaitAwakened);
+ free(m_shridWaitAwakened);
}
}
@@ -4091,9 +4091,8 @@ namespace CorUnix
pthread_condattr_t attrs;
pthread_condattr_t *attrsPtr = nullptr;
- m_shridWaitAwakened = RawSharedObjectAlloc(sizeof(DWORD),
- DefaultSharedPool);
- if (NULLSharedID == m_shridWaitAwakened)
+ m_shridWaitAwakened = malloc(sizeof(DWORD));
+ if (NULL == m_shridWaitAwakened)
{
ERROR("Fail allocating thread wait status shared object\n");
palErr = ERROR_NOT_ENOUGH_MEMORY;
diff --git a/src/pal/src/synchmgr/synchmanager.hpp b/src/pal/src/synchmgr/synchmanager.hpp
index 883d5b8b61..b0cc2e7622 100644
--- a/src/pal/src/synchmgr/synchmanager.hpp
+++ b/src/pal/src/synchmgr/synchmanager.hpp
@@ -172,7 +172,7 @@ namespace CorUnix
public:
CSynchData()
- : m_ulcWaitingThreads(0), m_shridThis(NULLSharedID), m_lRefCount(1),
+ : m_ulcWaitingThreads(0), m_shridThis(NULL), m_lRefCount(1),
m_lSignalCount(0), m_lOwnershipCount(0), m_dwOwnerPid(0),
m_dwOwnerTid(0), m_pOwnerThread(NULL),
m_poolnOwnedObjectListNode(NULL), m_fAbandoned(false)
diff --git a/src/pal/src/synchobj/event.cpp b/src/pal/src/synchobj/event.cpp
index 80725f0bf9..3db9a3a38a 100644
--- a/src/pal/src/synchobj/event.cpp
+++ b/src/pal/src/synchobj/event.cpp
@@ -35,7 +35,10 @@ CObjectType CorUnix::otManualResetEvent(
NULL, // No cleanup routine
NULL, // No initialization routine
0, // No immutable data
+ NULL, // No immutable data copy routine
+ NULL, // No immutable data cleanup routine
0, // No process local data
+ NULL, // No process local data cleanup routine
0, // No shared data
EVENT_ALL_ACCESS, // Currently ignored (no Win32 security)
CObjectType::SecuritySupported,
@@ -53,7 +56,10 @@ CObjectType CorUnix::otAutoResetEvent(
NULL, // No cleanup routine
NULL, // No initialization routine
0, // No immutable data
+ NULL, // No immutable data copy routine
+ NULL, // No immutable data cleanup routine
0, // No process local data
+ NULL, // No process local data cleanup routine
0, // No shared data
EVENT_ALL_ACCESS, // Currently ignored (no Win32 security)
CObjectType::SecuritySupported,
@@ -536,84 +542,4 @@ OpenEventWExit:
PERF_EXIT(OpenEventW);
return hEvent;
-}
-
-/*++
-Function:
- InternalOpenEvent
-
-Note:
- dwDesiredAccess is currently ignored (no Win32 object security support)
- bInheritHandle is currently ignored (handles to events are not inheritable)
-
-Parameters:
- pthr -- thread data for calling thread
- phEvent -- on success, receives the allocated event handle
-
- See MSDN docs on OpenEvent for all other parameters.
---*/
-
-PAL_ERROR
-CorUnix::InternalOpenEvent(
- CPalThread *pthr,
- DWORD dwDesiredAccess,
- BOOL bInheritHandle,
- LPCWSTR lpName,
- HANDLE *phEvent
- )
-{
- PAL_ERROR palError = NO_ERROR;
- IPalObject *pobjEvent = NULL;
- CPalString sObjectName(lpName);
-
- _ASSERTE(NULL != pthr);
- _ASSERTE(NULL != lpName);
- _ASSERTE(NULL != phEvent);
-
- ENTRY("InternalOpenEvent(pthr=%p, dwDesiredAccess=%#x, bInheritHandle=%d, "
- "lpName=%p, phEvent=%p)\n",
- pthr,
- dwDesiredAccess,
- bInheritHandle,
- lpName,
- phEvent
- );
-
- palError = g_pObjectManager->LocateObject(
- pthr,
- &sObjectName,
- &aotEvent,
- &pobjEvent
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalOpenEventExit;
- }
-
- palError = g_pObjectManager->ObtainHandleForObject(
- pthr,
- pobjEvent,
- dwDesiredAccess,
- bInheritHandle,
- NULL,
- phEvent
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalOpenEventExit;
- }
-
-InternalOpenEventExit:
-
- if (NULL != pobjEvent)
- {
- pobjEvent->ReleaseReference(pthr);
- }
-
- LOGEXIT("InternalOpenEvent returns %d\n", palError);
-
- return palError;
-}
-
+} \ No newline at end of file
diff --git a/src/pal/src/synchobj/mutex.cpp b/src/pal/src/synchobj/mutex.cpp
index 36c1935939..ccebd3b261 100644
--- a/src/pal/src/synchobj/mutex.cpp
+++ b/src/pal/src/synchobj/mutex.cpp
@@ -49,7 +49,10 @@ CObjectType CorUnix::otMutex(
NULL, // No cleanup routine
NULL, // No initialization routine
0, // No immutable data
+ NULL, // No immutable data copy routine
+ NULL, // No immutable data cleanup routine
0, // No process local data
+ NULL, // No process local data cleanup routine
0, // No shared data
0, // Should be MUTEX_ALL_ACCESS; currently ignored (no Win32 security)
CObjectType::SecuritySupported,
@@ -69,7 +72,10 @@ CObjectType CorUnix::otNamedMutex(
&SharedMemoryProcessDataHeader::PalObject_Close, // Cleanup routine
NULL, // No initialization routine
sizeof(SharedMemoryProcessDataHeader *), // Immutable data
+ NULL, // No immutable data copy routine
+ NULL, // No immutable data cleanup routine
0, // No process local data
+ NULL, // No process local data cleanup routine
0, // No shared data
0, // Should be MUTEX_ALL_ACCESS; currently ignored (no Win32 security)
CObjectType::SecuritySupported,
diff --git a/src/pal/src/synchobj/semaphore.cpp b/src/pal/src/synchobj/semaphore.cpp
index 8d72e9f931..d11003ff5c 100644
--- a/src/pal/src/synchobj/semaphore.cpp
+++ b/src/pal/src/synchobj/semaphore.cpp
@@ -35,7 +35,10 @@ CObjectType CorUnix::otSemaphore(
NULL, // No cleanup routine
NULL, // No initialization routine
sizeof(SemaphoreImmutableData),
+ NULL, // No immutable data copy routine
+ NULL, // No immutable data cleanup routine
0, // No process local data
+ NULL, // No process local data cleanup routine
0, // No shared data
0, // Should be SEMAPHORE_ALL_ACCESS; currently ignored (no Win32 security)
CObjectType::SecuritySupported,
@@ -593,84 +596,4 @@ OpenSemaphoreW(
PERF_EXIT(OpenSemaphoreW);
return hSemaphore;
-}
-
-/*++
-Function:
- InternalOpenSemaphore
-
-Note:
- dwDesiredAccess is currently ignored (no Win32 object security support)
- bInheritHandle is currently ignored (handles to semaphores are not inheritable)
-
-Parameters:
- pthr -- thread data for calling thread
- phEvent -- on success, receives the allocated semaphore handle
-
- See MSDN docs on OpenSemaphore for all other parameters.
---*/
-
-PAL_ERROR
-CorUnix::InternalOpenSemaphore(
- CPalThread *pthr,
- DWORD dwDesiredAccess,
- BOOL bInheritHandle,
- LPCWSTR lpName,
- HANDLE *phSemaphore
- )
-{
- PAL_ERROR palError = NO_ERROR;
- IPalObject *pobjSemaphore = NULL;
- CPalString sObjectName(lpName);
-
- _ASSERTE(NULL != pthr);
- _ASSERTE(NULL != lpName);
- _ASSERTE(NULL != phSemaphore);
-
- ENTRY("InternalOpenSemaphore(pthr=%p, dwDesiredAccess=%d, bInheritHandle=%d, "
- "lpName=%p, phSemaphore=%p)\n",
- pthr,
- dwDesiredAccess,
- bInheritHandle,
- phSemaphore
- );
-
- palError = g_pObjectManager->LocateObject(
- pthr,
- &sObjectName,
- &aotSempahore,
- &pobjSemaphore
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalOpenSemaphoreExit;
- }
-
- palError = g_pObjectManager->ObtainHandleForObject(
- pthr,
- pobjSemaphore,
- dwDesiredAccess,
- bInheritHandle,
- NULL,
- phSemaphore
- );
-
- if (NO_ERROR != palError)
- {
- goto InternalOpenSemaphoreExit;
- }
-
-InternalOpenSemaphoreExit:
-
- if (NULL != pobjSemaphore)
- {
- pobjSemaphore->ReleaseReference(pthr);
- }
-
- LOGEXIT("InternalOpenSemaphore returns %d\n", palError);
-
- return palError;
-}
-
-
+} \ No newline at end of file
diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp
index ba3ebd9691..7c9014ed49 100644
--- a/src/pal/src/thread/process.cpp
+++ b/src/pal/src/thread/process.cpp
@@ -78,11 +78,14 @@ using namespace CorUnix;
CObjectType CorUnix::otProcess(
otiProcess,
- NULL,
- NULL,
- 0,
+ NULL, // No cleanup routine
+ NULL, // No initialization routine
+ 0, // No immutable data
+ NULL, // No immutable data copy routine
+ NULL, // No immutable data cleanup routine
sizeof(CProcProcessLocalData),
- 0,
+ NULL, // No process local data cleanup routine
+ 0, // No shared data
PROCESS_ALL_ACCESS,
CObjectType::SecuritySupported,
CObjectType::SecurityInfoNotPersisted,
diff --git a/src/pal/src/thread/thread.cpp b/src/pal/src/thread/thread.cpp
index e56761b3d6..c6bbfb81f2 100644
--- a/src/pal/src/thread/thread.cpp
+++ b/src/pal/src/thread/thread.cpp
@@ -130,10 +130,13 @@ CObjectType CorUnix::otThread(
otiThread,
ThreadCleanupRoutine,
ThreadInitializationRoutine,
- 0, //sizeof(CThreadImmutableData),
+ 0, // sizeof(CThreadImmutableData),
+ NULL, // No immutable data copy routine
+ NULL, // No immutable data cleanup routine
sizeof(CThreadProcessLocalData),
- 0, //sizeof(CThreadSharedData),
- 0, // THREAD_ALL_ACCESS,
+ NULL, // No process local data cleanup routine
+ 0, // sizeof(CThreadSharedData),
+ 0, // THREAD_ALL_ACCESS,
CObjectType::SecuritySupported,
CObjectType::SecurityInfoNotPersisted,
CObjectType::UnnamedObject,
@@ -633,15 +636,21 @@ CorUnix::InternalCreateThread(
fAttributesInitialized = TRUE;
+ if (alignedStackSize == 0)
+ {
+ // The thread is to be created with default stack size. Use the default stack size
+ // override that was determined during the PAL initialization.
+ alignedStackSize = g_defaultStackSize;
+ }
+
/* adjust the stack size if necessary */
if (alignedStackSize != 0)
{
#ifdef PTHREAD_STACK_MIN
- const size_t MinStackSize = PTHREAD_STACK_MIN;
+ size_t MinStackSize = ALIGN_UP(PTHREAD_STACK_MIN, GetVirtualPageSize());
#else // !PTHREAD_STACK_MIN
- const size_t MinStackSize = 64 * 1024; // this value is typically accepted by pthread_attr_setstacksize()
+ size_t MinStackSize = 64 * 1024; // this value is typically accepted by pthread_attr_setstacksize()
#endif // PTHREAD_STACK_MIN
- _ASSERTE(IS_ALIGNED(MinStackSize, GetVirtualPageSize()));
if (alignedStackSize < MinStackSize)
{
// Adjust the stack size to a minimum value that is likely to be accepted by pthread_attr_setstacksize(). If this
@@ -846,80 +855,6 @@ ExitThread(
/*++
Function:
- GetExitCodeThread
-
-See MSDN doc.
---*/
-BOOL
-PALAPI
-GetExitCodeThread(
- IN HANDLE hThread,
- IN LPDWORD lpExitCode)
-{
- PAL_ERROR palError = NO_ERROR;
- CPalThread *pthrCurrent = NULL;
- CPalThread *pthrTarget = NULL;
- IPalObject *pobjThread = NULL;
- BOOL fExitCodeSet;
-
- PERF_ENTRY(GetExitCodeThread);
- ENTRY("GetExitCodeThread(hThread = %p, lpExitCode = %p)\n",
- hThread, lpExitCode);
-
- if (NULL == lpExitCode)
- {
- WARN("Got NULL lpExitCode\n");
- palError = ERROR_INVALID_PARAMETER;
- goto done;
- }
-
- pthrCurrent = InternalGetCurrentThread();
- palError = InternalGetThreadDataFromHandle(
- pthrCurrent,
- hThread,
- 0,
- &pthrTarget,
- &pobjThread
- );
-
- pthrTarget->Lock(pthrCurrent);
-
- fExitCodeSet = pthrTarget->GetExitCode(lpExitCode);
- if (!fExitCodeSet)
- {
- if (TS_DONE == pthrTarget->synchronizationInfo.GetThreadState())
- {
-#ifdef FEATURE_PAL_SXS
- // The thread exited without ever calling ExitThread.
- // It must have wandered in.
- *lpExitCode = 0;
-#else // FEATURE_PAL_SXS
- ASSERT("exit code not set but thread is dead\n");
-#endif // FEATURE_PAL_SXS
- }
- else
- {
- *lpExitCode = STILL_ACTIVE;
- }
- }
-
- pthrTarget->Unlock(pthrCurrent);
-
-done:
- if (NULL != pobjThread)
- {
- pobjThread->ReleaseReference(pthrCurrent);
- }
-
- LOGEXIT("GetExitCodeThread returns BOOL %d\n", NO_ERROR == palError);
- PERF_EXIT(GetExitCodeThread);
-
- return NO_ERROR == palError;
-}
-
-
-/*++
-Function:
InternalEndCurrentThread
Does any necessary memory clean up, signals waiting threads, and then forces
diff --git a/src/pal/tests/palsuite/README.txt b/src/pal/tests/palsuite/README.txt
index 577fc543f0..1cc930b9b0 100644
--- a/src/pal/tests/palsuite/README.txt
+++ b/src/pal/tests/palsuite/README.txt
@@ -15,7 +15,6 @@
4. ADDITIONAL NOTES ON TESTING/SPECIFIC TEST CASE ISSUES
C_runtime: _fdopen testing issues
- File_IO: getfiletime/test5
File_IO: getfilesize/test1, setfilepointer/test(5,6,7)
File_IO: gettempfilename(a,w)/test2
File_IO: setfileattributesa/test(1,4), setfileattributesw/test(1,4)
@@ -93,11 +92,6 @@ The modes that will not be tested are as follows:
-File_IO: getfiletime/test5
-
-This test case is NTFS specific.
-
-
File_IO: getfilesize/test1, getfilesizeex/test1 setfilepointer/test(5,6,7)
These tests cases create a large number of temporary files which require
diff --git a/src/pal/tests/palsuite/c_runtime/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/CMakeLists.txt
index 7a6b2cd919..64ac174a97 100644
--- a/src/pal/tests/palsuite/c_runtime/CMakeLists.txt
+++ b/src/pal/tests/palsuite/c_runtime/CMakeLists.txt
@@ -139,7 +139,6 @@ add_subdirectory(wcstol)
add_subdirectory(wcstoul)
add_subdirectory(wprintf)
add_subdirectory(_alloca)
-add_subdirectory(_ecvt)
add_subdirectory(_fdopen)
add_subdirectory(_finite)
add_subdirectory(_finitef)
diff --git a/src/pal/tests/palsuite/c_runtime/_ecvt/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/_ecvt/CMakeLists.txt
deleted file mode 100644
index f6aa0cb2d9..0000000000
--- a/src/pal/tests/palsuite/c_runtime/_ecvt/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-
diff --git a/src/pal/tests/palsuite/c_runtime/_ecvt/test1/CMakeLists.txt b/src/pal/tests/palsuite/c_runtime/_ecvt/test1/CMakeLists.txt
deleted file mode 100644
index 152271cf59..0000000000
--- a/src/pal/tests/palsuite/c_runtime/_ecvt/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- test1.cpp
-)
-
-add_executable(paltest_ecvt_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_ecvt_test1 coreclrpal)
-
-target_link_libraries(paltest_ecvt_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/c_runtime/_ecvt/test1/test1.cpp b/src/pal/tests/palsuite/c_runtime/_ecvt/test1/test1.cpp
deleted file mode 100644
index fbcf11ecfc..0000000000
--- a/src/pal/tests/palsuite/c_runtime/_ecvt/test1/test1.cpp
+++ /dev/null
@@ -1,135 +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.
-
-/*=====================================================================
-**
-** Source: c_runtime/_ecvt/test1/test1.c
-**
-** Purpose: Call the _ecvt function on a number of cases. Check that it
-** handles negatives, positives and double bounds correctly. Also check that
-** the 'digit' specification works.
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-#define INT64_TO_DOUBLE(a) (*(double*)&a)
-
-INT64 NaN = 0x7ff8000000000000;
-INT64 NegativeInfinity = 0xfff0000000000000;
-INT64 NegativeSmall = 0x8000000000000001;
-INT64 PositiveInfinity = 0x7ff0000000000000;
-INT64 PositiveSmall = 0x0000000000000001;
-
-struct testCase
-{
- double value; /* number to be converted */
- int precision; /* number of digits to be stored */
- int decimal; /* (expected) decimal point position for stored
- * number */
- int sign; /* (expected) return value */
- char expResult[256]; /* (expected) character array to be returned
- * NOTE: this necessarily limits precision
- * to a value between 0 and 255 */
- char bsdExpResult[256]; /* (expected) character array to be returned
- * NOTE: this necessarily limits precision
- * to a value between 0 and 255 */
-};
-
-int __cdecl main(int argc, char **argv)
-{
- char *result;
- int testDecimal;
- int testSign;
- int i=0;
-
- struct testCase testCases[] =
- {
- /* odd ball values */
- {INT64_TO_DOUBLE(NaN), 7, 1, 0, "1#QNAN0" },
- /* positive values */
- {0, 0, 0, 0, ""},
- {INT64_TO_DOUBLE(PositiveSmall), 17, -323, 0,
- "49406564584124654"},
- {.00123, 3, -2, 0, "123"},
- {.123, 3, 0, 0, "123"},
- {123, 3, 3, 0, "123"},
- {3.1415926535, 9, 1, 0, "314159265"},
- {3.1415926535, 10, 1, 0, "3141592654"},
- {3.1415926535, 11, 1, 0, "31415926535"},
- {3.1415926535, 12, 1, 0, "314159265350"},
- {184467444073709570000.0, 21, 21, 0, "184467444073709570000",
- "184467444073709568000" },
- {184467444073709570000.0, 22, 21, 0, "1844674440737095700000",
- "1844674440737095680000" },
- {INT64_TO_DOUBLE(PositiveInfinity), 7, 1, 0, "1#INF00" },
- /* negative values */
- {-0, 0, 0, 0, ""},
- {INT64_TO_DOUBLE(NegativeSmall), 17, -323, 1,
- "49406564584124654"},
- {-.00123, 3, -2, 1, "123"},
- {-.123, 3, 0, 1, "123"},
- {-123, 3, 3, 1, "123"},
- {-3.1415926535, 9, 1, 1, "314159265"},
- {-3.1415926535, 10, 1, 1, "3141592654"},
- {-3.1415926535, 11, 1, 1, "31415926535"},
- {-3.1415926535, 12, 1, 1, "314159265350"},
- {-184467444073709570000.0, 21, 21, 1, "184467444073709570000",
- "184467444073709568000" },
- {-184467444073709570000.0, 22, 21, 1, "1844674440737095700000",
- "1844674440737095680000" },
- {INT64_TO_DOUBLE(NegativeInfinity), 7, 1, 1, "1#INF00"}
-
- };
-
- if (0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Loop through each case. Call _ecvt on each test case and check the
- result.
- */
-
- for(i = 0; i < sizeof(testCases) / sizeof(struct testCase); i++)
- {
- result = _ecvt(testCases[i].value,
- testCases[i].precision,
- &testDecimal,
- &testSign);
-
- if (( strcmp(testCases[i].expResult, result) != 0 &&
- strcmp(testCases[i].bsdExpResult, result) != 0 ) ||
-
- ( testCases[i].sign != testSign ) ||
- ( testCases[i].decimal != testDecimal ))
-
- {
- Fail("PALSUITE ERROR: Test %d\n"
- "-----------------------\n"
- "testCases[i].value = '%f'\n"
- "testCases[i].precision = '%d'\n"
- "testCases[i].decimal = '%d'\n"
- "testCases[i].sign = '%d'\n"
- "testCases[i].expResult = '%s'\n"
- "result = '%s'\n"
- "testDecimal = '%d'\n"
- "testSign = '%d'\n\n",
- i,
- testCases[i].value,
- testCases[i].precision,
- testCases[i].decimal,
- testCases[i].sign,
- testCases[i].expResult,
- result,
- testDecimal,
- testSign);
- }
-
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/c_runtime/_ecvt/test1/testinfo.dat b/src/pal/tests/palsuite/c_runtime/_ecvt/test1/testinfo.dat
deleted file mode 100644
index 12e7292ec7..0000000000
--- a/src/pal/tests/palsuite/c_runtime/_ecvt/test1/testinfo.dat
+++ /dev/null
@@ -1,14 +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.
-
-Version = 1.0
-Section = C Runtime
-Function = _ecvt
-Name = Call _ecvt on normal values, negatives, exponents and hex digits.
-TYPE = DEFAULT
-EXE1 = test1
-Description
-= Call the _ecvt function on a number of cases. Check that it
-= handles negatives, positives and double bounds correctly. Also check that
-= the 'digit' specification works.
diff --git a/src/pal/tests/palsuite/c_runtime/wcsstr/test1/test1.cpp b/src/pal/tests/palsuite/c_runtime/wcsstr/test1/test1.cpp
index 8296a74983..16005a9f8e 100644
--- a/src/pal/tests/palsuite/c_runtime/wcsstr/test1/test1.cpp
+++ b/src/pal/tests/palsuite/c_runtime/wcsstr/test1/test1.cpp
@@ -21,6 +21,7 @@ int __cdecl main(int argc, char *argv[])
WCHAR *key1;
WCHAR *key2;
WCHAR key3[] = { 0 };
+ WCHAR *key4;
WCHAR *result;
if (PAL_Initialize(argc, argv))
@@ -31,6 +32,7 @@ int __cdecl main(int argc, char *argv[])
string = convert("foo bar baz bar");
key1 = convert("bar");
key2 = convert("Bar");
+ key4 = convert("arggggh!");
result = wcsstr(string, key1);
if (result != string + 4)
@@ -57,6 +59,14 @@ int __cdecl main(int argc, char *argv[])
convertC(key3), string, result);
}
+ result = wcsstr(string, key4);
+ if (result != nullptr)
+ {
+ Fail("ERROR: Got incorrect result in scanning \"%s\" for \"%s\".\n"
+ "Expected to get pointer to null, got %#p\n", convertC(string),
+ convertC(key4), result);
+ }
+
PAL_Terminate();
return PASS;
}
diff --git a/src/pal/tests/palsuite/file_io/AreFileApisANSI/CMakeLists.txt b/src/pal/tests/palsuite/file_io/AreFileApisANSI/CMakeLists.txt
deleted file mode 100644
index f6aa0cb2d9..0000000000
--- a/src/pal/tests/palsuite/file_io/AreFileApisANSI/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-
diff --git a/src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/AreFileApisANSI.cpp b/src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/AreFileApisANSI.cpp
deleted file mode 100644
index ec61f0cb7d..0000000000
--- a/src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/AreFileApisANSI.cpp
+++ /dev/null
@@ -1,40 +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.
-
-/*=====================================================================
-**
-** Source: AreFileApisANSI.c
-**
-** Purpose: Tests the PAL implementation of the AreFileApisANSI function.
-** The only possible return is TRUE.
-**
-**
-**===================================================================*/
-
-
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char *argv[])
-{
- BOOL bRc = FALSE;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- bRc = AreFileApisANSI();
-
-
- if (bRc == FALSE)
- {
- Fail("AreFileApisANSI: ERROR: Function returned FALSE whereas only TRUE "
- "is acceptable.\n");
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/CMakeLists.txt b/src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/CMakeLists.txt
deleted file mode 100644
index ac76e5dc12..0000000000
--- a/src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- AreFileApisANSI.cpp
-)
-
-add_executable(paltest_arefileapisansi_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_arefileapisansi_test1 coreclrpal)
-
-target_link_libraries(paltest_arefileapisansi_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/testinfo.dat b/src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/testinfo.dat
deleted file mode 100644
index 5e6b422ae4..0000000000
--- a/src/pal/tests/palsuite/file_io/AreFileApisANSI/test1/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = file_io
-Function = AreFileApisANSI
-Name = Positive Test for AreFileApisANSI
-Type = DEFAULT
-EXE1 = arefileapisansi
-Description
-=Ensure the return is TRUE because FALSE is not an option in FreeBSD
-
diff --git a/src/pal/tests/palsuite/file_io/CMakeLists.txt b/src/pal/tests/palsuite/file_io/CMakeLists.txt
index 3d6eff1379..09fb686c1a 100644
--- a/src/pal/tests/palsuite/file_io/CMakeLists.txt
+++ b/src/pal/tests/palsuite/file_io/CMakeLists.txt
@@ -1,6 +1,5 @@
cmake_minimum_required(VERSION 2.8.12.2)
-add_subdirectory(AreFileApisANSI)
add_subdirectory(CompareFileTime)
add_subdirectory(CopyFileA)
add_subdirectory(CopyFileW)
@@ -12,25 +11,20 @@ add_subdirectory(DeleteFileA)
add_subdirectory(DeleteFileW)
add_subdirectory(errorpathnotfound)
add_subdirectory(FILECanonicalizePath)
-add_subdirectory(FileTimeToDosDateTime)
add_subdirectory(FindClose)
add_subdirectory(FindFirstFileA)
add_subdirectory(FindFirstFileW)
add_subdirectory(FindNextFileA)
add_subdirectory(FindNextFileW)
add_subdirectory(FlushFileBuffers)
-add_subdirectory(GetConsoleCP)
add_subdirectory(GetConsoleOutputCP)
add_subdirectory(GetCurrentDirectoryA)
add_subdirectory(GetCurrentDirectoryW)
-add_subdirectory(GetDiskFreeSpaceW)
add_subdirectory(GetFileAttributesA)
add_subdirectory(GetFileAttributesExW)
add_subdirectory(GetFileAttributesW)
add_subdirectory(GetFileSize)
add_subdirectory(GetFileSizeEx)
-add_subdirectory(GetFileTime)
-add_subdirectory(GetFileType)
add_subdirectory(GetFullPathNameA)
add_subdirectory(GetFullPathNameW)
add_subdirectory(GetLongPathNameW)
@@ -41,10 +35,8 @@ add_subdirectory(GetTempFileNameA)
add_subdirectory(GetTempFileNameW)
add_subdirectory(gettemppatha)
add_subdirectory(GetTempPathW)
-add_subdirectory(MoveFileA)
add_subdirectory(MoveFileExA)
add_subdirectory(MoveFileExW)
-add_subdirectory(MoveFileW)
add_subdirectory(ReadFile)
add_subdirectory(RemoveDirectoryA)
add_subdirectory(RemoveDirectoryW)
@@ -56,6 +48,5 @@ add_subdirectory(SetEndOfFile)
add_subdirectory(SetFileAttributesA)
add_subdirectory(SetFileAttributesW)
add_subdirectory(SetFilePointer)
-add_subdirectory(SetFileTime)
add_subdirectory(WriteFile)
diff --git a/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/CMakeLists.txt b/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/CMakeLists.txt
deleted file mode 100644
index f6aa0cb2d9..0000000000
--- a/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-
diff --git a/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/CMakeLists.txt b/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/CMakeLists.txt
deleted file mode 100644
index 9d5c678be3..0000000000
--- a/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- test1.cpp
-)
-
-add_executable(paltest_filetimetodosdatetime_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_filetimetodosdatetime_test1 coreclrpal)
-
-target_link_libraries(paltest_filetimetodosdatetime_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/test1.cpp b/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/test1.cpp
deleted file mode 100644
index 5f2c81ff98..0000000000
--- a/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/test1.cpp
+++ /dev/null
@@ -1,116 +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.
-
-/*=====================================================================
-**
-** Source: test1.c
-**
-** Purpose: Tests that FileTimeToDosDateTime successfully converts values.
-** Makes sure values are rounded up, and the limits of the function
-** pass. Also tests that values outside the valid range fail.
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-typedef struct
-{
- DWORD FileTimeLow;
- DWORD FileTimeHigh;
- WORD FatDate;
- WORD FatTime;
-} testCase;
-
-int __cdecl main(int argc, char **argv)
-{
- FILETIME FileTime;
- WORD ResultDate;
- WORD ResultTime;
- BOOL ret;
- int i;
-
- testCase testCases[] =
- {
- /* Test a normal time */
- {0x9BE00100, 0x1B4A02C, 0x14CF, 0x55AF}, /* 12/15/2000, 10:45:30 AM*/
- /* Test that 12/15/2000, 10:45:29 Gets rounded up */
- {0x9B476A80, 0x1B4A02C, 0x14CF, 0x55AF}, /* 12/15/2000, 10:45:30 AM*/
- /* Test that 12/15/2000, 10:45:31 Gets rounded up */
- {0x9C789780, 0x1B4A02C, 0x14CF, 0x55B0}, /* 12/15/2000, 10:45:32 AM*/
-
- /* Test the upper and lower limits of the function */
- {0xE1D58000, 0x1A8E79F, 0x0021, 0x0000}, /* 1/1/1980, 12:00:00 AM*/
- {0xb9de1300, 0x1e9eede, 0x739f, 0xbf7d}, /* 12/31/2037, 11:59:58 PM*/
-
- /* Tests that should fail */
- {0, 0, 0, 0},
- {0xE0A45300, 0x1A8E79F, 0, 0},
- {0x66D29301, 0x23868B8, 0, 0}
-
- /* All this accomplishes is for the date to overflow.
- Likely the only reason it fails in Windows is bacause the
- resulting date falls outside of the legal range. Under BSD,
- it falls into a legal range. This being that BSD calculates time
- from 1900 to 2037, not 1980 to 2107.
- {0xFFFFFFFF, 0xFFFFFFF, 0, 0}
- */
- };
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- for (i=0; i<sizeof(testCases) / sizeof(testCase); i++)
- {
- ResultDate = 0xFFFF;
- ResultTime = 0xFFFF;
-
- FileTime.dwLowDateTime = testCases[i].FileTimeLow;
- FileTime.dwHighDateTime = testCases[i].FileTimeHigh;
-
-
- ret = FileTimeToDosDateTime(&FileTime, &ResultDate, &ResultTime);
- if (testCases[i].FatDate != 0 || testCases[i].FatTime != 0)
- {
- /* Expected it to pass */
- if (!ret)
- {
- Fail("FileTimeToDosDateTime failed for %X,%X!\n",
- testCases[i].FileTimeLow, testCases[i].FileTimeHigh);
- }
-
- if (ResultDate != testCases[i].FatDate ||
- ResultTime != testCases[i].FatTime)
- {
- Fail("FileTimeToDosDateTime did not convert %X,%X "
- "successfully:\nExpected date to be %hX, time %hx.\n"
- "Got %hX, %hX\n", testCases[i].FileTimeLow,
- testCases[i].FileTimeHigh, testCases[i].FatDate,
- testCases[i].FatTime, ResultDate, ResultTime);
- }
- }
- else
- {
- /* Expected it to fail. */
- if (ret)
- {
- Fail("FileTimeToDosDateTime passed for %X,%X!\n",
- testCases[i].FileTimeLow, testCases[i].FileTimeHigh);
- }
-
- if (ResultDate != 0xFFFF || ResultTime != 0xFFFF)
- {
- Fail("FileTimeToDosDateTime failed, but modified output "
- "parameters: %X %X\n", ResultDate, ResultTime);
- }
- }
-
- }
-
- PAL_Terminate();
- return PASS;
-}
-
diff --git a/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/testinfo.dat b/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/testinfo.dat
deleted file mode 100644
index b5c7e1ae52..0000000000
--- a/src/pal/tests/palsuite/file_io/FileTimeToDosDateTime/test1/testinfo.dat
+++ /dev/null
@@ -1,15 +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.
-
-Version = 1.0
-Section = file_io
-Function = FileTimeToDosDateTime
-Name = Positive Test #1 for FileTimeToDosDateTime
-TYPE = DEFAULT
-EXE1 = test1
-Description
-=Tests that FileTimeToDosDateTime successfully converts values.
-=Makes sure values are rounded up, and the limits of the function
-=pass. Also tests that values outside the valid range fail.
-
diff --git a/src/pal/tests/palsuite/file_io/GetConsoleCP/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetConsoleCP/CMakeLists.txt
deleted file mode 100644
index f6aa0cb2d9..0000000000
--- a/src/pal/tests/palsuite/file_io/GetConsoleCP/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-
diff --git a/src/pal/tests/palsuite/file_io/GetConsoleCP/test1/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetConsoleCP/test1/CMakeLists.txt
deleted file mode 100644
index 7753cadea4..0000000000
--- a/src/pal/tests/palsuite/file_io/GetConsoleCP/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- GetConsoleCP.cpp
-)
-
-add_executable(paltest_getconsolecp_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_getconsolecp_test1 coreclrpal)
-
-target_link_libraries(paltest_getconsolecp_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetConsoleCP/test1/GetConsoleCP.cpp b/src/pal/tests/palsuite/file_io/GetConsoleCP/test1/GetConsoleCP.cpp
deleted file mode 100644
index ba17d6c64d..0000000000
--- a/src/pal/tests/palsuite/file_io/GetConsoleCP/test1/GetConsoleCP.cpp
+++ /dev/null
@@ -1,35 +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.
-
-/*=====================================================================
-**
-** Source: GetConsoleCP.c (test 1)
-**
-** Purpose: Tests the PAL implementation of the GetConsoleCP function.
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char *argv[])
-{
- UINT uiCP = 0;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- uiCP = GetConsoleCP();
- if ((uiCP != CP_ACP) && (uiCP != GetACP()) && (uiCP != 437)) /*437 for MSDOS*/
- {
- Fail("GetConsoleCP: ERROR -> The invalid code page %d was returned.\n",
- uiCP);
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/GetConsoleCP/test1/testinfo.dat b/src/pal/tests/palsuite/file_io/GetConsoleCP/test1/testinfo.dat
deleted file mode 100644
index 608a01b2ef..0000000000
--- a/src/pal/tests/palsuite/file_io/GetConsoleCP/test1/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetConsoleCP
-Name = Positive Test for GetConsoleCP (test 1)
-Type = DEFAULT
-EXE1 = getconsolecp
-Description
-= Test GetConsoleCP. Apparently there are only two possible
-= return values: CP_ACP or 1252
diff --git a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/CMakeLists.txt
deleted file mode 100644
index ef14ea5352..0000000000
--- a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-add_subdirectory(test2)
-
diff --git a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/CMakeLists.txt
deleted file mode 100644
index e0238707e5..0000000000
--- a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- GetDiskFreeSpaceW.cpp
-)
-
-add_executable(paltest_getdiskfreespacew_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_getdiskfreespacew_test1 coreclrpal)
-
-target_link_libraries(paltest_getdiskfreespacew_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/GetDiskFreeSpaceW.cpp b/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/GetDiskFreeSpaceW.cpp
deleted file mode 100644
index c1445f654f..0000000000
--- a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/GetDiskFreeSpaceW.cpp
+++ /dev/null
@@ -1,94 +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.
-
-/*=====================================================================
-**
-** Source: GetDiskFreeSpaceW.c (test 1)
-**
-** Purpose: Tests the PAL implementation of the GetDiskFreeSpaceW function.
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char *argv[])
-{
- DWORD dwSectorsPerCluster; /* sectors per cluster */
- DWORD dwBytesPerSector; /* bytes per sector */
- DWORD dwSectorsPerCluster_02; /* sectors per cluster */
- DWORD dwBytesPerSector_02; /* bytes per sector */
- DWORD dwNumberOfFreeClusters; /* free clusters */
- DWORD dwTotalNumberOfClusters; /* total clusters */
- BOOL bRc = FALSE;
- WCHAR szwRootPath[10] = {'/','\0'};
-
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* test the NULL option which translates to the current drive */
- bRc = GetDiskFreeSpaceW(NULL,
- &dwSectorsPerCluster,
- &dwBytesPerSector,
- &dwNumberOfFreeClusters,
- &dwTotalNumberOfClusters);
- if (bRc != TRUE)
- {
- Fail("GetDiskFreeSpaceW: ERROR -> Failed with error code: %ld\n",
- GetLastError());
- }
- else if (dwSectorsPerCluster == 0)
- {
- Fail("GetDiskFreeSpaceW: ERROR -> dwSectorsPerCluster returned 0\n");
- }
- else if (dwBytesPerSector == 0)
- {
- Fail("GetDiskFreeSpaceW: ERROR -> dwBytesPerSector returned 0\n");
- }
-
- /* test the root directory to the current drive */
- bRc = GetDiskFreeSpaceW(szwRootPath,
- &dwSectorsPerCluster_02,
- &dwBytesPerSector_02,
- &dwNumberOfFreeClusters,
- &dwTotalNumberOfClusters);
- if (bRc != TRUE)
- {
- Fail("GetDiskFreeSpaceW: ERROR -> Failed with error code: %ld\n",
- GetLastError());
- }
- else if (dwSectorsPerCluster == 0)
- {
- Fail("GetDiskFreeSpaceW: ERROR -> dwSectorsPerCluster returned 0\n");
- }
- else if (dwBytesPerSector == 0)
- {
- Fail("GetDiskFreeSpaceW: ERROR -> dwBytesPerSector returned 0\n");
- }
- /*
- ** make sure the values returned for NULL path and root path
- ** are the same
- */
- else if (dwSectorsPerCluster_02 != dwSectorsPerCluster)
- {
- Fail("GetDiskFreeSpaceW: ERROR -> dwSectorsPerCluster for NULL path "
- "(%u) should have been the same as the root path (%u).\n",
- dwSectorsPerCluster,
- dwSectorsPerCluster_02);
- }
- else if (dwBytesPerSector_02 != dwBytesPerSector)
- {
- Fail("GetDiskFreeSpaceW: ERROR -> dwBytesPerSector for NULL path "
- "(%u) should have been the same as the root path (%u).\n",
- dwBytesPerSector,
- dwBytesPerSector_02);
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/testinfo.dat b/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/testinfo.dat
deleted file mode 100644
index 61b0e55fae..0000000000
--- a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test1/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetDiskFreeSpaceW
-Name = Positive Test for GetDiskFreeSpaceW (test 1)
-Type = DEFAULT
-EXE1 = getdiskfreespacew
-Description
-= Test GetDiskFreeSpaceW. lpNumberOfFreeClusters and
-= lpTotalNumberOfClusters are to be ignored
diff --git a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/CMakeLists.txt
deleted file mode 100644
index fb6a08789b..0000000000
--- a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- getdiskfreespacew.cpp
-)
-
-add_executable(paltest_getdiskfreespacew_test2
- ${SOURCES}
-)
-
-add_dependencies(paltest_getdiskfreespacew_test2 coreclrpal)
-
-target_link_libraries(paltest_getdiskfreespacew_test2
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/getdiskfreespacew.cpp b/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/getdiskfreespacew.cpp
deleted file mode 100644
index 83dcb54b51..0000000000
--- a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/getdiskfreespacew.cpp
+++ /dev/null
@@ -1,65 +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.
-
-/*=====================================================================
-**
-** Source: GetDiskFreeSpaceW.c (test 2)
-**
-** Purpose: Tests the PAL implementation of the GetDiskFreeSpaceW
-** function on valid non-root paths.
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char *argv[])
-{
- DWORD dwSectorsPerCluster; /* sectors per cluster */
- DWORD dwBytesPerSector; /* bytes per sector */
- DWORD dwNumberOfFreeClusters; /* free clusters */
- DWORD dwTotalNumberOfClusters; /* total clusters */
- BOOL bRc = FALSE;
- WCHAR szwCurrentPath[MAX_LONGPATH];
-
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* get the current directory so we are sure to have a valid path */
- if (!GetCurrentDirectoryW(MAX_LONGPATH, szwCurrentPath))
- {
- Fail("GetDiskFreeSpaceW: ERROR -> GetCurrentDirectoryW failed with "
- "error code: %u.\n",
- GetLastError());
- }
-
- /* test the current path*/
- bRc = GetDiskFreeSpaceW(szwCurrentPath,
- &dwSectorsPerCluster,
- &dwBytesPerSector,
- &dwNumberOfFreeClusters,
- &dwTotalNumberOfClusters);
- if (bRc != TRUE)
- {
- Fail("GetDiskFreeSpaceW: ERROR -> Failed with error code: %u for "
- "the path \"%S\".\n",
- GetLastError(),
- szwCurrentPath);
- }
- else if (dwSectorsPerCluster == 0)
- {
- Fail("GetDiskFreeSpaceW: ERROR -> dwSectorsPerCluster returned 0\n");
- }
- else if (dwBytesPerSector == 0)
- {
- Fail("GetDiskFreeSpaceW: ERROR -> dwBytesPerSector returned 0\n");
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/testinfo.dat b/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/testinfo.dat
deleted file mode 100644
index 0a687240ea..0000000000
--- a/src/pal/tests/palsuite/file_io/GetDiskFreeSpaceW/test2/testinfo.dat
+++ /dev/null
@@ -1,12 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetDiskFreeSpaceW
-Name = Positive Test for GetDiskFreeSpaceW (test 2)
-Type = DEFAULT
-EXE1 = getdiskfreespacew
-Description
-= Test GetDiskFreeSpaceW with valid non-root paths
diff --git a/src/pal/tests/palsuite/file_io/GetFileAttributesExW/test1/test1.cpp b/src/pal/tests/palsuite/file_io/GetFileAttributesExW/test1/test1.cpp
index 7a622b628c..af279ed1ef 100644
--- a/src/pal/tests/palsuite/file_io/GetFileAttributesExW/test1/test1.cpp
+++ b/src/pal/tests/palsuite/file_io/GetFileAttributesExW/test1/test1.cpp
@@ -9,8 +9,7 @@
** Purpose: Tests the PAL implementation of the GetFileAttributesExW function.
** Call the function on a normal directory and file and a read-only directory
** and file and a hidden file and directory.
-** Ensure that the attributes returned are correct, and the
-** file times and file sizes.
+** Ensure that the returned attributes and file sizes are correct.
**
**
**===================================================================*/
@@ -24,24 +23,6 @@ typedef enum Item
IS_FILE
}ItemType;
-/*
- This is a helper function which takes two FILETIME structures and
- checks to see if they contain the exact same time.
-*/
-int IsEqualFileTime(FILETIME FirstTime, FILETIME SecondTime)
-{
-
- ULONG64 TimeOne, TimeTwo;
-
- TimeOne = ((((ULONG64)FirstTime.dwHighDateTime)<<32) |
- ((ULONG64)FirstTime.dwLowDateTime));
-
- TimeTwo = ((((ULONG64)SecondTime.dwHighDateTime)<<32) |
- ((ULONG64)SecondTime.dwLowDateTime));
-
- return(TimeOne == TimeTwo);
-}
-
/* This function takes a structure and checks that the information
within the structure is correct. The 'Attribs' are the expected
file attributes, 'TheType' is IS_DIR or IS_FILE and the 'Name' is the
@@ -88,38 +69,6 @@ void VerifyInfo(WIN32_FILE_ATTRIBUTE_DATA InfoStruct,
GetLastError());
}
-
-
- /* Get the FileTime of the file in question */
- if(GetFileTime(hFile, &CorrectCreation,
- &CorrectAccess, &CorrectModify) == 0)
- {
- Fail("ERROR: GetFileTime failed to get the filetime of the "
- "file. GetLastError() returned %d.",
- GetLastError());
- }
-
- /* Check that the Creation, Access and Last Modified times are all
- the same in the structure as what GetFileTime just returned.
- */
- if(!IsEqualFileTime(CorrectCreation, InfoStruct.ftCreationTime))
- {
- Fail("ERROR: The creation time of the file "
- "does not match the creation time given from "
- "GetFileTime.\n");
- }
- if(!IsEqualFileTime(CorrectAccess, InfoStruct.ftLastAccessTime))
- {
- Fail("ERROR: The access time of the file "
- "does not match the access time given from "
- "GetFileTime.\n");
- }
- if(!IsEqualFileTime(CorrectModify, InfoStruct.ftLastWriteTime))
- {
- Fail("ERROR: The write time of the file "
- "does not match the last write time given from "
- "GetFileTime.\n");
- }
if(InfoStruct.nFileSizeLow != GetFileSize(hFile,NULL))
{
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileTime/CMakeLists.txt
deleted file mode 100644
index 19ee487a6a..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-add_subdirectory(test2)
-add_subdirectory(test3)
-add_subdirectory(test4)
-add_subdirectory(test5)
-add_subdirectory(test6)
-add_subdirectory(test7)
-
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test1/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileTime/test1/CMakeLists.txt
deleted file mode 100644
index 3c95a5d992..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- GetFileTime.cpp
-)
-
-add_executable(paltest_getfiletime_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_getfiletime_test1 coreclrpal)
-
-target_link_libraries(paltest_getfiletime_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test1/GetFileTime.cpp b/src/pal/tests/palsuite/file_io/GetFileTime/test1/GetFileTime.cpp
deleted file mode 100644
index fb7bcb8513..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test1/GetFileTime.cpp
+++ /dev/null
@@ -1,180 +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.
-
-/*=====================================================================
-**
-** Source: GetFileTime.c
-**
-** Purpose: Tests the PAL implementation of the GetFileTime function.
-** This test checks the time of a file, writes to it, then checks the
-** time again to ensure that write time has increased. It
-** also checks that creation time is the same under WIN32 and has
-** increased under FreeBSD.
-**
-** Depends:
-** CreateFile
-** WriteFile
-** CloseHandle
-**
-**
-**===================================================================*/
-
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char **argv)
-{
-
- FILETIME Creation,LastAccess,LastWrite;
- HANDLE TheFileHandle;
- ULONG64 FirstWrite, SecondWrite, FirstCreationTime, SecondCreationTime;
- DWORD temp;
- BOOL result;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* Open the file to get a HANDLE */
- TheFileHandle =
- CreateFile(
- "the_file", // File Name
- GENERIC_READ|GENERIC_WRITE, // Access Mode
- 0, // Share Mode
- NULL, // SD
- OPEN_ALWAYS, // Howto Create
- FILE_ATTRIBUTE_NORMAL, // File Attributes
- NULL // Template file
- );
-
- if(TheFileHandle == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to open the file. The error number "
- "returned was %d.",GetLastError());
- }
-
-
- /* Get the Last Write, Creation and Access File time of that File */
- if(!GetFileTime(TheFileHandle,&Creation,&LastAccess,&LastWrite))
- {
- Fail("ERROR: GetFileTime returned 0, indicating failure.");
- }
-
- /* Convert the structure to an ULONG64 */
-
- FirstCreationTime = ((((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
- FirstWrite = ((((ULONG64)LastWrite.dwHighDateTime)<<32) |
- ((ULONG64)LastWrite.dwLowDateTime));
-
- /* Sleep for 3 seconds, this will ensure the time changes */
- Sleep(3000);
-
- /* Write to the file -- this should change write access and
- last access
- */
-
- result = WriteFile(TheFileHandle, // File handle
- "something", // String to write
- 9, // Bytes to write
- &temp, // Bytes written
- NULL);
-
- if(result == 0)
- {
- Fail("ERROR: Failed to write to file. The file must be "
- "written to in order to test that the write time is "
- "updated.");
- }
-
- /* Close the File, so the changes are recorded */
- result = CloseHandle(TheFileHandle);
-
- if(result == 0)
- {
- Fail("ERROR: Failed to close the file handle.");
- }
-
-
- /* Reopen the file */
- TheFileHandle =
- CreateFile(
- "the_file", /* file name */
- GENERIC_READ|GENERIC_WRITE, /* access mode */
- 0, /* share mode */
- NULL, /* SD */
- OPEN_ALWAYS, /* how to create */
- FILE_ATTRIBUTE_NORMAL, /* file attributes */
- NULL /* handle to template file */
- );
-
-
- if(TheFileHandle == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to re-open the file. The error number "
- "returned was %d.",GetLastError());
- }
-
-
-
- /* Call GetFileTime again */
- if(!GetFileTime(TheFileHandle,&Creation,&LastAccess,&LastWrite))
- {
- Fail("ERROR: GetFileTime returned 0, indicating failure.");
- }
-
- /* Store the results in a ULONG64 */
-
- SecondCreationTime = ( (((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
- SecondWrite = ( (((ULONG64)LastWrite.dwHighDateTime)<<32) |
- ((ULONG64)LastWrite.dwLowDateTime));
-
-
- /* Now -- to test. We'll ensure that the Second
- LastWrite time is larger than the first. It tells us that
- time is passing, which is good!
- */
-
- if(FirstWrite >= SecondWrite)
- {
- Fail("ERROR: The last-write-file-time after writing did not "
- "increase from the original. The second value should be "
- "larger.");
- }
-
-#if WIN32
- /* Then we can check to make sure that the creation time
- hasn't changed. This should always stay the same.
- */
-
- if(FirstCreationTime != SecondCreationTime)
- {
- Fail("ERROR: The creation time after writing should not "
- "not change from the original. The second value should be "
- "equal.");
- }
-#else
- /* Then we can check to make sure that the creation time
- has changed. Under FreeBSD it changes whenever the file is
- access or written.
- */
-
- if(FirstCreationTime >= SecondCreationTime)
- {
- Fail("ERROR: The creation time after writing should be "
- "greater than the original. The second value should be "
- "larger.");
- }
-
-#endif
-
- PAL_Terminate();
- return PASS;
-}
-
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test1/testinfo.dat b/src/pal/tests/palsuite/file_io/GetFileTime/test1/testinfo.dat
deleted file mode 100644
index 50cd35214d..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test1/testinfo.dat
+++ /dev/null
@@ -1,14 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetFileTime
-Name = Positive Test for GetFileTime
-TYPE = DEFAULT
-EXE1 = getfiletime
-Description
-= Test the GetFileTime function. Open a file and get the time. Then write
-= to that file. This will change the write and under FreeBSD
-= the creation time. Ensure that all of these are increasing.
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test2/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileTime/test2/CMakeLists.txt
deleted file mode 100644
index 329d8c6553..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test2/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- GetFileTime.cpp
-)
-
-add_executable(paltest_getfiletime_test2
- ${SOURCES}
-)
-
-add_dependencies(paltest_getfiletime_test2 coreclrpal)
-
-target_link_libraries(paltest_getfiletime_test2
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test2/GetFileTime.cpp b/src/pal/tests/palsuite/file_io/GetFileTime/test2/GetFileTime.cpp
deleted file mode 100644
index 5b14a1e357..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test2/GetFileTime.cpp
+++ /dev/null
@@ -1,195 +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.
-
-/*=====================================================================
-**
-** Source: GetFileTime.c
-**
-** Purpose: Tests the PAL implementation of the GetFileTime function
-** Test to see that access date either stays the same or increases
-** when a read is performed. Write
-** and creation time should stay unchanged. Note: Under FreeBSD
-** the Creation time should not change with just a read.
-**
-** Depends:
-** FileTimeToDosDateTime
-** CreateFile
-** ReadFile
-** CloseHandle
-**
-**
-**===================================================================*/
-
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char **argv)
-{
-
- FILETIME Creation,LastAccess,LastWrite;
- HANDLE TheFileHandle;
- ULONG64 FirstWrite, SecondWrite,
- FirstCreationTime, SecondCreationTime;
- DWORD temp;
- char ReadBuffer[10];
- BOOL result;
- WORD DosDateOne, DosDateTwo, DosTime;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* Open the file to get a HANDLE */
- TheFileHandle =
- CreateFile(
- "the_file",
- GENERIC_READ,
- 0,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
-
- if(TheFileHandle == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to open the file. The error number "
- "returned was %d.",GetLastError());
- }
-
-
- /* Get the Last Write, Creation and Access File time of that File */
- if(GetFileTime(TheFileHandle,&Creation,&LastAccess,&LastWrite)==0)
- {
- Fail("ERROR: GetFileTime returned 0, indicating failure.");
- }
-
- /* Call FileTimeToDosDateTime so we can aquire just the date
- portion of the Last Access FILETIME.
- */
- if(FileTimeToDosDateTime(&LastAccess, &DosDateOne, &DosTime) == 0)
- {
- Fail("ERROR: FiletimeToDosDateTime failed, returning 0. "
- "GetLastError returned %d.\n",GetLastError());
- }
-
- /* Convert the structure to an ULONG64 */
-
- FirstCreationTime = ( (((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
- FirstWrite = ( (((ULONG64)LastWrite.dwHighDateTime)<<32) |
- ((ULONG64)LastWrite.dwLowDateTime));
-
- /* Sleep for 3 seconds, this will ensure the time changes */
- Sleep(3000);
-
- /* Read from the file -- this should change
- last access, but we'll only check the date portion, because some file
- systems have a resolution of a day.
- */
-
- result = ReadFile(TheFileHandle, // handle to file
- &ReadBuffer, // data buffer
- 2, // number of bytes to read
- &temp, // number of bytes read
- NULL);
-
- if(result == 0)
- {
- Fail("ERROR: Failed to read from the file.");
- }
-
-
- /* Close the File, so the changes are recorded */
- result = CloseHandle(TheFileHandle);
-
- if(result == 0)
- {
- Fail("ERROR: Failed to close the file handle.");
- }
-
-
- /* Reopen the file */
- TheFileHandle =
- CreateFile("the_file", /* file name */
- GENERIC_READ, /* access mode */
- 0, /* share mode */
- NULL, /* SD */
- OPEN_ALWAYS, /* how to create */
- FILE_ATTRIBUTE_NORMAL, /* file attributes */
- NULL /* handle to template file */
- );
-
- if(TheFileHandle == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to re-open the file. The error number "
- "returned was %d.",GetLastError());
- }
-
- /* Call GetFileTime again */
- if(GetFileTime(TheFileHandle,&Creation,&LastAccess,&LastWrite) == 0)
- {
- Fail("ERROR: GetFileTime returned 0, indicating failure.");
- }
-
- /* Get the Date of the LastAccessTime here again. */
- if(FileTimeToDosDateTime(&LastAccess, &DosDateTwo, &DosTime) == 0)
- {
- Fail("ERROR: FileTimeToDosDateTime failed, returning 0. "
- "GetLastError returned %d.\n",GetLastError());
- }
-
-
- /* Store the results in a ULONG64 */
-
- SecondCreationTime = ( (((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
- SecondWrite = ( (((ULONG64)LastWrite.dwHighDateTime)<<32) |
- ((ULONG64)LastWrite.dwLowDateTime));
-
- /* Now -- to test. We'll ensure that the Second
- LastWrite time is the same as the first. This shouldn't
- have changed.
- */
-
- if(FirstWrite != SecondWrite)
- {
- Fail("ERROR: The last-write-file-time after reading "
- "increased from the original. The second value should be "
- "equal.");
- }
-
-
- /*
- For LastAccessTime, just check that the date is greater or equal
- for the second over the first. The time is not conisered on some
- file systems. (such as fat32)
- */
-
- if(DosDateOne > DosDateTwo)
- {
- Fail("ERROR: The last-access-time after reading should have "
- "stayed the same or increased, but it did not.\n");
- }
-
-
- /* Check to ensure CreationTime hasn't changed. This should not
- have changed in either environment.
- */
-
- if(FirstCreationTime != SecondCreationTime)
- {
- Fail("ERROR: The creation time after reading should not "
- "not change from the original. The second value should be "
- "equal.");
- }
-
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test2/testinfo.dat b/src/pal/tests/palsuite/file_io/GetFileTime/test2/testinfo.dat
deleted file mode 100644
index a60dcf45a4..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test2/testinfo.dat
+++ /dev/null
@@ -1,16 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetFileTime
-Name = Positive Test for GetFileTime
-TYPE = DEFAULT
-EXE1 = getfiletime
-Description
-= Tests the PAL implementation of the GetFileTime function
-= Test to see that access date either stays the same or increases
-= when a read is performed. Write
-= and creation time should stay unchanged. Note: Under FreeBSD
-= the Creation time should not change with just a read.
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test3/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileTime/test3/CMakeLists.txt
deleted file mode 100644
index d154a8810b..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test3/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- GetFileTime.cpp
-)
-
-add_executable(paltest_getfiletime_test3
- ${SOURCES}
-)
-
-add_dependencies(paltest_getfiletime_test3 coreclrpal)
-
-target_link_libraries(paltest_getfiletime_test3
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test3/GetFileTime.cpp b/src/pal/tests/palsuite/file_io/GetFileTime/test3/GetFileTime.cpp
deleted file mode 100644
index a3f46c2bf8..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test3/GetFileTime.cpp
+++ /dev/null
@@ -1,142 +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.
-
-/*=====================================================================
-**
-** Source: GetFileTime.c
-**
-** Purpose: Tests the PAL implementation of the GetFileTime function
-** Test to see that creation time is changed when two different files
-** are created.
-**
-** Depends:
-** CreateFile
-** ReadFile
-** CloseHandle
-**
-**
-**===================================================================*/
-
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char **argv)
-{
-
- FILETIME Creation;
- HANDLE TheFileHandle, SecondFileHandle;
- ULONG64 FirstCreationTime, SecondCreationTime;
- BOOL result;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* Open the file to get a HANDLE */
- TheFileHandle =
- CreateFile(
- "the_file",
- GENERIC_READ,
- 0,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
-
- if(TheFileHandle == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to open the file. The error number "
- "returned was %d.",GetLastError());
- }
-
-
- /* Get the Creation time of the File */
- if(GetFileTime(TheFileHandle,&Creation,NULL,NULL)==0)
- {
- Fail("ERROR: GetFileTime returned 0, indicating failure. "
- "Two of the params were NULL in this case, did they "
- "cause the probleM?");
- }
-
- /* Convert the structure to an ULONG64 */
-
- FirstCreationTime = ( (((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
-
- /* Close the File, so the changes are recorded */
- result = CloseHandle(TheFileHandle);
-
- if(result == 0)
- {
- Fail("ERROR: Failed to close the file handle.");
- }
-
-
- /* Sleep for 3 seconds, this will ensure the time changes */
- Sleep(3000);
-
-
-
- /* Open another file */
- SecondFileHandle =
- CreateFile("the_other_file", /* file name */
- GENERIC_READ, /* access mode */
- 0, /* share mode */
- NULL, /* SD */
- CREATE_ALWAYS, /* how to create */
- FILE_ATTRIBUTE_NORMAL, /* file attributes */
- NULL /* handle to template file */
- );
-
- if(SecondFileHandle == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to open the second file. The error number "
- "returned was %d.",GetLastError());
- }
-
-
- /* Call GetFileTime again */
- if(GetFileTime(SecondFileHandle,&Creation,NULL,NULL) == 0)
- {
- Fail("ERROR: GetFileTime returned 0, indicating failure. "
- "Perhaps the NULLs in the function broke it?");
- }
-
- /* Close the File*/
- result = CloseHandle(SecondFileHandle);
-
- if(result == 0)
- {
- Fail("ERROR: Failed to close the file handle.");
- }
-
-
- /* Store the results in a ULONG64 */
-
- SecondCreationTime = ( (((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
-
-
- /* Now -- to test. We ensure that the FirstCreationTime is
- less than the SecondCreationTime
- */
-
-
- if(FirstCreationTime >= SecondCreationTime)
- {
- Fail("ERROR: The creation time of the two files should be "
- "different. The first file should have a creation "
- "time less than the second.");
- }
-
-
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test3/testinfo.dat b/src/pal/tests/palsuite/file_io/GetFileTime/test3/testinfo.dat
deleted file mode 100644
index 6d1eba739d..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test3/testinfo.dat
+++ /dev/null
@@ -1,14 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetFileTime
-Name = Positive Test for GetFileTime
-TYPE = DEFAULT
-EXE1 = getfiletime
-Description
-= Test the GetFileTime function. This test creates two files and compares
-= their creation times. They should be different. It also tries to get the
-= file time of an invalid handle, which should cause the function to tail.
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test4/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileTime/test4/CMakeLists.txt
deleted file mode 100644
index 416db15a06..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test4/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- GetFileTime.cpp
-)
-
-add_executable(paltest_getfiletime_test4
- ${SOURCES}
-)
-
-add_dependencies(paltest_getfiletime_test4 coreclrpal)
-
-target_link_libraries(paltest_getfiletime_test4
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test4/GetFileTime.cpp b/src/pal/tests/palsuite/file_io/GetFileTime/test4/GetFileTime.cpp
deleted file mode 100644
index ffba516e35..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test4/GetFileTime.cpp
+++ /dev/null
@@ -1,98 +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.
-
-/*=====================================================================
-**
-** Source: GetFileTime.c
-**
-** Purpose: Tests the PAL implementation of the GetFileTime function
-** Test to see that passing NULL values to GetFileTime works and that
-** calling the function on a bad HANDLE causes the correct failure.
-**
-** Depends:
-** CreateFile
-** CloseHandle
-**
-**
-**===================================================================*/
-
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char **argv)
-{
-
- FILETIME Creation,LastWrite,LastAccess;
- HANDLE TheFileHandle;
- BOOL result;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* Open the file to get a HANDLE */
- TheFileHandle =
- CreateFile(
- "the_file",
- GENERIC_READ,
- 0,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
-
- if(TheFileHandle == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to open the file. The error number "
- "returned was %d.",GetLastError());
- }
-
- /* Pass all NULLs, this is useless but should still work. */
- if(GetFileTime(TheFileHandle,NULL,NULL,NULL)==0)
- {
- Fail("ERROR: GetFileTime returned 0, indicating failure. "
- "Three of the params were NULL in this case, did they "
- "cause the problem?");
- }
-
-
- /* Get the Creation time of the File */
- if(GetFileTime(TheFileHandle,&Creation,NULL,NULL)==0)
- {
- Fail("ERROR: GetFileTime returned 0, indicating failure. "
- "Two of the params were NULL in this case, did they "
- "cause the probleM?");
- }
-
- /* Get the Creation, LastWrite time of the File */
- if(GetFileTime(TheFileHandle,&Creation,&LastWrite,NULL)==0)
- {
- Fail("ERROR: GetFileTime returned 0, indicating failure. "
- "One of the params were NULL in this case, did it "
- "cause the problem?");
- }
-
-
- /* Close the File, so the changes are recorded */
- result = CloseHandle(TheFileHandle);
-
- if(result == 0)
- {
- Fail("ERROR: Failed to close the file handle.");
- }
-
- /* Call GetFileTime again */
- if(GetFileTime(TheFileHandle,&Creation,&LastWrite,&LastAccess) != 0)
- {
- Fail("ERROR: GetFileTime returned non zero, indicating success. "
- "It was passed an invalid file HANDLE and should have "
- "failed.");
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test4/testinfo.dat b/src/pal/tests/palsuite/file_io/GetFileTime/test4/testinfo.dat
deleted file mode 100644
index af90558cae..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test4/testinfo.dat
+++ /dev/null
@@ -1,14 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetFileTime
-Name = Positive Test for GetFileTime
-TYPE = DEFAULT
-EXE1 = getfiletime
-Description
-= Test the GetFileTime function. This test gets the file time of a given
-= file while passing all the combonations of NULL as parameters. The
-= function should handle these as unneeded times, and still succeed.
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test5/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileTime/test5/CMakeLists.txt
deleted file mode 100644
index 4072a52fbc..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test5/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- getfiletime.cpp
-)
-
-add_executable(paltest_getfiletime_test5
- ${SOURCES}
-)
-
-add_dependencies(paltest_getfiletime_test5 coreclrpal)
-
-target_link_libraries(paltest_getfiletime_test5
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test5/getfiletime.cpp b/src/pal/tests/palsuite/file_io/GetFileTime/test5/getfiletime.cpp
deleted file mode 100644
index d8196d84bc..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test5/getfiletime.cpp
+++ /dev/null
@@ -1,224 +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.
-
-/*=====================================================================
-**
-** Source: GetFileTime.c
-**
-** Purpose: Test the PAL implementation of GetFileTime. This test
-** creates a file and compares create and write times between
-** writes, but before the close, and verifies the results are
-** as expected
-**
-** Depends:
-** CreateFile
-** WriteFile
-** CloseHandle
-**
-**
-**===================================================================*/
-
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char **argv)
-{
- FILETIME Creation;
- FILETIME LastAccess;
- FILETIME LastWrite;
- HANDLE hFile;
- ULONG64 FirstWrite;
- ULONG64 SecondWrite;
- ULONG64 FirstCreationTime;
- ULONG64 SecondCreationTime;
- DWORD temp;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* Open the file to get a HANDLE */
- hFile = CreateFile("test.tmp",
- GENERIC_READ|GENERIC_WRITE,
- 0,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if(hFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to create the file. The error number "
- "returned was %u.\n",
- GetLastError());
- }
-
- /* Write to the file -- this should change write access and
- last access
- */
- if(!WriteFile(hFile, "something", 9, &temp, NULL))
- {
- Trace("ERROR: Failed to write to file. The file must be "
- "written to in order to test that the write time is "
- "updated. GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- FlushFileBuffers(hFile);
-
- /* Get the Last Write, Creation and Access File time of that File */
- if(!GetFileTime(hFile, &Creation, &LastAccess, &LastWrite))
- {
- Trace("ERROR: GetFileTime returned 0, indicating failure."
- " GetLastError returned %u\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Convert the structure to an ULONG64 */
-
- FirstCreationTime = ((((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
- FirstWrite = ((((ULONG64)LastWrite.dwHighDateTime)<<32) |
- ((ULONG64)LastWrite.dwLowDateTime));
-
- /* Sleep for 3 seconds, this will ensure the time changes */
- Sleep(3000);
-
- /* Write to the file again -- this should change write access and
- last access
- */
- if(!WriteFile(hFile, "something", 9, &temp, NULL))
- {
- Trace("ERROR: Failed to write to file. The file must be "
- "written to in order to test that the write time is "
- "updated. GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
-
- FlushFileBuffers(hFile);
-
- /* Call GetFileTime again */
- if(!GetFileTime(hFile,&Creation,&LastAccess,&LastWrite))
- {
- Trace("ERROR: GetFileTime returned 0, indicating failure."
- "GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Store the results in a ULONG64 */
-
- SecondCreationTime = ( (((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
- SecondWrite = ( (((ULONG64)LastWrite.dwHighDateTime)<<32) |
- ((ULONG64)LastWrite.dwLowDateTime));
-
-
- /* Now -- to test. We'll ensure that the Second
- LastWrite time is larger than the first. It tells us that
- time is passing, which is good!
- */
-
- if(FirstWrite >= SecondWrite)
- {
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("ERROR: The last-write-file-time after writing did not "
- "increase from the original. The second value should be "
- "larger.\n");
- }
-
-#if WIN32
- /* Then we can check to make sure that the creation time
- hasn't changed. This should always stay the same.
- */
-
- if(FirstCreationTime != SecondCreationTime)
- {
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("ERROR: The creation time after writing should not "
- "not change from the original. The second value should be "
- "equal.\n");
- }
-#else
- /* Then we can check to make sure that the creation time
- has changed. Under FreeBSD it changes whenever the file is
- access or written.
- */
-
- if(FirstCreationTime >= SecondCreationTime)
- {
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("ERROR: The creation time after writing should be "
- "greater than the original. The second value should be "
- "larger.\n");
- }
-
-#endif
-
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Fail("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
-
- PAL_Terminate();
- return PASS;
-}
-
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test5/testinfo.dat b/src/pal/tests/palsuite/file_io/GetFileTime/test5/testinfo.dat
deleted file mode 100644
index fd4112b1fa..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test5/testinfo.dat
+++ /dev/null
@@ -1,15 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetFileTime
-Name = Positive Test for GetFileTime
-TYPE = DEFAULT
-EXE1 = getfiletime
-Description
-= Test the PAL implementation of GetFileTime. This test
-= creates a file and compares create and write times between
-= writes, but before the close, and verifies the results are
-= as expected
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test6/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileTime/test6/CMakeLists.txt
deleted file mode 100644
index 83d652461b..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test6/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- getfiletime.cpp
-)
-
-add_executable(paltest_getfiletime_test6
- ${SOURCES}
-)
-
-add_dependencies(paltest_getfiletime_test6 coreclrpal)
-
-target_link_libraries(paltest_getfiletime_test6
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test6/getfiletime.cpp b/src/pal/tests/palsuite/file_io/GetFileTime/test6/getfiletime.cpp
deleted file mode 100644
index 3eedddf82d..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test6/getfiletime.cpp
+++ /dev/null
@@ -1,281 +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.
-
-/*=====================================================================
-**
-** Source: GetFileTime.c
-**
-** Purpose: Tests the PAL implementation of the GetFileTime function.
-** Perform two reads from a file without closing until the end
-** of the test and verify that only the access times change.
-** Note: Under Win32, modify time changes as well so we will
-** check that it doesn't go backwards
-**
-** Depends:
-** FileTimeToDosDateTime
-** CreateFile
-** ReadFile
-** WriteFile
-** CloseHandle
-**
-**
-**===================================================================*/
-
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char **argv)
-{
-
- FILETIME Creation;
- FILETIME LastAccess;
- FILETIME LastWrite;
- HANDLE hFile;
- ULONG64 FirstWrite = (ULONG64)0;
- ULONG64 SecondWrite = (ULONG64)0;
- ULONG64 FirstCreationTime = (ULONG64)0;
- ULONG64 SecondCreationTime = (ULONG64)0;
- DWORD temp;
- char ReadBuffer[10];
- WORD DosDateOne;
- WORD DosDateTwo;
- WORD DosTime;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
- memset(&Creation, 0, sizeof(FILETIME));
- memset(&LastAccess, 0, sizeof(FILETIME));
- memset(&LastWrite, 0, sizeof(FILETIME));
-
- /* Create the file to get a HANDLE */
- hFile = CreateFile("test.tmp",
- GENERIC_READ|GENERIC_WRITE,
- 0,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if(hFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to create the file. The error number "
- "returned was %u.\n",
- GetLastError());
- }
-
- /* give us something to read from the file */
- if(!WriteFile(hFile, "something", 9, &temp, NULL))
- {
- Trace("ERROR: Failed to write to file. "
- "GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* let's do a read to set the file times for our test */
- if(!ReadFile(hFile, &ReadBuffer, 2, &temp, NULL))
- {
- Trace("ERROR: Failed to read from the file. "
- "GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Get the Last Write, Creation and Access File time of the file */
- if(GetFileTime(hFile, &Creation, &LastAccess, &LastWrite)==0)
- {
- Trace("ERROR: GetFileTime returned 0, indicating failure."
- " GetLastError returned %u\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Call FileTimeToDosDateTime so we can aquire just the date
- portion of the Last Access FILETIME.
- */
- if(FileTimeToDosDateTime(&LastAccess, &DosDateOne, &DosTime) == 0)
- {
- Trace("ERROR: FiletimeToDosDateTime failed, returning 0. "
- "GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Convert the structure to an ULONG64 */
- FirstCreationTime = ( (((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
- FirstWrite = ( (((ULONG64)LastWrite.dwHighDateTime)<<32) |
- ((ULONG64)LastWrite.dwLowDateTime));
-
- /* Sleep for 3 seconds, this will ensure the time changes */
- Sleep(3000);
-
- /* Read from the file -- this should change
- last access, but we'll only check the date portion, because some file
- systems have a resolution of a day.
- */
- memset(&Creation, 0, sizeof(FILETIME));
- memset(&LastAccess, 0, sizeof(FILETIME));
- memset(&LastWrite, 0, sizeof(FILETIME));
-
- if(!ReadFile(hFile, &ReadBuffer, 2, &temp, NULL))
- {
- Trace("ERROR: Failed to read from the file. "
- "GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
-
- /* Call GetFileTime to get the updated time values*/
- if(GetFileTime(hFile, &Creation, &LastAccess, &LastWrite) == 0)
- {
- Trace("ERROR: GetFileTime returned 0, indicating failure. "
- "GetLastError returned %d.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Get the Date of the LastAccessTime here again. */
- if(FileTimeToDosDateTime(&LastAccess, &DosDateTwo, &DosTime) == 0)
- {
- Trace("ERROR: FileTimeToDosDateTime failed, returning 0. "
- "GetLastError returned %d.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
-
- /* Store the results in a ULONG64 */
- SecondCreationTime = ( (((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
- SecondWrite = ( (((ULONG64)LastWrite.dwHighDateTime)<<32) |
- ((ULONG64)LastWrite.dwLowDateTime));
-
- /* Now -- to test. We'll ensure that the SecondWrite
- time is not less than the FirstWrite time
- */
-
- if(SecondWrite < FirstWrite)
- {
- Trace("ERROR: The write-file-time (%I64d) after the first read "
- "is less than the write-file-time (%I64d) after the second "
- "read.\n",
- FirstWrite,
- LastWrite);
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /*
- For LastAccessTime, just check that the date is greater or equal
- for the second over the first. The time is not conisered on some
- file systems. (such as fat32)
- */
-
- if(DosDateOne > DosDateTwo)
- {
- Trace("ERROR: The last-access-time after reading should have "
- "stayed the same or increased, but it did not.\n");
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
-
- /* Check to ensure CreationTime hasn't changed. This should not
- have changed in either environment.
- */
-
- if(FirstCreationTime != SecondCreationTime)
- {
- Trace("ERROR: The creation time after reading should not "
- "not change from the original. The second value should be "
- "equal.\n");
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Close the File, so the changes are recorded */
- if(!CloseHandle(hFile))
- {
- Fail("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test6/testinfo.dat b/src/pal/tests/palsuite/file_io/GetFileTime/test6/testinfo.dat
deleted file mode 100644
index 844043689c..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test6/testinfo.dat
+++ /dev/null
@@ -1,14 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetFileTime
-Name = Positive Test for GetFileTime
-TYPE = DEFAULT
-EXE1 = getfiletime
-Description
-= Tests the PAL implementation of the GetFileTime function.
-= Perform two reads from a file without closing until the end
-= of the test and verify that only the access times change.
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test7/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileTime/test7/CMakeLists.txt
deleted file mode 100644
index 34a08db686..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test7/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- getfiletime.cpp
-)
-
-add_executable(paltest_getfiletime_test7
- ${SOURCES}
-)
-
-add_dependencies(paltest_getfiletime_test7 coreclrpal)
-
-target_link_libraries(paltest_getfiletime_test7
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test7/getfiletime.cpp b/src/pal/tests/palsuite/file_io/GetFileTime/test7/getfiletime.cpp
deleted file mode 100644
index d33175b8ec..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test7/getfiletime.cpp
+++ /dev/null
@@ -1,279 +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.
-
-/*=====================================================================
-**
-** Source: GetFileTime.c
-**
-** Purpose: Test the PAL implementation of GetFileTime. This test
-** creates a file and compares create and write times after
-** the buffers are flushed, but before the close, and verifies
-** the results are as expected
-**
-** Depends:
-** CreateFile
-** WriteFile
-** FlushFileBuffers
-** CloseHandle
-**
-**
-**===================================================================*/
-
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char **argv)
-{
- FILETIME Creation;
- FILETIME LastAccess;
- FILETIME LastWrite;
- HANDLE hFile;
- ULONG64 FirstWrite;
- ULONG64 SecondWrite;
- ULONG64 FirstAccess;
- ULONG64 SecondAccess;
- ULONG64 FirstCreationTime;
- ULONG64 SecondCreationTime;
- DWORD temp;
- const char* someText = "1234567890123456789012345678901234567890";
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* Open the file to get a HANDLE */
- hFile = CreateFile("test.tmp",
- GENERIC_READ|GENERIC_WRITE,
- 0,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if(hFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to create the file. The error number "
- "returned was %u.\n",
- GetLastError());
- }
-
- /* Write to the file -- this should change write access and
- last access
- */
- if(!WriteFile(hFile, someText, strlen(someText), &temp, NULL))
- {
- Trace("ERROR: Failed to write to file. The file must be "
- "written to in order to test that the write time is "
- "updated. GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Flush the buffers */
- if(!FlushFileBuffers(hFile))
- {
- Trace("ERROR: The FlushFileBuffers function failed. "
- "GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Get the Last Write, Creation and Access File time of that File */
- if(!GetFileTime(hFile, &Creation, &LastAccess, &LastWrite))
- {
- Trace("ERROR: GetFileTime returned 0, indicating failure."
- " GetLastError returned %u\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Convert the structures to an ULONG64 */
- FirstCreationTime = ((((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
- FirstWrite = ((((ULONG64)LastWrite.dwHighDateTime)<<32) |
- ((ULONG64)LastWrite.dwLowDateTime));
-
- FirstAccess = ((((ULONG64)LastAccess.dwHighDateTime)<<32) |
- ((ULONG64)LastAccess.dwLowDateTime));
-
- /* Sleep for 3 seconds, this will ensure the time changes */
- Sleep(3000);
-
- /* Write to the file again so we have something to flush */
- if(!WriteFile(hFile, someText, strlen(someText), &temp, NULL))
- {
- Trace("ERROR: Failed to write to file. The file must be "
- "written to in order to test that the write time is "
- "updated. GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Flush the buffers forcing the access/mod time to change */
- if(!FlushFileBuffers(hFile))
- {
- Trace("ERROR: The FlushFileBuffers function failed. "
- "GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
-
- /* Call GetFileTime again */
- if(!GetFileTime(hFile,&Creation,&LastAccess,&LastWrite))
- {
- Trace("ERROR: GetFileTime returned 0, indicating failure."
- "GetLastError returned %u.\n",
- GetLastError());
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("");
- }
-
- /* Store the results in a ULONG64 */
-
- SecondCreationTime = ( (((ULONG64)Creation.dwHighDateTime)<<32) |
- ((ULONG64)Creation.dwLowDateTime));
-
- SecondWrite = ( (((ULONG64)LastWrite.dwHighDateTime)<<32) |
- ((ULONG64)LastWrite.dwLowDateTime));
-
- SecondAccess = ((((ULONG64)LastAccess.dwHighDateTime)<<32) |
- ((ULONG64)LastAccess.dwLowDateTime));
-
-
- /* Now -- to test. We'll ensure that the Second
- LastWrite and access times are larger than the first.
- It tells us that time is passing, which is good!
- */
-
- if(FirstWrite >= SecondWrite)
- {
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("ERROR: The write-file-time (%I64d) after the first flush "
- "should be less than the write-file-time (%I64d) after the second "
- "flush.\n",
- FirstWrite,
- LastWrite);
-
- }
-
-
- if(SecondAccess < FirstAccess)
- {
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("ERROR: The access-file-time (%I64d) after the first flush "
- "should be less than or equal to the access-file-time (%I64d) "
- "after the second flush.\n",
- FirstAccess,
- LastAccess);
- }
-
-#if WIN32
- /* Then we can check to make sure that the creation time
- hasn't changed. This should always stay the same.
- */
-
- if(FirstCreationTime != SecondCreationTime)
- {
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("ERROR: The creation time after writing should not "
- "not change from the original. The second value should be "
- "equal.\n");
- }
-#else
- /* Then we can check to make sure that the creation time
- has changed. Under FreeBSD it changes whenever the file is
- access or written.
- */
-
- if(FirstCreationTime >= SecondCreationTime)
- {
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Trace("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
- Fail("ERROR: The creation time after writing should be "
- "greater than the original. The second value should be "
- "larger.\n");
- }
-
-#endif
-
- /* Close the File */
- if(!CloseHandle(hFile))
- {
- Fail("ERROR: Failed to close the file handle. "
- "GetLastError returned %u.\n",
- GetLastError());
- }
-
- PAL_Terminate();
- return PASS;
-}
-
diff --git a/src/pal/tests/palsuite/file_io/GetFileTime/test7/testinfo.dat b/src/pal/tests/palsuite/file_io/GetFileTime/test7/testinfo.dat
deleted file mode 100644
index 774f759adc..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileTime/test7/testinfo.dat
+++ /dev/null
@@ -1,15 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetFileTime
-Name = Positive Test for GetFileTime
-TYPE = DEFAULT
-EXE1 = getfiletime
-Description
-= Test the PAL implementation of GetFileTime. This test
-= creates a file and compares create and write times after
-= the buffers are flushed, but before the close, and verifies
-= the results are as expected
diff --git a/src/pal/tests/palsuite/file_io/GetFileType/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileType/CMakeLists.txt
deleted file mode 100644
index 1962ade358..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileType/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-add_subdirectory(test2)
-add_subdirectory(test3)
-
diff --git a/src/pal/tests/palsuite/file_io/GetFileType/test1/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileType/test1/CMakeLists.txt
deleted file mode 100644
index c0acc6e484..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileType/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- GetFileType.cpp
-)
-
-add_executable(paltest_getfiletype_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_getfiletype_test1 coreclrpal)
-
-target_link_libraries(paltest_getfiletype_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetFileType/test1/GetFileType.cpp b/src/pal/tests/palsuite/file_io/GetFileType/test1/GetFileType.cpp
deleted file mode 100644
index 6558c00bdd..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileType/test1/GetFileType.cpp
+++ /dev/null
@@ -1,76 +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.
-
-/*=====================================================================
-**
-** Source: GetFileType.c (test 1)
-**
-** Purpose: Tests the PAL implementation of the GetFileType function.
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-const char* szTextFile = "text.txt";
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE hFile = NULL;
- DWORD dwRc = 0;
-
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
-
- /* test FILE_TYPE_UNKNOWN */
- dwRc = GetFileType(hFile);
- if (dwRc != FILE_TYPE_UNKNOWN)
- {
- Fail("GetFileType: ERROR -> Was expecting a return type of "
- "FILE_TYPE_UNKNOWN but the function returned %ld.\n",
- dwRc);
- }
-
-
- /* create a test file */
- hFile = CreateFile(szTextFile,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if(hFile == INVALID_HANDLE_VALUE)
- {
- Fail("GetFileType: ERROR -> Unable to create file \"%s\".\n",
- szTextFile);
- }
-
- dwRc = GetFileType(hFile);
- if (CloseHandle(hFile) != TRUE)
- {
- Fail("GetFileType: ERROR -> Unable to close file \"%s\".\n",
- szTextFile);
- }
- if (!DeleteFileA(szTextFile))
- {
- Fail("GetFileType: ERROR -> Unable to delete file \"%s\".\n",
- szTextFile);
- }
-
- if (dwRc != FILE_TYPE_DISK)
- {
- Fail("GetFileType: ERROR -> Was expecting a return type of "
- "FILE_TYPE_DISK but the function returned %ld.\n",
- dwRc);
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/GetFileType/test1/testinfo.dat b/src/pal/tests/palsuite/file_io/GetFileType/test1/testinfo.dat
deleted file mode 100644
index f12a81a20b..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileType/test1/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetFileType
-Name = Positive Test for GetFileType (test 1)
-Type = DEFAULT
-EXE1 = getfiletype
-Description
-= Test GetFileType on a NULL handle and a valid handle to a file
-
diff --git a/src/pal/tests/palsuite/file_io/GetFileType/test2/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileType/test2/CMakeLists.txt
deleted file mode 100644
index 1f2ee78f75..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileType/test2/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- getfiletype.cpp
-)
-
-add_executable(paltest_getfiletype_test2
- ${SOURCES}
-)
-
-add_dependencies(paltest_getfiletype_test2 coreclrpal)
-
-target_link_libraries(paltest_getfiletype_test2
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetFileType/test2/getfiletype.cpp b/src/pal/tests/palsuite/file_io/GetFileType/test2/getfiletype.cpp
deleted file mode 100644
index c9d4eb6572..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileType/test2/getfiletype.cpp
+++ /dev/null
@@ -1,95 +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.
-
-/*=====================================================================
-**
-** Source: getfiletype.c
-**
-** Purpose: Test the PAL implementation of GetFileType to ensure it
-** recognizes opened pipes.
-**
-** Depends: CreatePipe
-** CloseHandle
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char **argv)
-{
- HANDLE hReadPipe = NULL;
- HANDLE hWritePipe = NULL;
- BOOL bRetVal = FALSE;
- DWORD dwFileType;
- SECURITY_ATTRIBUTES lpPipeAttributes;
-
- /*Initialize the PAL*/
- if ((PAL_Initialize(argc, argv)) != 0)
- {
- return (FAIL);
- }
-
- /*
- ** create a pipe and make sure GetFileType returns the correct value
- */
-
- /*Setup SECURITY_ATTRIBUTES structure for CreatePipe*/
- lpPipeAttributes.nLength = sizeof(lpPipeAttributes);
- lpPipeAttributes.lpSecurityDescriptor = NULL;
- lpPipeAttributes.bInheritHandle = TRUE;
-
- /*Create a Pipe*/
- bRetVal = CreatePipe(&hReadPipe, /* read handle*/
- &hWritePipe, /* write handle */
- &lpPipeAttributes, /* security attributes*/
- 0); /* pipe size*/
- if (bRetVal == FALSE)
- {
- Fail("ERROR: %u :Unable to create pipe.\n", GetLastError());
- }
-
- // Get the file type
- dwFileType = GetFileType(hReadPipe);
- if (dwFileType != FILE_TYPE_PIPE)
- {
- if (!CloseHandle(hWritePipe))
- {
- Trace("ERROR: %u : Unable to close write pipe handle "
- "hWritePipe=0x%lx\n", GetLastError(), hWritePipe);
- }
- if (!CloseHandle(hReadPipe))
- {
- Trace("ERROR: %u : Unable to close read pipe handle "
- "hReadPipe=0x%lx\n", GetLastError(), hReadPipe);
- }
- Fail("ERROR: GetFileType returned %u for a pipe instead of the "
- "expected FILE_TYPE_PIPE (%u).\n",
- dwFileType,
- FILE_TYPE_PIPE);
- }
-
- /*Close write pipe handle*/
- if (!CloseHandle(hWritePipe))
- {
- if (!CloseHandle(hReadPipe))
- {
- Trace("ERROR: %u : Unable to close read pipe handle "
- "hReadPipe=0x%lx\n", GetLastError(), hReadPipe);
- }
- Fail("ERROR: %u : Unable to close write pipe handle "
- "hWritePipe=0x%lx\n", GetLastError(), hWritePipe);
- }
-
- /*Close Read pipe handle*/
- if (!CloseHandle(hReadPipe))
- {
- Fail("ERROR: %u : Unable to close read pipe handle "
- "hReadPipe=0x%lx\n", GetLastError(), hReadPipe);
- }
-
- PAL_Terminate();
- return (PASS);
-}
diff --git a/src/pal/tests/palsuite/file_io/GetFileType/test2/testinfo.dat b/src/pal/tests/palsuite/file_io/GetFileType/test2/testinfo.dat
deleted file mode 100644
index eb1361d3f6..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileType/test2/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetFileType
-Name = Test for GetFileType
-TYPE = DEFAULT
-EXE1 = getfiletype
-Description
-= Test the PAL implementation of GetFileType to ensure it
-= recognizes opened pipes.
diff --git a/src/pal/tests/palsuite/file_io/GetFileType/test3/CMakeLists.txt b/src/pal/tests/palsuite/file_io/GetFileType/test3/CMakeLists.txt
deleted file mode 100644
index ed70e6c96c..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileType/test3/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- getfiletype.cpp
-)
-
-add_executable(paltest_getfiletype_test3
- ${SOURCES}
-)
-
-add_dependencies(paltest_getfiletype_test3 coreclrpal)
-
-target_link_libraries(paltest_getfiletype_test3
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/GetFileType/test3/getfiletype.cpp b/src/pal/tests/palsuite/file_io/GetFileType/test3/getfiletype.cpp
deleted file mode 100644
index 6a95585bab..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileType/test3/getfiletype.cpp
+++ /dev/null
@@ -1,72 +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.
-
-/*=====================================================================
-**
-** Source: getfiletype.c
-**
-** Purpose: Test the PAL implementation of the GetFileType on a handle
-** to a console.
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE hFile;
-#if WIN32
- char *lpFileName = "CONIN$";
-#else
- char *lpFileName = "/dev/null";
-#endif
- DWORD dwFileType;
-
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* get a handle to the console */
- hFile = CreateFile(lpFileName,
- GENERIC_READ,
- FILE_SHARE_READ,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
- if (hFile == INVALID_HANDLE_VALUE)
- {
- Fail("GetFileType: ERROR: CreateFile failed to open %s with "
- "error %u.\n",
- lpFileName,
- GetLastError());
- }
-
- /* Get the file type */
- if ((dwFileType = GetFileType(hFile)) != FILE_TYPE_CHAR)
- {
- if (!CloseHandle(hFile))
- {
- Trace("GetFileType: ERROR: %u : Unable to close the handle "
- "hFile=0x%lx\n", GetLastError(), hFile);
- }
- Fail("GetFileType: ERROR: GetFileType returned %u for a device "
- "instead of the expected FILE_TYPE_CHAR (%u).\n",
- dwFileType,
- FILE_TYPE_CHAR);
- }
-
- if (!CloseHandle(hFile))
- {
- Fail("GetFileType: ERROR: %u : Unable to close the handle "
- "hFile=0x%lx\n", GetLastError(), hFile);
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/GetFileType/test3/testinfo.dat b/src/pal/tests/palsuite/file_io/GetFileType/test3/testinfo.dat
deleted file mode 100644
index 9ffd4c2b15..0000000000
--- a/src/pal/tests/palsuite/file_io/GetFileType/test3/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = file_io
-Function = GetFileType
-Name = Test for GetFileTYpe
-TYPE = DEFAULT
-EXE1 = getfiletype
-Description
-= Test the PAL implementation of the GetFileType on a handle
-= to a console.
diff --git a/src/pal/tests/palsuite/file_io/GetStdHandle/test1/GetStdHandle.cpp b/src/pal/tests/palsuite/file_io/GetStdHandle/test1/GetStdHandle.cpp
index f4fe03195d..47b1eba599 100644
--- a/src/pal/tests/palsuite/file_io/GetStdHandle/test1/GetStdHandle.cpp
+++ b/src/pal/tests/palsuite/file_io/GetStdHandle/test1/GetStdHandle.cpp
@@ -96,15 +96,6 @@ int __cdecl main(int argc, char *argv[])
GetLastError());
}
- /* check if the file type is correct for the handle */
- if((dwFileType = GetFileType(hFile)) != FILE_TYPE_CHAR)
- {
- Fail("GetStdHandle: ERROR -> GetFileType returned %u for "
- "STD_ERROR_HANDLE instead of the expected FILE_TYPE_CHAR (%u).\n",
- dwFileType,
- FILE_TYPE_CHAR);
- }
-
/* check to see if we can CloseHandle works on the STD_ERROR_HANDLE */
if (!CloseHandle(hFile))
{
diff --git a/src/pal/tests/palsuite/file_io/MoveFileA/CMakeLists.txt b/src/pal/tests/palsuite/file_io/MoveFileA/CMakeLists.txt
deleted file mode 100644
index f6aa0cb2d9..0000000000
--- a/src/pal/tests/palsuite/file_io/MoveFileA/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-
diff --git a/src/pal/tests/palsuite/file_io/MoveFileA/test1/CMakeLists.txt b/src/pal/tests/palsuite/file_io/MoveFileA/test1/CMakeLists.txt
deleted file mode 100644
index b8f445b1e7..0000000000
--- a/src/pal/tests/palsuite/file_io/MoveFileA/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- MoveFileA.cpp
-)
-
-add_executable(paltest_movefilea_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_movefilea_test1 coreclrpal)
-
-target_link_libraries(paltest_movefilea_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/MoveFileA/test1/ExpectedResults.txt b/src/pal/tests/palsuite/file_io/MoveFileA/test1/ExpectedResults.txt
deleted file mode 100644
index 43b67af6b2..0000000000
--- a/src/pal/tests/palsuite/file_io/MoveFileA/test1/ExpectedResults.txt
+++ /dev/null
@@ -1 +0,0 @@
-0101000001010000 \ No newline at end of file
diff --git a/src/pal/tests/palsuite/file_io/MoveFileA/test1/MoveFileA.cpp b/src/pal/tests/palsuite/file_io/MoveFileA/test1/MoveFileA.cpp
deleted file mode 100644
index 6d1337af03..0000000000
--- a/src/pal/tests/palsuite/file_io/MoveFileA/test1/MoveFileA.cpp
+++ /dev/null
@@ -1,469 +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.
-
-/*=====================================================================
-**
-** Source: MoveFileA.c
-**
-** Purpose: Tests the PAL implementation of the MoveFileA function.
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-LPSTR lpSource[4] = {"src_existing.txt",
- "src_non-existant.txt",
- "src_dir_existing",
- "src_dir_non-existant"};
-LPSTR lpDestination[4] = {"dst_existing.txt",
- "dst_non-existant.txt",
- "dst_dir_existing",
- "dst_dir_non-existant"};
-
-
-/* Create all the required test files */
-int createExisting(void)
-{
- FILE* tempFile = NULL;
- DWORD dwError;
- BOOL bRc = FALSE;
- char szBuffer[100];
-
- /* create the src_existing file */
- tempFile = fopen(lpSource[0], "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFileA test file: src_existing.txt\n");
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR: couldn't create %s\n", lpSource[0]);
- return FAIL;
- }
-
- /* create the src_dir_existing directory and files */
- bRc = CreateDirectoryA(lpSource[2], NULL);
- if (bRc != TRUE)
- {
- Trace("MoveFileA: ERROR: couldn't create \"%s\" because of "
- "error code %ld\n",
- lpSource[2],
- GetLastError());
- return FAIL;
- }
-
- memset(szBuffer, 0, 100);
- sprintf_s(szBuffer, _countof(szBuffer), "%s/test01.txt", lpSource[2]);
- tempFile = fopen(szBuffer, "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFileA test file: %s\n", szBuffer);
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR[%ld]:MoveFileA couldn't create %s\n", GetLastError(), szBuffer);
- return FAIL;
- }
-
- memset(szBuffer, 0, 100);
- sprintf_s(szBuffer, _countof(szBuffer), "%s/test02.txt", lpSource[2]);
- tempFile = fopen(szBuffer, "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFileA test file: %s\n", szBuffer);
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR[%ld]: couldn't create %s\n", GetLastError(), szBuffer);
- return FAIL;
- }
-
-
- /* create the dst_existing file */
- tempFile = fopen(lpDestination[0], "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFileA test file: dst_existing.txt\n");
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR[%ld]:MoveFileA couldn't create \"%s\"\n", GetLastError(), lpDestination[0]);
- return FAIL;
- }
-
- /* create the dst_dir_existing directory and files */
- bRc = CreateDirectoryA(lpDestination[2], NULL);
- if (bRc != TRUE)
- {
- dwError = GetLastError();
- Trace("Error[%ld]:MoveFileA: couldn't create \"%s\"\n", GetLastError(), lpDestination[2]);
- return FAIL;
- }
-
- tempFile = fopen("dst_dir_existing/test01.txt", "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFileA test file: dst_dir_existing/test01.txt\n");
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR: couldn't create dst_dir_existing/test01.txt\n");
- return FAIL;
- }
- tempFile = fopen("dst_dir_existing/test02.txt", "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFileA test file: dst_dir_existing/test02.txt\n");
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR[%ul]: couldn't create dst_dir_existing/test02.txt\n", GetLastError());
- return FAIL;
- }
-
- return PASS;
-}
-
-
-
-void removeDirectoryHelper(LPSTR dir, int location)
-{
- DWORD dwAtt = GetFileAttributesA(dir);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- if(!RemoveDirectoryA(dir))
- {
- Fail("ERROR: Failed to remove Directory [%s], Error Code [%d], location [%d]\n", dir, GetLastError(), location);
- }
- }
-}
-
-void removeFileHelper(LPSTR pfile, int location)
-{
- FILE *fp;
- fp = fopen( pfile, "r");
-
- if (fp != NULL)
- {
- if(fclose(fp))
- {
- Fail("ERROR: Failed to close the file [%s], Error Code [%d], location [%d]\n", pfile, GetLastError(), location);
- }
-
- if(!DeleteFileA(pfile))
- {
- Fail("ERROR: Failed to delete file [%s], Error Code [%d], location [%d]\n", pfile, GetLastError(), location);
- }
- else
- {
- // Trace("Success: deleted file [%S], Error Code [%d], location [%d]\n", wfile, GetLastError(), location);
- }
- }
-
-}
-
-
-/* remove all created files in preparation for the next test */
-void removeAll(void)
-{
- char szTemp[40];
- DWORD dwAtt;
-
- /* get rid of source dirs and files */
- removeFileHelper(lpSource[0], 1);
- removeFileHelper(lpSource[1], 2);
-
- dwAtt = GetFileAttributesA(lpSource[2]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpSource[2]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpSource[2]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpSource[2], 103);
- }
- else
- {
- removeFileHelper(lpSource[2], 17);
- }
-
-
- dwAtt = GetFileAttributesA(lpSource[3]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpSource[3]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpSource[3]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpSource[3], 103);
- }
- else
- {
- removeFileHelper(lpSource[3], 17);
- }
-
- /* get rid of destination dirs and files */
- dwAtt = GetFileAttributesA(lpDestination[0]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpDestination[0]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpDestination[0]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpDestination[0], 103);
- }
- else
- {
- removeFileHelper(lpDestination[0], 17);
- }
-
- dwAtt = GetFileAttributesA(lpDestination[1]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpDestination[1]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpDestination[1]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpDestination[1], 103);
- }
- else
- {
- removeFileHelper(lpDestination[1], 17);
- }
-
- dwAtt = GetFileAttributesA(lpDestination[2]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpDestination[2]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpDestination[2]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpDestination[2], 103);
- }
- else
- {
- removeFileHelper(lpDestination[2], 17);
- }
-
- dwAtt = GetFileAttributesA(lpDestination[3]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpDestination[3]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpDestination[3]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpDestination[3], 103);
- }
- else
- {
- removeFileHelper(lpDestination[3], 17);
- }
-
-}
-
-
-
-
-
-int __cdecl main(int argc, char *argv[])
-{
- BOOL bRc = TRUE;
- BOOL bSuccess = TRUE;
- char results[40];
- FILE* resultsFile = NULL;
- int nCounter = 0;
- int i, j;
- char tempSource[] = {'t','e','m','p','k','.','t','m','p','\0'};
- char tempDest[] = {'t','e','m','p','2','.','t','m','p','\0'};
- HANDLE hFile;
- DWORD result;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* read in the expected results to compare with actual results */
- memset (results, 0, 20);
- resultsFile = fopen("expectedresults.txt", "r");
- if (resultsFile == NULL)
- {
- Fail("MoveFileA ERROR[%ul]: Unable to open \"expectedresults.txt\"\n", GetLastError());
- }
-
- fgets(results, 20, resultsFile);
- fclose(resultsFile);
-
- /* clean the slate */
- removeAll();
-
- if (createExisting() != 0)
- {
- removeAll();
- }
-
-
- /* lpSource loop */
- for (i = 0; i < 4; i++)
- {
- /* lpDestination loop */
- for (j = 0; j < 4; j++)
- {
- bRc = MoveFileA(lpSource[i], lpDestination[j]);
- if (!(
- ((bRc == TRUE) && (results[nCounter] == '1'))
- ||
- ((bRc == FALSE ) && (results[nCounter] == '0')) )
- )
- {
- Trace("MoveFileA: FAILED: test[%d][%d]: \"%s\" -> \"%s\"\n",
- i, j, lpSource[i], lpDestination[j]);
- bSuccess = FALSE;
- }
-
- /* undo the last move */
- removeAll();
- createExisting();
-
- nCounter++;
- }
- }
-
- removeAll();
- if (bSuccess == FALSE)
- {
- Fail("MoveFileA: Test Failed");
- }
-
- /* create the temp source file */
- hFile = CreateFileA(tempSource, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL, 0);
-
- if( hFile == INVALID_HANDLE_VALUE )
- {
- Fail("Error[%ul]:MoveFileA: CreateFile failed to "
- "create the file correctly.\n", GetLastError());
- }
-
- bRc = CloseHandle(hFile);
- if(!bRc)
- {
- Trace("MoveFileA: CloseHandle failed to close the "
- "handle correctly. ERROR:%u\n",GetLastError());
-
- /* delete the created file */
- bRc = DeleteFileA(tempSource);
- if(!bRc)
- {
- Fail("Error[%ul]:MoveFileA: DeleteFileA failed to delete the"
- "file correctly.\n", GetLastError());
- }
- Fail("");
- }
-
- /* set the file attributes to be readonly */
- bRc = SetFileAttributesA(tempSource, FILE_ATTRIBUTE_READONLY);
- if(!bRc)
- {
- Trace("MoveFileA: SetFileAttributes failed to set file "
- "attributes correctly. GetLastError returned %u\n",GetLastError());
- /* delete the created file */
- bRc = DeleteFileA(tempSource);
- if(!bRc)
- {
- Fail("Error[%ul]:MoveFileA: DeleteFileA failed to delete the"
- "file correctly.\n", GetLastError());
- }
- Fail("");
- }
-
- /* move the file to the new location */
- bRc = MoveFileA(tempSource, tempDest);
- if(!bRc)
- {
- /* delete the created file */
- bRc = DeleteFileA(tempSource);
- if(!bRc)
- {
- Fail("Error[%ul]:MoveFileA: DeleteFileA failed to delete the"
- "file correctly.\n", GetLastError());
- }
-
- Fail("Error[%ul]:MoveFileA(%S, %S): GetFileAttributes "
- "failed to get the file's attributes.\n",
- GetLastError(), tempSource, tempDest);
- }
-
- /* check that the newly moved file has the same file attributes
- as the original */
- result = GetFileAttributesA(tempDest);
- if(result == 0)
- {
- /* delete the created file */
- bRc = DeleteFileA(tempDest);
- if(!bRc)
- {
- Fail("Error[%ul]:MoveFileA: DeleteFileA failed to delete the"
- "file correctly.\n", GetLastError());
- }
-
- Fail("Error[%ul]:MoveFileA: GetFileAttributes failed to get "
- "the file's attributes.\n", GetLastError());
- }
-
- if((result & FILE_ATTRIBUTE_READONLY) != FILE_ATTRIBUTE_READONLY)
- {
- /* delete the newly moved file */
- bRc = DeleteFileA(tempDest);
- if(!bRc)
- {
- Fail("Error[%ul]:MoveFileA: DeleteFileA failed to delete the"
- "file correctly.\n", GetLastError());
- }
-
- Fail("Error[%ul]MoveFileA: GetFileAttributes failed to get "
- "the correct file attributes.\n", GetLastError());
- }
-
- /* set the file attributes back to normal, to be deleted */
- bRc = SetFileAttributesA(tempDest, FILE_ATTRIBUTE_NORMAL);
- if(!bRc)
- {
- /* delete the newly moved file */
- bRc = DeleteFileA(tempDest);
- if(!bRc)
- {
- Fail("Error[%ul]:MoveFileA: DeleteFileA failed to delete the"
- "file correctly.\n", GetLastError());
- }
-
- Fail("Error[%ul]:MoveFileA: SetFileAttributes failed to set "
- "file attributes correctly.\n", GetLastError());
- }
-
- /* delete the newly moved file */
- bRc = DeleteFileA(tempDest);
- if(!bRc)
- {
- Fail("Error[%ul]:MoveFileA: DeleteFileA failed to delete the"
- "file correctly.\n", GetLastError());
- }
-
- PAL_Terminate();
-
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/MoveFileA/test1/testinfo.dat b/src/pal/tests/palsuite/file_io/MoveFileA/test1/testinfo.dat
deleted file mode 100644
index 685072fcd9..0000000000
--- a/src/pal/tests/palsuite/file_io/MoveFileA/test1/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = file_io
-Function = MoveFileA
-Name = Positive Test for MoveFileA
-TYPE = DEFAULT
-EXE1 = movefilea
-Description
-=Performs a number of MoveFileA tests and uses the
-=file ExpectedResults.txt to determine if the test passed/failed
diff --git a/src/pal/tests/palsuite/file_io/MoveFileW/CMakeLists.txt b/src/pal/tests/palsuite/file_io/MoveFileW/CMakeLists.txt
deleted file mode 100644
index f6aa0cb2d9..0000000000
--- a/src/pal/tests/palsuite/file_io/MoveFileW/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-
diff --git a/src/pal/tests/palsuite/file_io/MoveFileW/test1/CMakeLists.txt b/src/pal/tests/palsuite/file_io/MoveFileW/test1/CMakeLists.txt
deleted file mode 100644
index 03b7ab907c..0000000000
--- a/src/pal/tests/palsuite/file_io/MoveFileW/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- MoveFileW.cpp
-)
-
-add_executable(paltest_movefilew_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_movefilew_test1 coreclrpal)
-
-target_link_libraries(paltest_movefilew_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/MoveFileW/test1/ExpectedResults.txt b/src/pal/tests/palsuite/file_io/MoveFileW/test1/ExpectedResults.txt
deleted file mode 100644
index 43b67af6b2..0000000000
--- a/src/pal/tests/palsuite/file_io/MoveFileW/test1/ExpectedResults.txt
+++ /dev/null
@@ -1 +0,0 @@
-0101000001010000 \ No newline at end of file
diff --git a/src/pal/tests/palsuite/file_io/MoveFileW/test1/MoveFileW.cpp b/src/pal/tests/palsuite/file_io/MoveFileW/test1/MoveFileW.cpp
deleted file mode 100644
index 8a7fae5983..0000000000
--- a/src/pal/tests/palsuite/file_io/MoveFileW/test1/MoveFileW.cpp
+++ /dev/null
@@ -1,478 +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.
-
-/*=====================================================================
-**
-** Source: MoveFileW.c
-**
-** Purpose: Tests the PAL implementation of the MoveFileW function.
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-LPSTR lpSource[4] = {"src_existing.txt",
- "src_non-existant.txt",
- "src_dir_existing",
- "src_dir_non-existant"};
-LPSTR lpDestination[4] = {"dst_existing.txt",
- "dst_non-existant.txt",
- "dst_dir_existing",
- "dst_dir_non-existant"};
-
-
-/* Create all the required test files */
-int createExisting(void)
-{
- FILE* tempFile = NULL;
- DWORD dwError;
- BOOL bRc = FALSE;
- WCHAR* wPtr = NULL;
- char szBuffer[100];
-
- /* create the src_existing file */
- tempFile = fopen(lpSource[0], "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFile test file: src_existing.txt\n");
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR: couldn't create %s\n", lpSource[0]);
- return FAIL;
- }
-
- /* create the src_dir_existing directory and files */
- wPtr = convert(lpSource[2]);
- bRc = CreateDirectoryW(wPtr, NULL);
- free(wPtr);
- if (bRc != TRUE)
- {
- Trace("MoveFileW: ERROR: couldn't create \"%s\" because of "
- "error code %ld\n",
- lpSource[2],
- GetLastError());
- return FAIL;
- }
-
- memset(szBuffer, 0, 100);
- sprintf_s(szBuffer, _countof(szBuffer), "%s/test01.txt", lpSource[2]);
- tempFile = fopen(szBuffer, "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFileW test file: %s\n", szBuffer);
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR: couldn't create %s\n", szBuffer);
- return FAIL;
- }
-
- memset(szBuffer, 0, 100);
- sprintf_s(szBuffer, _countof(szBuffer), "%s/test02.txt", lpSource[2]);
- tempFile = fopen(szBuffer, "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFileW test file: %s\n", szBuffer);
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR: couldn't create %s\n", szBuffer);
- return FAIL;
- }
-
-
- /* create the dst_existing file */
- tempFile = fopen(lpDestination[0], "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFileW test file: dst_existing.txt\n");
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR: couldn't create \"%s\"\n", lpDestination[0]);
- return FAIL;
- }
-
- /* create the dst_dir_existing directory and files */
- wPtr = convert(lpDestination[2]);
- bRc = CreateDirectoryW(wPtr, NULL);
- free(wPtr);
- if (bRc != TRUE)
- {
- dwError = GetLastError();
- Trace("MoveFileW: ERROR: couldn't create \"%s\"\n", lpDestination[2]);
- return FAIL;
- }
-
- tempFile = fopen("dst_dir_existing/test01.txt", "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFileW test file: dst_dir_existing/test01.txt\n");
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR: couldn't create dst_dir_existing/test01.txt\n");
- return FAIL;
- }
- tempFile = fopen("dst_dir_existing/test02.txt", "w");
- if (tempFile != NULL)
- {
- fprintf(tempFile, "MoveFileW test file: dst_dir_existing/test02.txt\n");
- fclose(tempFile);
- }
- else
- {
- Trace("ERROR: couldn't create dst_dir_existing/test02.txt\n");
- return FAIL;
- }
-
- return PASS;
-}
-
-void removeDirectoryHelper(LPSTR dir, int location)
-{
- DWORD dwAtt = GetFileAttributesA(dir);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- if(!RemoveDirectoryA(dir))
- {
- Fail("ERROR: Failed to remove Directory [%s], Error Code [%d], location [%d]\n", dir, GetLastError(), location);
- }
- }
-}
-
-void removeFileHelper(LPSTR pfile, int location)
-{
- FILE *fp;
- fp = fopen( pfile, "r");
-
- if (fp != NULL)
- {
- if(fclose(fp))
- {
- Fail("ERROR: Failed to close the file [%s], Error Code [%d], location [%d]\n", pfile, GetLastError(), location);
- }
-
- if(!DeleteFileA(pfile))
- {
- Fail("ERROR: Failed to delete file [%s], Error Code [%d], location [%d]\n", pfile, GetLastError(), location);
- }
- else
- {
- // Trace("Success: deleted file [%S], Error Code [%d], location [%d]\n", wfile, GetLastError(), location);
- }
- }
-
-}
-
-/* remove all created files in preparation for the next test */
-void removeAll(void)
-{
- char szTemp[40];
- DWORD dwAtt;
-
- /* get rid of source dirs and files */
- removeFileHelper(lpSource[0], 1);
- removeFileHelper(lpSource[1], 2);
-
- dwAtt = GetFileAttributesA(lpSource[2]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpSource[2]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpSource[2]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpSource[2], 103);
- }
- else
- {
- removeFileHelper(lpSource[2], 17);
- }
-
-
- dwAtt = GetFileAttributesA(lpSource[3]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpSource[3]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpSource[3]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpSource[3], 103);
- }
- else
- {
- removeFileHelper(lpSource[3], 17);
- }
-
- /* get rid of destination dirs and files */
- dwAtt = GetFileAttributesA(lpDestination[0]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpDestination[0]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpDestination[0]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpDestination[0], 103);
- }
- else
- {
- removeFileHelper(lpDestination[0], 17);
- }
-
- dwAtt = GetFileAttributesA(lpDestination[1]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpDestination[1]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpDestination[1]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpDestination[1], 103);
- }
- else
- {
- removeFileHelper(lpDestination[1], 17);
- }
-
- dwAtt = GetFileAttributesA(lpDestination[2]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpDestination[2]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpDestination[2]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpDestination[2], 103);
- }
- else
- {
- removeFileHelper(lpDestination[2], 17);
- }
-
- dwAtt = GetFileAttributesA(lpDestination[3]);
- if (( dwAtt != INVALID_FILE_ATTRIBUTES ) && ( dwAtt & FILE_ATTRIBUTE_DIRECTORY) )
- {
- sprintf_s(szTemp, _countof(szTemp), "%s/test01.txt", lpDestination[3]);
- removeFileHelper(szTemp, 18);
-
- sprintf_s(szTemp, _countof(szTemp), "%s/test02.txt", lpDestination[3]);
- removeFileHelper(szTemp, 19);
- removeDirectoryHelper(lpDestination[3], 103);
- }
- else
- {
- removeFileHelper(lpDestination[3], 17);
- }
-
-}
-
-
-
-
-
-int __cdecl main(int argc, char *argv[])
-{
- BOOL bRc = TRUE;
- BOOL bSuccess = TRUE;
- char results[40];
- FILE* resultsFile = NULL;
- int nCounter = 0;
- int i, j;
- WCHAR* wSource = NULL;
- WCHAR* wDest = NULL;
- WCHAR tempSource[] = {'t','e','m','p','k','.','t','m','p','\0'};
- WCHAR tempDest[] = {'t','e','m','p','2','.','t','m','p','\0'};
- HANDLE hFile;
- DWORD result;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* read in the expected results to compare with actual results */
- memset (results, 0, 20);
- resultsFile = fopen("expectedresults.txt", "r");
- if (resultsFile == NULL)
- {
- Fail("MoveFileW ERROR: Unable to open \"expectedresults.txt\"\n");
- }
-
- fgets(results, 20, resultsFile);
- fclose(resultsFile);
-
- /* clean the slate */
- removeAll();
-
- if (createExisting() != 0)
- {
- removeAll();
- }
-
-
- /* lpSource loop */
- for (i = 0; i < 4; i++)
- {
- /* lpDestination loop */
- for (j = 0; j < 4; j++)
- {
-
- wSource = convert(lpSource[i]);
- wDest = convert(lpDestination[j]);
- bRc = MoveFileW(wSource, wDest);
- free(wSource);
- free(wDest);
- if (!(
- ((bRc == TRUE) && (results[nCounter] == '1'))
- ||
- ((bRc == FALSE ) && (results[nCounter] == '0')) )
- )
- {
- Trace("MoveFileW: FAILED: test[%d][%d]: \"%s\" -> \"%s\"\n",
- i, j, lpSource[i], lpDestination[j]);
- bSuccess = FALSE;
- }
-
- /* undo the last move */
- removeAll();
- createExisting();
-
- nCounter++;
- }
- }
-
- removeAll();
- if (bSuccess == FALSE)
- {
- Fail("MoveFileW: Test Failed");
- }
-
- /* create the temp source file */
- hFile = CreateFileW(tempSource, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL, 0);
-
- if( hFile == INVALID_HANDLE_VALUE )
- {
- Fail("MoveFileW: CreateFile failed to "
- "create the file correctly.\n");
- }
-
- bRc = CloseHandle(hFile);
- if(!bRc)
- {
- Trace("MoveFileW: CloseHandle failed to close the "
- "handle correctly. ERROR:%u\n",GetLastError());
-
- /* delete the created file */
- bRc = DeleteFileW(tempSource);
- if(!bRc)
- {
- Fail("MoveFileW: DeleteFileW failed to delete the"
- "file correctly.\n");
- }
- Fail("");
- }
-
- /* set the file attributes to be readonly */
- bRc = SetFileAttributesW(tempSource, FILE_ATTRIBUTE_READONLY);
- if(!bRc)
- {
- Trace("MoveFileW: SetFileAttributes failed to set file "
- "attributes correctly. GetLastError returned %u\n",GetLastError());
- /* delete the created file */
- bRc = DeleteFileW(tempSource);
- if(!bRc)
- {
- Fail("MoveFileW: DeleteFileW failed to delete the"
- "file correctly.\n");
- }
- Fail("");
- }
-
- /* move the file to the new location */
- bRc = MoveFileW(tempSource, tempDest);
- if(!bRc)
- {
- /* delete the created file */
- bRc = DeleteFileW(tempSource);
- if(!bRc)
- {
- Fail("MoveFileW: DeleteFileW failed to delete the"
- "file correctly.\n");
- }
-
- Fail("MoveFileW(%S, %S): GetFileAttributes "
- "failed to get the file's attributes.\n",
- tempSource, tempDest);
- }
-
- /* check that the newly moved file has the same file attributes
- as the original */
- result = GetFileAttributesW(tempDest);
- if(result == 0)
- {
- /* delete the created file */
- bRc = DeleteFileW(tempDest);
- if(!bRc)
- {
- Fail("MoveFileW: DeleteFileW failed to delete the"
- "file correctly.\n");
- }
-
- Fail("MoveFileW: GetFileAttributes failed to get "
- "the file's attributes.\n");
- }
-
- if((result & FILE_ATTRIBUTE_READONLY) != FILE_ATTRIBUTE_READONLY)
- {
- /* delete the newly moved file */
- bRc = DeleteFileW(tempDest);
- if(!bRc)
- {
- Fail("MoveFileW: DeleteFileW failed to delete the"
- "file correctly.\n");
- }
-
- Fail("MoveFileW: GetFileAttributes failed to get "
- "the correct file attributes.\n");
- }
-
- /* set the file attributes back to normal, to be deleted */
- bRc = SetFileAttributesW(tempDest, FILE_ATTRIBUTE_NORMAL);
- if(!bRc)
- {
- /* delete the newly moved file */
- bRc = DeleteFileW(tempDest);
- if(!bRc)
- {
- Fail("MoveFileW: DeleteFileW failed to delete the"
- "file correctly.\n");
- }
-
- Fail("MoveFileW: SetFileAttributes failed to set "
- "file attributes correctly.\n");
- }
-
- /* delete the newly moved file */
- bRc = DeleteFileW(tempDest);
- if(!bRc)
- {
- Fail("MoveFileW: DeleteFileW failed to delete the"
- "file correctly.\n");
- }
-
- PAL_Terminate();
-
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/MoveFileW/test1/testinfo.dat b/src/pal/tests/palsuite/file_io/MoveFileW/test1/testinfo.dat
deleted file mode 100644
index 8852a03ca2..0000000000
--- a/src/pal/tests/palsuite/file_io/MoveFileW/test1/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = file_io
-Function = MoveFileW
-Name = Positive Test for MoveFileW
-TYPE = DEFAULT
-EXE1 = movefilew
-Description
-=Performs a number of MoveFileW tests and uses the
-=file ExpectedResults.txt to determine if the test passed/failed
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/CMakeLists.txt b/src/pal/tests/palsuite/file_io/SetFileTime/CMakeLists.txt
deleted file mode 100644
index a3847f8ca9..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-add_subdirectory(test2)
-add_subdirectory(test3)
-add_subdirectory(test4)
-
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test1/CMakeLists.txt b/src/pal/tests/palsuite/file_io/SetFileTime/test1/CMakeLists.txt
deleted file mode 100644
index e9d8dce966..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- SetFileTime.cpp
-)
-
-add_executable(paltest_setfiletime_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_setfiletime_test1 coreclrpal)
-
-target_link_libraries(paltest_setfiletime_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test1/SetFileTime.cpp b/src/pal/tests/palsuite/file_io/SetFileTime/test1/SetFileTime.cpp
deleted file mode 100644
index 4711aeba89..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test1/SetFileTime.cpp
+++ /dev/null
@@ -1,129 +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.
-
-/*=====================================================================
-**
-** Source: SetFileTime.c
-**
-** Purpose: Tests the PAL implementation of the SetFileTime function.
-** This test first sets a valid file time on the file which is opened.
-** Then it calls GetFileTime, and compares the values. They should
-** be the same. Note: Access time isn't checked in this test. It will
-** be dealt with seperatly due to odd behaviour.
-**
-** Depends:
-** CreateFile
-** GetFileTime
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-int __cdecl main(int argc, char **argv)
-{
-
-#if WIN32
- FILETIME Creation;
- FILETIME SetCreation;
-#endif
- FILETIME LastWrite;
- FILETIME SetLastWrite;
- HANDLE TheFileHandle;
- BOOL result;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* Populate some FILETIME structures with values
- These values are valid Creation, Access and Write times
- which I generated, and should work properly.
- */
-#if WIN32
- SetCreation.dwLowDateTime = 458108416;
- SetCreation.dwHighDateTime = 29436904;
-#endif
-
- SetLastWrite.dwLowDateTime = -1995099136;
- SetLastWrite.dwHighDateTime = 29436915;
-
-
- /* Open the file to get a HANDLE */
- TheFileHandle = CreateFile("the_file",
- GENERIC_READ|GENERIC_WRITE,
- 0,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if(TheFileHandle == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to open the file. The error number "
- "returned was %d.\n",GetLastError());
- }
-
- /* Set the new file time */
-#if WIN32
- result = SetFileTime(TheFileHandle,
- &SetCreation, NULL, &SetLastWrite);
-#else
- result = SetFileTime(TheFileHandle,
- NULL, NULL, &SetLastWrite);
-#endif
- if(result == 0)
- {
- Fail("ERROR: SetFileTime failed when trying to set the "
- "new file time. The GetLastError was %d.\n",GetLastError());
- }
-
-
- /* Then get the file time of the file */
-#if WIN32
- result = GetFileTime(TheFileHandle, &Creation, NULL, &LastWrite);
-#else
- result = GetFileTime(TheFileHandle, NULL, NULL, &LastWrite);
-#endif
-
- if(result == 0)
- {
- Fail("ERROR: GetFileTime failed, and this tests depends "
- "upon it working properly, in order to ensure that the "
- "file time was set with SetFileTime. GetLastError() "
- "returned %d.\n",GetLastError());
- }
-
- /* Compare the write time we Set to the write time aquired with
- Get. They should be the same.
- */
-
- if(LastWrite.dwLowDateTime != SetLastWrite.dwLowDateTime ||
- LastWrite.dwHighDateTime != SetLastWrite.dwHighDateTime)
- {
- Fail("ERROR: After setting the write time, it is not "
- "equal to what it was set to. Either Set of GetFileTime are "
- "broken.\n");
- }
-
- /* Within FreeBSD, the Creation time is ignored when SetFileTime
- is called. Since FreeBSD has no equivalent. For that reason,
- it is not checked with the following test.
- */
-
-#if WIN32
- if(Creation.dwHighDateTime != SetCreation.dwHighDateTime ||
- Creation.dwLowDateTime != SetCreation.dwLowDateTime)
- {
- Fail("ERROR: After setting the file time, the Creation "
- "time is not what it should be. Either Set or GetFileTime "
- "are broken.");
- }
-#endif
-
- PAL_Terminate();
- return PASS;
-}
-
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test1/testinfo.dat b/src/pal/tests/palsuite/file_io/SetFileTime/test1/testinfo.dat
deleted file mode 100644
index 09dfa0b623..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test1/testinfo.dat
+++ /dev/null
@@ -1,15 +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.
-
-Version = 1.0
-Section = file_io
-Function = SetFileTime
-Name = Positive Test for SetFileTime
-TYPE = DEFAULT
-EXE1 = setfiletime
-Description
-= Test the SetFileTime function.
-= This test calls SetFileTime and sets a file to a given time. It then
-= calls GetFileTime, and compares the values of the two, which should be
-= equal
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test2/CMakeLists.txt b/src/pal/tests/palsuite/file_io/SetFileTime/test2/CMakeLists.txt
deleted file mode 100644
index 12ec26c9f8..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test2/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- SetFileTime.cpp
-)
-
-add_executable(paltest_setfiletime_test2
- ${SOURCES}
-)
-
-add_dependencies(paltest_setfiletime_test2 coreclrpal)
-
-target_link_libraries(paltest_setfiletime_test2
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test2/SetFileTime.cpp b/src/pal/tests/palsuite/file_io/SetFileTime/test2/SetFileTime.cpp
deleted file mode 100644
index e950153bb0..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test2/SetFileTime.cpp
+++ /dev/null
@@ -1,101 +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.
-
-/*=====================================================================
-**
-** Source: SetFileTime.c
-**
-** Purpose: Tests the PAL implementation of the SetFileTime
-** This test first tries to SetFileTime on a HANDLE which doesn't have
-** GENERIC_WRITE set, which should fail.
-**
-**
-** Depends:
-** CreateFile
-** CloseHandle
-**
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-
-
-
-int __cdecl main(int argc, char **argv)
-{
-
- FILETIME SetCreation,SetLastAccess,SetLastWrite;
- HANDLE TheFileHandle;
- BOOL result;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* Populate some FILETIME structures with values
- These values are valid Creation, Access and Write times
- which I generated, and should work properly.
-
- The access times are not seperated into WIN32 and FreeBSD here,
- but it should be fine, as no comparisons are being done in this
- test.
- */
-
- SetCreation.dwLowDateTime = 458108416;
- SetCreation.dwHighDateTime = 29436904;
-
- SetLastAccess.dwLowDateTime = 341368832;
- SetLastAccess.dwHighDateTime = 29436808;
-
- SetLastWrite.dwLowDateTime = -1995099136;
- SetLastWrite.dwHighDateTime = 29436915;
-
-
-/* Open the file to get a HANDLE, without GENERIC WRITE */
-
- TheFileHandle =
- CreateFile(
- "the_file",
- GENERIC_READ,
- 0,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
-
- if(TheFileHandle == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to open the file. The error number "
- "returned was %d.",GetLastError());
- }
-
- /* This SetFileTime should fail, because the HANDLE isn't set with
- GENERIC_WRITE
- */
- result = SetFileTime(TheFileHandle,
- &SetCreation,&SetLastAccess,&SetLastWrite);
-
- if(result != 0)
- {
- Fail("ERROR: SetFileTime should have failed, but returned a "
- "non-zero result. The File HANDLE passed was no set Writable "
- "which should cause failure.");
- }
-
- result = CloseHandle(TheFileHandle);
-
- if(result == 0)
- {
- Fail("ERROR: CloseHandle failed. This test depends upon "
- "it working.");
- }
-
-
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test2/testinfo.dat b/src/pal/tests/palsuite/file_io/SetFileTime/test2/testinfo.dat
deleted file mode 100644
index f1699facaf..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test2/testinfo.dat
+++ /dev/null
@@ -1,15 +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.
-
-Version = 1.0
-Section = file_io
-Function = SetFileTime
-Name = Negative Test for SetFileTime
-TYPE = DEFAULT
-EXE1 = setfiletime
-Description
-= Test the SetFileTime function.
-= This test first tries to SetFileTime on a HANDLE which doesn't have
-= GENERIC_WRITE set, which should fail. Then it attempts to set the file
-= time with bad FILETIME structures, which should also fail
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test3/CMakeLists.txt b/src/pal/tests/palsuite/file_io/SetFileTime/test3/CMakeLists.txt
deleted file mode 100644
index ca0ed659f5..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test3/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- SetFileTime.cpp
-)
-
-add_executable(paltest_setfiletime_test3
- ${SOURCES}
-)
-
-add_dependencies(paltest_setfiletime_test3 coreclrpal)
-
-target_link_libraries(paltest_setfiletime_test3
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test3/SetFileTime.cpp b/src/pal/tests/palsuite/file_io/SetFileTime/test3/SetFileTime.cpp
deleted file mode 100644
index 97f49495d7..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test3/SetFileTime.cpp
+++ /dev/null
@@ -1,66 +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.
-
-/*=====================================================================
-**
-** Source: SetFileTime.c
-**
-** Purpose: Tests the PAL implementation of the SetFileTime function.
-** This test checks to ensure that the function fails when passed an
-** invalid file HANDLE
-**
-**
-**===================================================================*/
-
-
-
-#include <palsuite.h>
-
-
-
-
-int __cdecl main(int argc, char **argv)
-{
-
- FILETIME SetCreation, SetLastWrite, SetLastAccess;
- HANDLE TheFileHandle = NULL;
- BOOL result;
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* Populate some FILETIME structures with values
- These values are valid Creation, Access and Write times
- which I generated, and should work properly.
- */
-
- SetCreation.dwLowDateTime = 458108416;
- SetCreation.dwHighDateTime = 29436904;
-
- SetLastAccess.dwLowDateTime = 341368832;
- SetLastAccess.dwHighDateTime = 29436808;
-
- SetLastWrite.dwLowDateTime = -1995099136;
- SetLastWrite.dwHighDateTime = 29436915;
-
-
- /* Pass this function an invalid file HANDLE and it should
- fail.
- */
-
- result = SetFileTime(TheFileHandle,
- &SetCreation,&SetLastAccess,&SetLastWrite);
-
- if(result != 0)
- {
- Fail("ERROR: Passed an invalid file HANDLE to SetFileTime, but it "
- "returned non-zero. This should return zero for failure.");
- }
-
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test3/testinfo.dat b/src/pal/tests/palsuite/file_io/SetFileTime/test3/testinfo.dat
deleted file mode 100644
index e27280469e..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test3/testinfo.dat
+++ /dev/null
@@ -1,14 +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.
-
-Version = 1.0
-Section = file_io
-Function = SetFileTime
-Name = Negative Test for SetFileTime
-TYPE = DEFAULT
-EXE1 = setfiletime
-Description
-= Test the SetFileTime function.
-= This test checks to ensure that the function fails when passed an
-= invalid file HANDLE
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test4/CMakeLists.txt b/src/pal/tests/palsuite/file_io/SetFileTime/test4/CMakeLists.txt
deleted file mode 100644
index 432ebe594f..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test4/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- SetFileTime.cpp
-)
-
-add_executable(paltest_setfiletime_test4
- ${SOURCES}
-)
-
-add_dependencies(paltest_setfiletime_test4 coreclrpal)
-
-target_link_libraries(paltest_setfiletime_test4
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test4/SetFileTime.cpp b/src/pal/tests/palsuite/file_io/SetFileTime/test4/SetFileTime.cpp
deleted file mode 100644
index 3edd2403c4..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test4/SetFileTime.cpp
+++ /dev/null
@@ -1,108 +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.
-
-/*=====================================================================
-**
-** Source: SetFileTime.c
-**
-** Purpose: Tests the PAL implementation of the SetFileTime function
-** This passes a variety of NULL values as parameters to the function.
-** It should still succeed.
-**
-** Depends:
-** CreateFile
-**
-
-**
-**===================================================================*/
-
-#include <palsuite.h>
-
-
-
-int __cdecl main(int argc, char **argv)
-{
-#if WIN32
- FILETIME Creation;
-#endif
- FILETIME LastWrite,LastAccess;
- HANDLE TheFileHandle;
-
-
- if (0 != PAL_Initialize(argc,argv))
- {
- return FAIL;
- }
-
- /* Populate some FILETIME structures with values
- These values are valid Creation, Access and Write times
- which I generated, and should work properly.
-
- These values aren't being used for comparison, so they should
- work ok, even though they weren't generated specifically for
- FreeBSD or WIN32 ...
- */
-#if WIN32
- Creation.dwLowDateTime = 458108416;
- Creation.dwHighDateTime = 29436904;
-#endif
- LastAccess.dwLowDateTime = 341368832;
- LastAccess.dwHighDateTime = 29436808;
-
- LastWrite.dwLowDateTime = -1995099136;
- LastWrite.dwHighDateTime = 29436915;
-
- /* Open the file to get a HANDLE */
- TheFileHandle =
- CreateFile(
- "the_file",
- GENERIC_READ|GENERIC_WRITE,
- 0,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
-
- if(TheFileHandle == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Failed to open the file. The error number "
- "returned was %d.",GetLastError());
- }
-
- /* Pass all NULLs, this is useless but should still work. */
- if(SetFileTime(TheFileHandle,NULL,NULL,NULL)==0)
- {
- Fail("ERROR: SetFileTime returned 0, indicating failure. "
- "Three of the params were NULL in this case, did they "
- "cause the problem?");
- }
-
-#if WIN32
- /* Set the Creation time of the File */
- if(SetFileTime(TheFileHandle,&Creation,NULL,NULL)==0)
- {
- Fail("ERROR: SetFileTime returned 0, indicating failure. "
- "Two of the params were NULL in this case, did they "
- "cause the problem?");
- }
-#endif
-
-#if WIN32
- /* Set the Creation, LastWrite time of the File */
- if(SetFileTime(TheFileHandle,&Creation,&LastWrite,NULL)==0)
-#else
- /* Set the LastWrite time of the File */
- if(SetFileTime(TheFileHandle,NULL,&LastWrite,NULL)==0)
-#endif
- {
- Fail("ERROR: SetFileTime returned 0, indicating failure. "
- "One of the params were NULL in this case, did it "
- "cause the problem?");
- }
-
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/file_io/SetFileTime/test4/testinfo.dat b/src/pal/tests/palsuite/file_io/SetFileTime/test4/testinfo.dat
deleted file mode 100644
index a3dfdd02f0..0000000000
--- a/src/pal/tests/palsuite/file_io/SetFileTime/test4/testinfo.dat
+++ /dev/null
@@ -1,14 +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.
-
-Version = 1.0
-Section = file_io
-Function = SetFileTime
-Name = Positive Test for SetFileTime
-TYPE = DEFAULT
-EXE1 = setfiletime
-Description
-= Test the SetFileTime function.
-= This passes a variety of NULL values as parameters to the function.
-= It should still succeed
diff --git a/src/pal/tests/palsuite/file_io/errorpathnotfound/CMakeLists.txt b/src/pal/tests/palsuite/file_io/errorpathnotfound/CMakeLists.txt
index a3847f8ca9..1962ade358 100644
--- a/src/pal/tests/palsuite/file_io/errorpathnotfound/CMakeLists.txt
+++ b/src/pal/tests/palsuite/file_io/errorpathnotfound/CMakeLists.txt
@@ -3,5 +3,4 @@ cmake_minimum_required(VERSION 2.8.12.2)
add_subdirectory(test1)
add_subdirectory(test2)
add_subdirectory(test3)
-add_subdirectory(test4)
diff --git a/src/pal/tests/palsuite/file_io/errorpathnotfound/test2/test2.cpp b/src/pal/tests/palsuite/file_io/errorpathnotfound/test2/test2.cpp
index 5c2ab86b99..b9177ece4e 100644
--- a/src/pal/tests/palsuite/file_io/errorpathnotfound/test2/test2.cpp
+++ b/src/pal/tests/palsuite/file_io/errorpathnotfound/test2/test2.cpp
@@ -18,7 +18,7 @@
** Functions covered by this test are:
-** MoveFileW, FindFirstFileA, FindFirstFileW,
+** FindFirstFileA, FindFirstFileW,
** GetFileAttributesA, GetFileAttributesW,
@@ -95,80 +95,6 @@ int __cdecl main(int argc, char *argv[])
- /*...................Test MoveFileW.............................*/
-
-
-
- /* test with an invalid path */
-
- bRc = MoveFileW(wBadFilePath,wDest);
-
- if(!bRc)
-
- {
-
- if(GetLastError()!= ERROR_PATH_NOT_FOUND)
-
- {
-
- Trace("MoveFileW: calling GetLastError() after moving a file"
-
- " with wrong path returned [%u] while it should return [%u]\n"
-
- ,GetLastError(), ERROR_PATH_NOT_FOUND);
-
- testPass = FALSE;
-
- }
-
- }
-
- else
-
- {
-
- testPass = FALSE;
-
- }
-
-
-
- /* test with invalid file name */
-
- bRc = MoveFileW(wBadFileName,wDest);
-
- if(!bRc)
-
- {
-
- if(GetLastError()!= ERROR_FILE_NOT_FOUND)
-
- {
-
- Trace("MoveFileW: calling GetLastError() after moving a file"
-
- " with wrong name returned [%u] while it should return [%u]\n"
-
- ,GetLastError(), ERROR_FILE_NOT_FOUND);
-
- testPass = FALSE;
-
- }
-
- }
-
- else
-
- {
-
- Trace("MoveFileW: managed to move a file with wrong name\n");
-
- testPass = FALSE;
-
- }
-
-
-
/*............. Test FindFirstFileA..................................*/
diff --git a/src/pal/tests/palsuite/file_io/errorpathnotfound/test2/testinfo.dat b/src/pal/tests/palsuite/file_io/errorpathnotfound/test2/testinfo.dat
index c5a61b949a..ddc5081c11 100644
--- a/src/pal/tests/palsuite/file_io/errorpathnotfound/test2/testinfo.dat
+++ b/src/pal/tests/palsuite/file_io/errorpathnotfound/test2/testinfo.dat
@@ -26,7 +26,7 @@ Description
= Functions covered by this test are:
-= MoveFileW, FindFirstFileA, FindFirstFileW,
+= FindFirstFileA, FindFirstFileW,
= GetFileAttributesA, GetFileAttributesW,
diff --git a/src/pal/tests/palsuite/file_io/errorpathnotfound/test4/CMakeLists.txt b/src/pal/tests/palsuite/file_io/errorpathnotfound/test4/CMakeLists.txt
deleted file mode 100644
index 00621296d0..0000000000
--- a/src/pal/tests/palsuite/file_io/errorpathnotfound/test4/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- test4.cpp
-)
-
-add_executable(paltest_errorpathnotfound_test4
- ${SOURCES}
-)
-
-add_dependencies(paltest_errorpathnotfound_test4 coreclrpal)
-
-target_link_libraries(paltest_errorpathnotfound_test4
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/file_io/errorpathnotfound/test4/test4.cpp b/src/pal/tests/palsuite/file_io/errorpathnotfound/test4/test4.cpp
deleted file mode 100644
index e1b68995b0..0000000000
--- a/src/pal/tests/palsuite/file_io/errorpathnotfound/test4/test4.cpp
+++ /dev/null
@@ -1,351 +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.
-
-/*=====================================================================
-
-**
-
-** Source: test4.c
-
-**
-
-** Purpose: Test the return value of GetLastError() after calling
-
-** some file_io functions with an invalid path.
-
-**
-
-** Functions covered by this test are:
-
-** GetDiskFreeSpaceW, GetTempFileNameA
-
-** and GetTempFileNameW
-
-**
-**
-
-**
-
-
-
-**
-
-**===================================================================*/
-
-
-
-#include <palsuite.h>
-
-
-
-int __cdecl main(int argc, char *argv[])
-
-{
-
-
-
- BOOL testPass = TRUE;
-
- BOOL bRc = TRUE;
-
- DWORD lastErr=-50;
-
- DWORD dwSectorsPerCluster_02; /* sectors per cluster */
-
- DWORD dwBytesPerSector_02; /* bytes per sector */
-
- DWORD dwNumberOfFreeClusters; /* free clusters */
-
- DWORD dwTotalNumberOfClusters; /* total clusters */
-
-
-
- UINT uiError = 0;
-
- char szReturnedName[256];
-
- const UINT uUnique = 0;
-
- const char* sDot = {"tmpr"};
-
- const char* sPrefix = {"cfr"};
-
-
-
- WCHAR wzReturnedName[256];
-
- const WCHAR wDot[] = {'t','m','p','r','\0'};
-
- const WCHAR wPrefix[] = {'c','f','r','\0'};
-
-
-
-
-
- const WCHAR wBadFilePath[] =
-
- {'w','b','a','d','/','b','a',
-
- 'd','.','t','m','p','\0'};
-
- const WCHAR wBadFileName[] =
-
- {'w','B','a','d','.','t','m','p','\0'};
-
-
-
-
-
-
-
- if (0 != PAL_Initialize(argc,argv))
-
- {
-
- return FAIL;
-
- }
-
-
-
- /* test .................. GetDiskFreeSpaceW .................. */
-
-
-
- /* test with invalid file name */
-
- bRc = GetDiskFreeSpaceW(wBadFileName,
-
- &dwSectorsPerCluster_02,
-
- &dwBytesPerSector_02,
-
- &dwNumberOfFreeClusters,
-
- &dwTotalNumberOfClusters);
-
- if (bRc != TRUE)
-
-
-
- {
-
- lastErr=GetLastError();
-
-
-
- if(lastErr != ERROR_FILE_NOT_FOUND)
-
- {
-
- Trace("GetDiskFreeSpaceW: calling GetLastError() returned [%u] "
-
- "while it should return [%u] for a bad File Name\n",
-
- lastErr,ERROR_FILE_NOT_FOUND);
-
- testPass = FALSE;
-
- }
-
- }
-
- else
-
- {
-
- Trace("GetDiskFreeSpaceW: GetDiskFreeSpaceW succeeded when given "
-
- "a bad fileName\n");
-
- testPass = FALSE;
-
-
-
- }
-
-
-
-
-
- /* test with invalid path name */
-
- bRc = GetDiskFreeSpaceW(wBadFilePath,
-
- &dwSectorsPerCluster_02,
-
- &dwBytesPerSector_02,
-
- &dwNumberOfFreeClusters,
-
- &dwTotalNumberOfClusters);
-
- if (bRc != TRUE)
-
-
-
- {
-
- lastErr=GetLastError();
-
- if(lastErr != ERROR_PATH_NOT_FOUND)
-
- {
-
- Trace("GetDiskFreeSpaceW: calling GetLastError() returned [%u] "
-
- "while it should return [%u] for a bad File Name\n",
-
- lastErr,ERROR_PATH_NOT_FOUND);
-
- testPass = FALSE;
-
- }
-
- }
-
- else
-
- {
-
- Trace("GetDiskFreeSpaceW: GetDiskFreeSpaceW succeeded when given "
-
- "a bad fileName\n");
-
- testPass = FALSE;
-
-
-
-
-
- }
-
-
-
-
-
- /* test .................. GetTempFileNameA .................. */
-
-
-
- /* test with invalid path name */
-
- uiError = GetTempFileNameA(sDot, sPrefix, uUnique, szReturnedName);
-
- if (uiError == 0)
-
- {
-
- lastErr=GetLastError();
-
- if(lastErr != ERROR_DIRECTORY)
-
- {
-
-
-
- Trace("GetTempFileNameA: calling GetLastError() returned [%u] "
-
- "while it should return [%u] for invalid path name\n",
-
- lastErr,ERROR_DIRECTORY);
-
- testPass = FALSE;
-
- }
-
- }
-
- else
-
- {
-
- Trace("GetTempFileNameA: GetTempFileNameA succeeded when given "
-
- "invalid path name\n");
-
- testPass = FALSE;
-
- }
-
-
-
-
-
-
-
- /* test .................. GetTempFileNameW .................. */
-
-
-
- /* test with invalid path name */
-
- uiError = GetTempFileNameW(wDot, wPrefix, uUnique, wzReturnedName);
-
- if (uiError == 0)
-
- {
-
- lastErr=GetLastError();
-
- if(lastErr != ERROR_DIRECTORY)
-
- {
-
-
-
- Trace("GetTempFileNameW: calling GetLastError() returned [%u] "
-
- "while it should return [%u] for an invalid path name\n",
-
- lastErr,ERROR_DIRECTORY);
-
- testPass = FALSE;
-
- }
-
- }
-
- else
-
- {
-
- Trace("GetTempFileNameW: GetTempFileNameW succeeded when given"
-
- " an invalid path name\n");
-
- testPass = FALSE;
-
- }
-
-
-
- if(! testPass)
-
- {
-
- Fail("");
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
- PAL_Terminate();
-
- return PASS;
-
-}
-
-
-
diff --git a/src/pal/tests/palsuite/file_io/errorpathnotfound/test4/testinfo.dat b/src/pal/tests/palsuite/file_io/errorpathnotfound/test4/testinfo.dat
deleted file mode 100644
index d7b707fca9..0000000000
--- a/src/pal/tests/palsuite/file_io/errorpathnotfound/test4/testinfo.dat
+++ /dev/null
@@ -1,34 +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.
-
-
-
-Version = 1.0
-
-Section = file_io
-
-Function = some File_io functions
-
-Name = errorpathnotfound - checking GetLastError.
-
-Type = DEFAULT
-
-EXE1 = test4
-
-Description
-
-= Test the return value of GetLastError() after calling
-
-= some file_io functions with an invalid path.
-
-= Functions covered by this test are:
-
-= MoveFIlew, FindFirstFileA, FindFirstFileW,
-
-= GetFileAttributesA, GetFileAttributesW,
-
-= SetFileAttributesA, SetFileAttributesW.
-
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/CMakeLists.txt
index a573dae8cd..4f996bded0 100644
--- a/src/pal/tests/palsuite/filemapping_memmgt/CMakeLists.txt
+++ b/src/pal/tests/palsuite/filemapping_memmgt/CMakeLists.txt
@@ -13,13 +13,11 @@ add_subdirectory(HeapFree)
add_subdirectory(HeapReAlloc)
add_subdirectory(LocalAlloc)
add_subdirectory(LocalFree)
-add_subdirectory(LockFile)
add_subdirectory(MapViewOfFile)
add_subdirectory(OpenFileMappingA)
add_subdirectory(OpenFileMappingW)
add_subdirectory(ProbeMemory)
add_subdirectory(RtlMoveMemory)
-add_subdirectory(UnlockFile)
add_subdirectory(UnmapViewOfFile)
add_subdirectory(VirtualAlloc)
add_subdirectory(VirtualFree)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/CMakeLists.txt
deleted file mode 100644
index 19ee487a6a..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-add_subdirectory(test2)
-add_subdirectory(test3)
-add_subdirectory(test4)
-add_subdirectory(test5)
-add_subdirectory(test6)
-add_subdirectory(test7)
-
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/LockFile.h b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/LockFile.h
deleted file mode 100644
index 2862b6c524..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/LockFile.h
+++ /dev/null
@@ -1,152 +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.
-
-/*=============================================================
-**
-** Source: LockFile.h
-**
-** Purpose: This header file has a RunHelper method which will be used to
-** start a child proccess in many LockFile testcases. The CreateAndLockFile
-** method Creates a file and calls LockFile upon it. And the two Signal
-** methods are used for IPC.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-
-int RunHelper(char* Helper)
-{
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- DWORD RetCode;
-
- ZeroMemory( &si, sizeof(si) );
- si.cb = sizeof(si);
- ZeroMemory( &pi, sizeof(pi) );
-
- if(!CreateProcess( NULL,Helper,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi))
- {
- Fail("ERROR: CreateProcess failed to load executable '%s'.",Helper);
- }
-
- if(WaitForSingleObject( pi.hProcess, INFINITE ) == WAIT_FAILED)
- {
- Fail("ERROR: WaitForSingleObject returned WAIT_FAILED when it was "
- "called.");
- }
-
- /* Get the return value from the helper process */
- if (GetExitCodeProcess(pi.hProcess, &RetCode) == 0)
- {
- Fail("ERROR: GetExitCodeProccess failed when attempting to retrieve "
- "the exit code of the child process.");
- }
-
- if(CloseHandle( pi.hProcess ) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the process.");
- }
-
- if(CloseHandle( pi.hThread ) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the thread.");
- }
-
- return RetCode;
-}
-
-HANDLE CreateAndLockFile(HANDLE TheFile, char* FileName, char* WriteBuffer,
- DWORD LockStart, DWORD LockLength)
-{
- DWORD BytesWritten;
-
- TheFile = CreateFile(FileName,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.",FileName,GetLastError());
- }
-
- if(WriteFile(TheFile, WriteBuffer,
- strlen(WriteBuffer),&BytesWritten, NULL) == 0)
- {
- Fail("ERROR: WriteFile has failed. It returned 0 when we "
- "attempted to write to the file '%s'. GetLastError() "
- "returned %d.",FileName,GetLastError());
- }
-
- if(FlushFileBuffers(TheFile) == 0)
- {
- Fail("ERROR: FlushFileBuffers returned failure. GetLastError() "
- "returned %d.",GetLastError());
- }
-
- if(LockFile(TheFile, LockStart, 0, LockLength, 0) == 0)
- {
- Fail("ERROR: LockFile failed. GetLastError returns %d.",
- GetLastError());
- }
-
- return TheFile;
-}
-
-void SignalAndBusyWait(HANDLE TheFile)
-{
- int size;
- DWORD BytesWritten;
-
- size = GetFileSize(TheFile,NULL)+1;
-
- if(SetFilePointer(TheFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
- {
- Fail("ERROR: SetFilePointer was unable to set the pointer to the "
- "end of the file. GetLastError() returned %d.",GetLastError());
- }
-
- if(WriteFile(TheFile, "x", 1,&BytesWritten, NULL) == 0)
- {
- Fail("ERROR: WriteFile was unable to write to the WaitFile. "
- "GetLastError() returned %d.",GetLastError());
- }
-
- if(FlushFileBuffers(TheFile) == 0)
- {
- Fail("ERROR: FlushFileBuffers failed when flushing the WaitFile. "
- "GetLastError() returned %d.");
- }
-
- while(GetFileSize(TheFile,NULL) == size) { Sleep(100); }
-}
-
-void SignalFinish(HANDLE TheFile)
-{
- DWORD BytesWritten;
-
- if(SetFilePointer(TheFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
- {
- Fail("ERROR: SetFilePointer was unable to set the pointer to the "
- "end of the WaitFile. GetLastError() returned %d.",
- GetLastError());
- }
-
- if(WriteFile(TheFile, "x", 1,&BytesWritten, NULL) == 0)
- {
- Fail("ERROR: WriteFile was unable to write to the WaitFile. "
- "GetLastError returned %d.",GetLastError());
- }
-
- if(FlushFileBuffers(TheFile) == 0)
- {
- Fail("ERROR: FlushFileBuffers failed when flushing the WaitFile. "
- "GetLastError() returned %d.");
- }
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/CMakeLists.txt
deleted file mode 100644
index a9b8869c37..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(TESTSOURCES
- test1.cpp
-)
-
-add_executable(paltest_lockfile_test1
- ${TESTSOURCES}
-)
-
-add_dependencies(paltest_lockfile_test1 coreclrpal)
-
-target_link_libraries(paltest_lockfile_test1
- ${COMMON_TEST_LIBRARIES}
-)
-
-
-set(HELPERSOURCES
- helper.cpp
-)
-
-add_executable(paltest_lockfile_test1_helper
- ${HELPERSOURCES}
-)
-
-add_dependencies(paltest_lockfile_test1_helper coreclrpal)
-
-target_link_libraries(paltest_lockfile_test1_helper
- ${COMMON_TEST_LIBRARIES}
-) \ No newline at end of file
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/helper.cpp b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/helper.cpp
deleted file mode 100644
index 05b4b8451a..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/helper.cpp
+++ /dev/null
@@ -1,86 +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.
-
-/*=============================================================
-**
-** Source: helper.c
-**
-** Purpose: A child process which will attempt to read and write to files
-** which were locked in the parent.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-
-#define BUF_SIZE 128
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE TheFile;
- int result = 0;
- char DataBuffer[BUF_SIZE];
- DWORD BytesRead, BytesWritten;
- char fileName[] = "testfile.tmp";
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Open the same file that the parent has opened and locked */
- TheFile = CreateFile(fileName,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile.",fileName);
- }
-
- /* Attempt to Read 5 bytes from this file. Since it is locked, this
- should fail.
- */
-
- if(ReadFile(TheFile, DataBuffer, 5, &BytesRead, NULL) != 0)
- {
- Trace("ERROR: ReadFile should have failed! It was called on "
- "a locked file. But, it returned non-zero indicating success.");
- result = 1;
- }
-
- /* Attempt to Write 5 bytes to this file. Since it is locked this should
- fail.
- */
-
- memset(DataBuffer,'X',BUF_SIZE);
-
- if(WriteFile(TheFile, DataBuffer, 5,&BytesWritten, NULL) != 0)
- {
- Trace("ERROR: WriteFile should have failed! It was called on "
- "a locked file. But, it returned non-zero indicating success.");
- result = 1;
- }
-
- /* Check to ensure that the number of Bytes read/written is still 0,
- since nothing should have been read or written.
- */
-
- if(BytesRead != 0 || BytesWritten !=0)
- {
- Trace("ERROR: The number of bytes read is %d and written is %d. "
- "These should both be 0, as the file was locked.",
- BytesRead,BytesWritten);
- result = 1;
- }
-
- PAL_TerminateEx(result);
- return result;
-}
-
-
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/test1.cpp b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/test1.cpp
deleted file mode 100644
index cee223ef81..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/test1.cpp
+++ /dev/null
@@ -1,140 +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.
-
-/*=============================================================
-**
-** Source: test1.c
-**
-** Purpose: Open a file, and lock it from start to EOF. Then create a
-** new process, which will attempt to Read and Write from the file. Check
-** to ensure both of these operations fail.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../LockFile.h"
-
-#define HELPER "helper"
-
-int __cdecl main(int argc, char *argv[])
-{
-
- HANDLE TheFile;
- DWORD FileStart = 0;
- DWORD FileEnd = 0;
- const char lpBuffer[] = "This is a test file.";
- DWORD bytesWritten;
- BOOL bRc = TRUE;
- char fileName[] = "testfile.tmp";
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Important to have sharing enabled, or there is no need for the lock. */
- TheFile = CreateFile(fileName,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile.",fileName);
- }
-
- bRc = WriteFile(TheFile,
- lpBuffer,
- (DWORD)sizeof(lpBuffer),
- &bytesWritten,
- NULL);
-
- if(!bRc)
- {
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
-
- Fail("ERROR: Could not write to file '%s' with WriteFile.",fileName);
- }
- else if(bytesWritten != (DWORD)sizeof(lpBuffer))
- {
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
-
- Fail("ERROR: Could not write the correct number of bytes to the "
- "file '%s' with WriteFile.",fileName);
- }
-
- /* Find the value for the End of the file */
- FileEnd = SetFilePointer(TheFile,0,NULL,FILE_END);
-
- if(FileEnd == INVALID_SET_FILE_POINTER)
- {
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
-
- Fail("ERROR: Could not set the file pointer to the end of the file "
- "using SetFilePointer. It returned INVALID_SET_FILE_POINTER.");
- }
-
- /* Lock the file from Start to EOF */
-
- if(LockFile(TheFile, FileStart, 0, FileEnd, 0) == 0)
- {
- Trace("ERROR: LockFile failed. GetLastError returns %d.",
- GetLastError());
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Launch another process, which will attempt to read and write from
- the locked file.
-
- If the helper program returns 1, then the test fails. More
- specific errors are given by the Helper file itself.
- */
- if(RunHelper(HELPER))
- {
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
-
- Fail("ERROR: The Helper program determined that the file was not "
- "locked properly by LockFile.");
- }
-
- if(UnlockFile(TheFile, FileStart, 0, FileEnd, 0) == 0)
- {
- Trace("ERROR: UnlockFile failed. GetLastError returns %d.",
- GetLastError());
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
-
- PAL_Terminate();
- return PASS;
-}
-
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/testinfo.dat
deleted file mode 100644
index cbe4d94d40..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test1/testinfo.dat
+++ /dev/null
@@ -1,15 +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.
-
-Version = 1.0
-Section = Filemapping_memmgt
-Function = LockFile
-Name = Positive test for LockFile API
-TYPE = DEFAULT
-EXE1 = test1
-EXE2 = helper
-Description
-= Open a file, and lock it from start to EOF. Then create a
-= new process, which will attempt to Read and Write from the file. Check
-= to ensure both of these operations fail.
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/CMakeLists.txt
deleted file mode 100644
index 42e88c5999..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- test2.cpp
-)
-
-add_executable(paltest_lockfile_test2
- ${SOURCES}
-)
-
-add_dependencies(paltest_lockfile_test2 coreclrpal)
-
-target_link_libraries(paltest_lockfile_test2
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/test2.cpp b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/test2.cpp
deleted file mode 100644
index 8aef130ef4..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/test2.cpp
+++ /dev/null
@@ -1,94 +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.
-
-/*=============================================================
-**
-** Source: test2.c
-**
-** Purpose: Open a file, and lock it from start to EOF. Check to ensure
-** the current process can still read and write from/to the file.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../LockFile.h"
-
-#define FILENAME "testfile.txt"
-
-int __cdecl main(int argc, char *argv[])
-{
-
- HANDLE TheFile = NULL;
- DWORD FileStart = 0;
- DWORD FileEnd = 0;
- DWORD BytesWritten = 0;
- DWORD BytesRead = 0;
- char WriteBuffer[] = "This is some test data.";
- char DataBuffer[128];
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Call the helper function to Create a file, write 'WriteBuffer' to
- the file, and lock the file.
- */
-
- FileEnd = strlen(WriteBuffer);
- TheFile = CreateAndLockFile(TheFile, FILENAME, WriteBuffer,
- FileStart, FileEnd);
-
- /* Move the file pointer to the start of the file */
- if(SetFilePointer(TheFile, 0, NULL, FILE_BEGIN) != 0)
- {
- Fail("ERROR: SetFilePointer failed to move the file pointer back "
- "to the start of the file.");
- }
-
- /* Attempt to Read 5 bytes from this file. Since the lock does not
- affect the calling process, this should succeed.
- */
-
- if(ReadFile(TheFile, DataBuffer, 5, &BytesRead, NULL) == 0)
- {
- Fail("ERROR: ReadFile has failed. Attempted to read in 5 bytes from "
- "the file '%s' after it had LockFile called upon it, but within "
- "the same process.",FILENAME);
- }
-
- if(strncmp(DataBuffer, WriteBuffer, 5) != 0)
- {
- Fail("ERROR: The data read in from ReadFile is not what should have "
- "been written in the file. '%s' ",DataBuffer);
- }
-
- /* Attempt to Write 5 bytes to this file. Since the lock does not affect
- the calling process, this should succeed.
- */
-
- memset(WriteBuffer, 'X', strlen(WriteBuffer));
-
- if(WriteFile(TheFile, WriteBuffer, 5,&BytesWritten, NULL) == 0)
- {
- Fail("ERROR: WriteFile has failed. Attempted to write 5 bytes to "
- "the file '%s' after it had LockFile called upon it, but within "
- "the same process.",FILENAME);
- }
-
- if(UnlockFile(TheFile, FileStart, 0, FileEnd, 0) == 0)
- {
- Fail("ERROR: UnlockFile failed. GetLastError returns %d.",
- GetLastError());
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/testinfo.dat
deleted file mode 100644
index c84e7c0a0a..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test2/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = Filemapping_memmgt
-Function = LockFile
-Name = Positive test for LockFile API
-TYPE = DEFAULT
-EXE1 = test2
-Description
-= Open a file, and lock it from start to EOF. Check to ensure
-= the current process can still read and write from/to the file.
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/CMakeLists.txt
deleted file mode 100644
index 299e8cf76c..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(TESTSOURCES
- test3.cpp
-)
-
-add_executable(paltest_lockfile_test3
- ${TESTSOURCES}
-)
-
-add_dependencies(paltest_lockfile_test3 coreclrpal)
-
-target_link_libraries(paltest_lockfile_test3
- ${COMMON_TEST_LIBRARIES}
-)
-
-
-set(HELPERSOURCES
- helper.cpp
-)
-
-add_executable(paltest_lockfile_test3_helper
- ${HELPERSOURCES}
-)
-
-add_dependencies(paltest_lockfile_test3_helper coreclrpal)
-
-target_link_libraries(paltest_lockfile_test3_helper
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/helper.cpp b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/helper.cpp
deleted file mode 100644
index 079417fce8..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/helper.cpp
+++ /dev/null
@@ -1,102 +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.
-
-/*=============================================================
-**
-** Source: helper.c
-**
-** Purpose: A child process which will attempt to read and write to files
-** which were locked in the parent.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-
-#define FILENAME "testfile.txt"
-#define BUF_SIZE 128
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE TheFile;
- int result = 0;
- char DataBuffer[BUF_SIZE];
- DWORD BytesRead, BytesWritten;
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Open the same file that the parent has opened and locked */
- TheFile = CreateFile(FILENAME,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returns %d.",FILENAME,GetLastError());
- }
-
- /* Attempt to Read the first 3 bytes from this file.
- Since it is unlocked, this should work properly.
- */
-
- if(ReadFile(TheFile, DataBuffer, 3, &BytesRead, NULL) == 0)
- {
- Trace("ERROR: ReadFile should have succeeded in reading the first "
- "three bytes of the file, as these bytes were not locked. "
- "GetLastError() returned %d.",GetLastError());
- result = 1;
- }
-
- /* Now, read the next 10 bytes, which should be locked. Ensure that
- ReadFile fails.
- */
-
- if(ReadFile(TheFile, DataBuffer,10, &BytesRead, NULL) != 0)
- {
- Trace("ERROR: ReadFile should have failed when attempting to read in "
- "bytes between StartOfFile+3 and EndOfFile-3.");
- result = 1;
- }
-
- /* Attempt to Write 10 bytes to this file. Since it is locked this should
- fail.
- */
-
- memset(DataBuffer,'X',BUF_SIZE);
-
- if(WriteFile(TheFile, DataBuffer, 10,&BytesWritten, NULL) != 0)
- {
- Trace("ERROR: WriteFile should have failed when attempting to write "
- "bytes between StartOfFile+3 and EOF-3.");
- result = 1;
- }
-
-
- /* Move the FilePointer to the EOF-3, where the lock ends */
- if(SetFilePointer(TheFile,-3,NULL,FILE_END) == INVALID_SET_FILE_POINTER)
- {
- Fail("ERROR: Could not set the file pointer to the EOF-3 "
- "using SetFilePointer. It returned INVALID_SET_FILE_POINTER.");
- }
-
- /* Attempt to write to those 3 unlocked bytes on the end of the file */
- if(WriteFile(TheFile, DataBuffer, 3,&BytesWritten, NULL) == 0)
- {
- Trace("ERROR: WriteFile should have succeeded when attempting "
- "to write the last three bytes of the file, as they were not "
- "locked. GetLastError() returned %d.",GetLastError());
- result = 1;
- }
-
- PAL_TerminateEx(result);
- return result;
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/test3.cpp b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/test3.cpp
deleted file mode 100644
index 78662c5685..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/test3.cpp
+++ /dev/null
@@ -1,71 +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.
-
-/*=============================================================
-**
-** Source: test3.c
-**
-** Purpose: Open a file, lock a region in the middle. Create a new process
-** and attempt to read and write directly before and after that region, which
-** should succeed. Also, check to see that reading/writing in the locked
-** region fails.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../LockFile.h"
-
-#define HELPER "helper"
-#define FILENAME "testfile.txt"
-
-int __cdecl main(int argc, char *argv[])
-{
-
- HANDLE TheFile = NULL;
- DWORD FileStart = 0;
- DWORD FileEnd = 0;
- char* WriteBuffer = "12345678901234567890123456";
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Call the helper function to Create a file, write 'WriteBuffer' to
- the file, and lock the file.
- */
-
- FileEnd = strlen(WriteBuffer);
- TheFile = CreateAndLockFile(TheFile,FILENAME, WriteBuffer,
- FileStart+3, FileEnd-6);
-
-
- /* Launch another process, which will attempt to read and write from
- the locked file.
-
- If the helper program returns 1, then the test fails. More
- specific errors are given by the Helper file itself.
- */
- if(RunHelper(HELPER))
- {
- Fail("ERROR: The Helper program determined that the file was not "
- "locked properly by LockFile.");
- }
-
- if(UnlockFile(TheFile, FileStart+3, 0, FileEnd-6, 0) == 0)
- {
- Fail("ERROR: UnlockFile failed. GetLastError returns %d.",
- GetLastError());
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file. "
- "GetLastError() returned %d.",GetLastError());
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/testinfo.dat
deleted file mode 100644
index b64ec5ed03..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test3/testinfo.dat
+++ /dev/null
@@ -1,16 +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.
-
-Version = 1.0
-Section = Filemapping_memmgt
-Function = LockFile
-Name = Positive test for LockFile API
-TYPE = DEFAULT
-EXE1 = test3
-EXE2 = helper
-Description
-= Open a file, lock a region in the middle. Create a new process
-= and attempt to read and write directly before and after that region, which
-= should succeed. Also, check to see that reading/writing in the locked
-= region fails.
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/CMakeLists.txt
deleted file mode 100644
index 55cf9c64f7..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- test4.cpp
-)
-
-add_executable(paltest_lockfile_test4
- ${SOURCES}
-)
-
-add_dependencies(paltest_lockfile_test4 coreclrpal)
-
-target_link_libraries(paltest_lockfile_test4
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/test4.cpp b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/test4.cpp
deleted file mode 100644
index f5cd359fb5..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/test4.cpp
+++ /dev/null
@@ -1,231 +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.
-
-/*=============================================================
-**
-** Source: test4.c
-**
-** Purpose:
-** - Attempt to call LockFile on a file without GENERIC_READ or
-** GENERIC_WRITE (this should fail)
-** - Attempt to overlap two locks, this should fail.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-
-char fileName[] = "testfile.tmp";
-
-void OverlapTest()
-{
- HANDLE TheFile = NULL;
- DWORD FileStart = 0;
- const char lpBuffer[] = "This is a test file.";
- DWORD bytesWritten;
- BOOL bRc = TRUE;
-
- TheFile = CreateFile(fileName,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.",fileName,GetLastError());
- }
-
- bRc = WriteFile(TheFile,
- lpBuffer,
- (DWORD)sizeof(lpBuffer),
- &bytesWritten,
- NULL);
-
- if(!bRc)
- {
- Trace("ERROR: Could not write to file '%s' with WriteFile.",fileName);
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
-
- }
- else if(bytesWritten != (DWORD)sizeof(lpBuffer))
- {
- Trace("ERROR: Could not write the correct number of bytes to the "
- "file '%s' with WriteFile.",fileName);
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Lock the First 5 bytes of the File */
-
- if(LockFile(TheFile, FileStart, 0, 5, 0) == 0)
- {
- Trace("ERROR: LockFile failed in Overlap test. "
- "GetLastError returns %d.",
- GetLastError());
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Lock from Byte 2 until 7 -- this overlaps and should return failure. */
- if(LockFile(TheFile,FileStart+2, 0, 5, 0) != 0)
- {
- Trace("ERROR: LockFile returned success when it was overlapped on "
- "an already locked region of the file.");
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Unlock the file */
- if(UnlockFile(TheFile, FileStart, 0, 5, 0) == 0)
- {
- Trace("ERROR: UnlockFile failed in Overlap test. GetLastError "
- "returns %d.",GetLastError());
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Close the File */
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file in the Overlap "
- "test. GetLastError() returned %d.",GetLastError());
- }
-}
-
-void FlagsTest(DWORD TheFlags, int ExpectedResult)
-{
- HANDLE TheFile = NULL;
- DWORD FileStart = 0;
- int result;
-
- TheFile = CreateFile(fileName,
- TheFlags,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.",fileName,GetLastError());
- }
-
- /* Lock the First 5 bytes of the File. The result of this depends
- upon which flags were set with the CreateFile.
- */
-
- result = LockFile(TheFile, FileStart, 0, 5, 0);
-
- /* If the expected result is 1, check to ensure the result is non-zero,
- as non-zero is returned on success
- */
- if(ExpectedResult == 1)
- {
- if(result == 0)
- {
- Trace("ERROR: LockFile returned zero when the expected result "
- "was non-zero. It was passed the flag value %d.",
- TheFlags);
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
- }
- /* If the expected result is 0, check to ensure the result is 0 */
- else
- {
- if(result != 0)
- {
- Trace("ERROR: LockFile returned %d when the expected result "
- "was zero. It was passed the flag value %d.",
- result, TheFlags);
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
- }
-
- /* Only unlock the file if we expect it to be successfully locked */
- if(ExpectedResult)
- {
- if(UnlockFile(TheFile,FileStart,0, 5, 0) == 0)
- {
- Fail("ERROR: UnlockFile failed in the Flags Test. GetLastError() "
- "returned %d.",GetLastError());
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
- }
-
- /* Close the File */
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file in the Flags "
- "test. GetLastError() returned %d.",GetLastError());
- }
-}
-
-int __cdecl main(int argc, char *argv[])
-{
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* This test opens a file, then calls lock twice, overlapping the
- regions and checking to ensure that this causes an error.
- */
- OverlapTest();
-
- /* Test that LockFile fails if no flags are set */
- FlagsTest(0,0);
-
- /* Test that LockFile passes if only GENERIC_READ is set */
- FlagsTest(GENERIC_READ,1);
-
- /* Test that LockFile passes if only GENERIC_WRITE is set */
- FlagsTest(GENERIC_WRITE,1);
-
- PAL_Terminate();
- return PASS;
-}
-
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/testinfo.dat
deleted file mode 100644
index 0600260d82..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test4/testinfo.dat
+++ /dev/null
@@ -1,14 +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.
-
-Version = 1.0
-Section = Filemapping_memmgt
-Function = LockFile
-Name = Negative test for LockFile API
-TYPE = DEFAULT
-EXE1 = test4
-Description
-= - Attempt to call LockFile on a file without GENERIC_READ or
-= GENERIC_WRITE (this should fail)
-= - Attempt to overlap two locks, this should fail.
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/CMakeLists.txt
deleted file mode 100644
index 82a174907e..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(TESTSOURCES
- test5.cpp
-)
-
-add_executable(paltest_lockfile_test5
- ${TESTSOURCES}
-)
-
-add_dependencies(paltest_lockfile_test5 coreclrpal)
-
-target_link_libraries(paltest_lockfile_test5
- ${COMMON_TEST_LIBRARIES}
-)
-
-
-set(HELPERSOURCES
- helper.cpp
-)
-
-add_executable(paltest_lockfile_test5_helper
- ${HELPERSOURCES}
-)
-
-add_dependencies(paltest_lockfile_test5_helper coreclrpal)
-
-target_link_libraries(paltest_lockfile_test5_helper
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/helper.cpp b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/helper.cpp
deleted file mode 100644
index 1fc9b1a9a5..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/helper.cpp
+++ /dev/null
@@ -1,122 +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.
-
-/*=============================================================
-**
-** Source: helper.c
-**
-** Purpose: A child process which will attempt to read and write to files
-** which were locked in the parent. It will also lock another region of the
-** same file.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../LockFile.h"
-
-#define FILENAME "testfile.txt"
-#define WAITFILENAME "waitfile"
-#define BUF_SIZE 128
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE TheFile, WaitFile;
- int result = 0;
- char DataBuffer[BUF_SIZE];
- DWORD BytesRead;
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Open the same file that the parent has opened and locked */
- TheFile = CreateFile(FILENAME,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Trace("ERROR: Could not open file '%s' with CreateFile.",FILENAME);
- result = 1;
- }
-
- /* Open up the WaitFile that we're using for IPC */
- WaitFile = CreateFile(WAITFILENAME,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (WaitFile == INVALID_HANDLE_VALUE)
- {
- Trace("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.",WAITFILENAME,GetLastError());
- result = 1;
- }
-
- /* Lock the same file that the parent process locked, but the child
- locks bytes 11 through 20
- */
-
- if(LockFile(TheFile, 11, 0, 10, 0) == 0)
- {
- Trace("ERROR: LockFile failed in the child proccess. "
- "GetLastError returns %d.",
- GetLastError());
- result = 1;
- }
-
- /* Check to ensure the parent lock is respected */
- if(ReadFile(TheFile, DataBuffer, 10, &BytesRead, NULL) != 0)
- {
- Trace("ERROR: ReadFile returned success when it should "
- "have failed. Attempted to read the first 10 bytes "
- "of a file which was locked by the parent process.");
- result = 1;
- }
-
- /* Check to ensure the lock put on by this proccess doesn't restrict
- access
- */
-
- if(SetFilePointer(TheFile, 11, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
- {
- Trace("ERROR: SetFilePointer was unable to move the file pointer to "
- "the 11th byte in the file, within the child proccess. "
- "GetLastError() returned %d.",GetLastError());
- result = 1;
- }
-
- if(ReadFile(TheFile, DataBuffer, 10, &BytesRead, NULL) == 0)
- {
- Trace("ERROR: ReadFile failed when attempting to read a section of "
- "the file which was locked by the current process. It should "
- "have been able to read this. GetLastError() returned %d.",
- GetLastError());
- result = 1;
- }
-
- // Sleep for a bit to give the parent a chance to block before we do.
- Sleep(1000);
-
- /* Switch back to the parent, so it can check the child's locks */
- SignalAndBusyWait(WaitFile);
-
- if(UnlockFile(TheFile, 11, 0, 10, 0) == 0)
- {
- Fail("ERROR: Failed to Unlock bytes 11-20 in the file. "
- "GetLastError returned %d.",GetLastError());
- }
-
- PAL_TerminateEx(result);
- return result;
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/test5.cpp b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/test5.cpp
deleted file mode 100644
index a02a3c5a49..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/test5.cpp
+++ /dev/null
@@ -1,161 +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.
-
-/*=============================================================
-**
-** Source: test5.c
-**
-** Purpose:
-** Have two processes obtain a lock on a single file, but in different
-** regions of the file. Use Read/Write to ensure the locks are respected.
-** This requires some IPC, which is done here with a crude busy wait on a
-** file (waiting for the file size to change) to avoid too many more
-** dependencies.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../LockFile.h"
-
-#define HELPER "helper"
-#define FILENAME "testfile.txt"
-#define WAITFILENAME "waitfile"
-#define BUF_SIZE 128
-
-int RunTest(char* Helper, HANDLE TheFile, HANDLE WaitFile)
-{
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- DWORD ChildRetCode = 0;
- DWORD ParentRetCode = 0;
- DWORD BytesRead;
- char DataBuffer[BUF_SIZE];
-
-
- ZeroMemory( &si, sizeof(si) );
- si.cb = sizeof(si);
- ZeroMemory( &pi, sizeof(pi) );
-
- /* Load up the helper Process, and then Wait until it signals that it
- is finished locking.
- */
- if(!CreateProcess( NULL,Helper,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi))
- {
- Fail("ERROR: CreateProcess failed to load executable '%s'.",Helper);
- }
-
- SignalAndBusyWait(WaitFile);
-
- /* Now the child proccess has locked another section of the file, from
- bytes 11 through 20. Let's check that the parent lock is still ignored
- by the parent proccess and that the child's lock is respected.
- */
-
- if(ReadFile(TheFile, DataBuffer, 10, &BytesRead, NULL) == 0)
- {
- Trace("ERROR: ReadFile failed when attempting to read a section of "
- "the file which was locked by the current process. It should "
- "have been able to read this. GetLastError() returned %d.",
- GetLastError());
- ParentRetCode = 1;
- }
-
- SetFilePointer(TheFile, 11, 0, FILE_BEGIN);
-
- if(ReadFile(TheFile, DataBuffer, 10, &BytesRead, NULL) != 0)
- {
- Trace("ERROR: ReadFile returned success when it should "
- "have failed. Attempted to read 10 bytes of the file which "
- "were locked by the child.");
- ParentRetCode = 1;
- }
-
- /* We're finished testing. Let the child proccess know so it can clean
- up, and the parent will wait until it is done.
- */
- SignalFinish(WaitFile);
- WaitForSingleObject(pi.hProcess,INFINITE);
-
- /* Get the return value from the helper process */
- if (GetExitCodeProcess(pi.hProcess, &ChildRetCode) == 0)
- {
- Fail("ERROR: GetExitCodeProccess failed when attempting to retrieve "
- "the exit code of the child process.");
- }
-
- if(CloseHandle( pi.hProcess ) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the process.");
- }
-
- if(CloseHandle( pi.hThread ) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the thread.");
- }
-
- return (ChildRetCode || ParentRetCode);
-}
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE TheFile = NULL;
- HANDLE WaitFile = NULL;
- char* WriteBuffer = "12345678901234567890123456";
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Open up the file we'll be using for some crude IPC */
- WaitFile = CreateFile(WAITFILENAME,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (WaitFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.",WAITFILENAME,GetLastError());
- }
-
- /* Call the helper function to Create a file, write 'WriteBuffer' to
- the file, and lock the file from bytes 0 to 10.
- */
- TheFile = CreateAndLockFile(TheFile, FILENAME, WriteBuffer,
- 0, 10);
-
- /* Run the test. Better errors are displayed by Trace throughout. */
- if(RunTest(HELPER, TheFile, WaitFile))
- {
- Fail("ERROR: Attempting to have two processes lock different "
- "sections of the same file has failed.");
- }
-
- /* Unlock the first 10 bytes which were locked by the parent proccess */
- if(UnlockFile(TheFile, 0, 0, 10, 0) == 0)
- {
- Fail("ERROR: Failed to Unlock the first 10 bytes of the file. "
- "GetLastError returned %d.",GetLastError());
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file used for "
- "testing the locks. GetLastError() returns %d.",GetLastError());
- }
-
- if(CloseHandle(WaitFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the wait file. "
- "GetLastError() returns %d.",GetLastError());
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/testinfo.dat
deleted file mode 100644
index f020933cd9..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test5/testinfo.dat
+++ /dev/null
@@ -1,17 +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.
-
-Version = 1.0
-Section = Filemapping_memmgt
-Function = LockFile
-Name = Positive test for LockFile API
-TYPE = DEFAULT
-EXE1 = test5
-EXE2 = helper
-Description
-= Have two processes obtain a lock on a single file, but in different
-= regions of the file. Use Read/Write to ensure the locks are respected.
-= This requires some IPC, which is done here with a crude busy wait on a
-= file (waiting for the file size to change) to avoid too many more
-= dependencies.
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/CMakeLists.txt
deleted file mode 100644
index fc5f90113e..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(TESTSOURCES
- test6.cpp
-)
-
-add_executable(paltest_lockfile_test6
- ${TESTSOURCES}
-)
-
-add_dependencies(paltest_lockfile_test6 coreclrpal)
-
-target_link_libraries(paltest_lockfile_test6
- ${COMMON_TEST_LIBRARIES}
-)
-
-
-set(HELPERSOURCES
- helper.cpp
-)
-
-add_executable(paltest_lockfile_test6_helper
- ${HELPERSOURCES}
-)
-
-add_dependencies(paltest_lockfile_test6_helper coreclrpal)
-
-target_link_libraries(paltest_lockfile_test6_helper
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/helper.cpp b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/helper.cpp
deleted file mode 100644
index 98112fc4a5..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/helper.cpp
+++ /dev/null
@@ -1,71 +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.
-
-/*=============================================================
-**
-** Source: helper.c
-**
-** Purpose: A child process which will attempt to append to the end of
-** a locked file.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-
-#define FILENAME "testfile.txt"
-#define BUF_SIZE 128
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE TheFile;
- int result = 0;
- char DataBuffer[BUF_SIZE];
- DWORD BytesWritten;
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Open the same file that the parent has opened and locked */
- TheFile = CreateFile(FILENAME,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Trace("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returns %d.",FILENAME,GetLastError());
- result = -1;
- }
-
-
- /* Move the FilePointer to the EOF */
- if(SetFilePointer(TheFile,0,NULL,FILE_END) == INVALID_SET_FILE_POINTER)
- {
- Trace("ERROR: Could not set the file pointer to the EOF "
- "using SetFilePointer. It returned INVALID_SET_FILE_POINTER.");
- result = -1;
- }
-
- memset(DataBuffer, 'X', BUF_SIZE);
-
- /* Return the result of WriteFile -- we want to check in the parent that
- this was successful. Note: WriteFile doesn't get run if something
- failed during the setup, in that case -1 is returned.
- */
-
- if(result != -1)
- {
- result = WriteFile(TheFile, DataBuffer, 3,&BytesWritten, NULL);
- }
-
- PAL_TerminateEx(result);
- return result;
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/test6.cpp b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/test6.cpp
deleted file mode 100644
index ba01b9710a..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/test6.cpp
+++ /dev/null
@@ -1,146 +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.
-
-/*=============================================================
-**
-** Source: test6.c
-**
-** Purpose:
-** Append to a file which is locked until the end of the file, and
-** append to a file which is locked past the end of the file. (The first
-** should succeed, while the second should fail)
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../LockFile.h"
-
-#define HELPER "helper"
-#define FILENAME "testfile.txt"
-
-/* This test checks that you can append to a file which is locked from Start
- to EOF.
-*/
-void Test1()
-{
- HANDLE TheFile = NULL;
- DWORD FileStart = 0;
- DWORD FileEnd = 0;
- int result;
- char* WriteBuffer = "12345678901234567890123456";
-
- /* Call the helper function to Create a file, write 'WriteBuffer' to
- the file, and lock the file.
- */
-
- FileEnd = strlen(WriteBuffer);
- TheFile = CreateAndLockFile(TheFile,FILENAME, WriteBuffer,
- FileStart, FileEnd);
-
-
- /*
- Launch another proccess which will attempt to append to the
- end of the file. Note: This returns -1 if the setup failed in some way.
- */
-
- result = RunHelper(HELPER);
-
- if(result == -1)
- {
- Fail("ERROR: The Helper program failed in setting up the "
- "test, so it could never be run.");
- }
- else if(result == 0)
- {
- Fail("ERROR: Failed to append to the file which was Locked from "
- "start until EOF. Should have been able to append to this "
- "file still. GetLastError() is %d.",GetLastError());
- }
-
- if(UnlockFile(TheFile, FileStart, 0, FileEnd, 0) == 0)
- {
- Fail("ERROR: UnlockFile failed. GetLastError returns %d.",
- GetLastError());
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file. "
- "GetLastError() returned %d.",GetLastError());
- }
-
-}
-
-/* This test checks that you can't append to a file which is locked beyond
- EOF.
-*/
-void Test2()
-{
- HANDLE TheFile = NULL;
- DWORD FileStart = 0;
- DWORD FileEnd = 0;
- int result;
- char* WriteBuffer = "12345678901234567890123456";
-
- /* Call the helper function to Create a file, write 'WriteBuffer' to
- the file, and lock the file.
- */
-
- FileEnd = strlen(WriteBuffer);
- TheFile = CreateAndLockFile(TheFile,FILENAME, WriteBuffer,
- FileStart, FileEnd+20);
-
-
- /*
- Launch another proccess which will attempt to append to the
- end of the file.
- */
-
- result = RunHelper(HELPER);
-
- if(result == -1)
- {
- Fail("ERROR: The Helper program failed in setting up the "
- "test, so it could never be run.");
- }
- else if(result > 0)
- {
- Fail("ERROR: The Helper program successfully appended to the "
- "end of the file, even though it was locked beyond EOF. This "
- "should have failed.");
- }
-
- if(UnlockFile(TheFile, FileStart, 0, FileEnd+20, 0) == 0)
- {
- Fail("ERROR: UnlockFile failed. GetLastError returns %d.",
- GetLastError());
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file. "
- "GetLastError() returned %d.",GetLastError());
- }
-
-}
-
-
-int __cdecl main(int argc, char *argv[])
-{
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Test a file which is locked until EOF to see if you can append */
- Test1();
-
- /* Test a file which is locked past EOF to ensure you can't append */
- Test2();
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/testinfo.dat
deleted file mode 100644
index 871a9a2756..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test6/testinfo.dat
+++ /dev/null
@@ -1,15 +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.
-
-Version = 1.0
-Section = Filemapping_memmgt
-Function = LockFile
-Name = Positive test for LockFile API
-TYPE = DEFAULT
-EXE1 = test6
-EXE2 = helper
-Description
-= Append to a file which is locked until the end of the file, and
-= append to a file which is locked past the end of the file. (The first
-= should succeed, while the second should fail)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/CMakeLists.txt
deleted file mode 100644
index c52bf55ba8..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- test7.cpp
-)
-
-add_executable(paltest_lockfile_test7
- ${SOURCES}
-)
-
-add_dependencies(paltest_lockfile_test7 coreclrpal)
-
-target_link_libraries(paltest_lockfile_test7
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/test7.cpp b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/test7.cpp
deleted file mode 100644
index c572a6e653..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/test7.cpp
+++ /dev/null
@@ -1,135 +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.
-
-/*=============================================================
-**
-** Source: test7.c
-**
-** Purpose: Try locking an invalid HANDLE and a NULL Handle.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../LockFile.h"
-
-int __cdecl main(int argc, char *argv[])
-{
-
- HANDLE TheFile = NULL;
- DWORD FileEnd = 0;
- const char lpBuffer[] = "This is a test file.";
- DWORD bytesWritten;
- BOOL bRc = TRUE;
- char fileName[] = "testfile.tmp";
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- TheFile = CreateFile(fileName,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.",fileName,GetLastError());
- }
-
- bRc = WriteFile(
- TheFile, // handle to file
- lpBuffer, // data buffer
- (DWORD)sizeof(lpBuffer), // number of bytes to write
- &bytesWritten, // number of bytes written
- NULL // overlapped buffer
- );
-
- if(!bRc)
- {
- Trace("ERROR: Could not write to file '%s' with WriteFile.",fileName);
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
-
- }
- else if(bytesWritten != (DWORD)sizeof(lpBuffer))
- {
- Trace("ERROR: Could not write the correct number of bytes to the "
- "file '%s' with WriteFile.",fileName);
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Attempt to lock a region of this file beyond EOF, to ensure this
- doesn't cause an error.
- */
- FileEnd = SetFilePointer(TheFile, 0, NULL, FILE_END);
-
- if(LockFile(TheFile, FileEnd+10, 0, 10, 0) == 0)
- {
- Trace("ERROR: LockFile failed when attempting to lock a region "
- "beyond the EOF. GetLastError() returned %d.",GetLastError());
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- if(UnlockFile(TheFile, FileEnd+10, 0, 10, 0) == 0)
- {
- Trace("ERROR: UnlockFile failed when attempting to unlock the region "
- "which was locked beyond the EOF. GetLastError returned %d.",
- GetLastError());
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: Failed to call CloseHandle. GetLastError "
- "returned %d.",GetLastError());
- }
-
- /* Attempt to call Lockfile on an HANDLE which has been closed. This
- should fail.
- */
- if(LockFile(TheFile, 0, 0, 5, 0) != 0)
- {
- Fail("ERROR: Attempted to Lock an invalid handle and the function "
- "returned success.");
- }
-
- /* Attempt to call Lockfile by passing it NULL for a handle. This should
- fail.
- */
-
- if(LockFile(NULL, 0, 0, 5, 0) != 0)
- {
- Fail("ERROR: Attempted to Lock a NULL handle and the function "
- "returned success.");
- }
-
- PAL_Terminate();
- return PASS;
-}
-
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/testinfo.dat
deleted file mode 100644
index 74e7f00306..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/LockFile/test7/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = Filemapping_memmgt
-Function = LockFile
-Name = Positive test for LockFile API
-TYPE = DEFAULT
-EXE1 = test7
-Description
-= Ensure that LockFile succeeds when the lock begins beyond
-= EOF. Try locking an invalid HANDLE and a NULL Handle.
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/CMakeLists.txt
deleted file mode 100644
index a3847f8ca9..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-add_subdirectory(test2)
-add_subdirectory(test3)
-add_subdirectory(test4)
-
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/UnlockFile.h b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/UnlockFile.h
deleted file mode 100644
index 8fce2695a7..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/UnlockFile.h
+++ /dev/null
@@ -1,112 +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.
-
-/*=============================================================
-**
-** Source: UnLockFile.h
-**
-** Purpose: This header file has a RunHelper method which will be used to
-** start a child proccess in many LockFile testcases. The CreateAndLockFile
-** method Creates a file and calls LockFile upon it. And the two Signal
-** methods are used for IPC.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-
-HANDLE CreateAndLockFile(HANDLE TheFile, char* FileName, char* WriteBuffer,
- DWORD LockStart, DWORD LockLength)
-{
- DWORD BytesWritten;
-
- TheFile = CreateFile(FileName,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.\n",FileName,GetLastError());
- }
-
- if(WriteFile(TheFile, WriteBuffer,
- strlen(WriteBuffer),&BytesWritten, NULL) == 0)
- {
- Fail("ERROR: WriteFile has failed. It returned 0 when we "
- "attempted to write to the file '%s'. GetLastError() "
- "returned %d.\n",FileName,GetLastError());
- }
-
- if(FlushFileBuffers(TheFile) == 0)
- {
- Fail("ERROR: FlushFileBuffers returned failure. GetLastError() "
- "returned %d.\n",GetLastError());
- }
-
- if(LockFile(TheFile, LockStart, 0, LockLength, 0) == 0)
- {
- Fail("ERROR: LockFile failed. GetLastError returns %d.\n",
- GetLastError());
- }
-
- return TheFile;
-}
-
-void SignalAndBusyWait(HANDLE TheFile)
-{
- int size;
- DWORD BytesWritten;
-
- size = GetFileSize(TheFile,NULL)+1;
-
- if(SetFilePointer(TheFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
- {
- Fail("ERROR: SetFilePointer was unable to set the pointer to the "
- "end of the file. GetLastError() returned %d.\n",GetLastError());
- }
-
- if(WriteFile(TheFile, "x", 1,&BytesWritten, NULL) == 0)
- {
- Fail("ERROR: WriteFile was unable to write to the WaitFile. "
- "GetLastError() returned %d.\n",GetLastError());
- }
-
- if(FlushFileBuffers(TheFile) == 0)
- {
- Fail("ERROR: FlushFileBuffers failed when flushing the WaitFile. "
- "GetLastError() returned %d.\n");
- }
-
- while(GetFileSize(TheFile,NULL) == size) {}
-}
-
-void SignalFinish(HANDLE TheFile)
-{
- DWORD BytesWritten;
-
- if(SetFilePointer(TheFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
- {
- Fail("ERROR: SetFilePointer was unable to set the pointer to the "
- "end of the WaitFile. GetLastError() returned %d.\n",
- GetLastError());
- }
-
- if(WriteFile(TheFile, "x", 1,&BytesWritten, NULL) == 0)
- {
- Fail("ERROR: WriteFile was unable to write to the WaitFile. "
- "GetLastError returned %d.\n",GetLastError());
- }
-
- if(FlushFileBuffers(TheFile) == 0)
- {
- Fail("ERROR: FlushFileBuffers failed when flushing the WaitFile. "
- "GetLastError() returned %d.\n");
- }
-
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/CMakeLists.txt
deleted file mode 100644
index 58b0329d0d..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(TESTSOURCES
- test1.cpp
-)
-
-add_executable(paltest_unlockfile_test1
- ${TESTSOURCES}
-)
-
-add_dependencies(paltest_unlockfile_test1 coreclrpal)
-
-target_link_libraries(paltest_unlockfile_test1
- ${COMMON_TEST_LIBRARIES}
-)
-
-
-set(HELPERSOURCES
- helper.cpp
-)
-
-add_executable(paltest_unlockfile_test1_helper
- ${HELPERSOURCES}
-)
-
-add_dependencies(paltest_unlockfile_test1_helper coreclrpal)
-
-target_link_libraries(paltest_unlockfile_test1_helper
- ${COMMON_TEST_LIBRARIES}
-) \ No newline at end of file
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/helper.cpp b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/helper.cpp
deleted file mode 100644
index c2ef5a6736..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/helper.cpp
+++ /dev/null
@@ -1,92 +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.
-
-/*=============================================================
-**
-** Source: helper.c
-**
-** Purpose: A child process which will attempt to read from the
-** locked file to ensure it is locked. After it has been unlocked, it
-** will then read again to check that Unlock worked.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../UnlockFile.h"
-
-#define FILENAME "testfile.txt"
-#define WAITFILENAME "waitfile"
-#define BUF_SIZE 128
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE TheFile, WaitFile;
- int result = 0;
- char DataBuffer[BUF_SIZE];
- DWORD BytesRead;
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Open the same file that the parent has opened and locked */
- TheFile = CreateFile(FILENAME,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Trace("ERROR: Could not open file '%s' with CreateFile.\n",FILENAME);
- result = 1;
- }
-
- /* Open up the WaitFile that we're using for IPC */
- WaitFile = CreateFile(WAITFILENAME,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (WaitFile == INVALID_HANDLE_VALUE)
- {
- Trace("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.\n",WAITFILENAME,GetLastError());
- result = 1;
- }
-
-
- /* Check to ensure the parent lock is respected */
- if(ReadFile(TheFile, DataBuffer, 10, &BytesRead, NULL) != 0)
- {
- Trace("ERROR: ReadFile returned success when it should "
- "have failed. Attempted to read the first 10 bytes "
- "of a file which was locked by the parent process.\n");
- result = 1;
- }
-
- // Sleep for a bit to give the parent a chance to block before we do.
- Sleep(1000);
-
- /* Switch back to the parent, so it can unlock the file */
- SignalAndBusyWait(WaitFile);
-
- if(ReadFile(TheFile, DataBuffer, 10, &BytesRead, NULL) == 0)
- {
- Trace("ERROR: ReadFile was unable to read from the file after it "
- "had been unlocked. Attempted to read 10 bytes and ReadFile "
- "returned 0. GetLastError() returned %d.\n",GetLastError());
- result = 1;
- }
-
- PAL_TerminateEx(result);
- return result;
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/test1.cpp b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/test1.cpp
deleted file mode 100644
index 14634c7f7a..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/test1.cpp
+++ /dev/null
@@ -1,154 +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.
-
-/*=============================================================
-**
-** Source: test1.c
-**
-** Purpose:
-** Have the parent Lock a file, then have the child check the lock, then
-** have the parent unlock the file, and the child check again.
-** This requires some IPC, which is done here with a crude busy wait on a
-** file (waiting for the file size to change) to avoid too many more
-** dependencies.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../UnlockFile.h"
-
-#define HELPER "helper"
-#define FILENAME "testfile.txt"
-#define WAITFILENAME "waitfile"
-#define BUF_SIZE 128
-
-int RunTest(char* Helper, HANDLE TheFile, HANDLE WaitFile)
-{
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- DWORD ChildRetCode = 0;
- DWORD ParentRetCode = 0;
- DWORD FileEnd;
-
- ZeroMemory( &si, sizeof(si) );
- si.cb = sizeof(si);
- ZeroMemory( &pi, sizeof(pi) );
-
- /* Load up the helper Process, and then Wait until it signals that it
- is finished locking.
- */
- if(!CreateProcess( NULL, Helper, NULL,
- NULL, FALSE, 0,
- NULL, NULL, &si, &pi))
- {
- Fail("ERROR: CreateProcess failed to load executable '%s'.\n",Helper);
- }
-
- SignalAndBusyWait(WaitFile);
-
- /* When the child proccess is finished verifying the lock, find the end
- of the file and unlock the file.
- */
-
- FileEnd = SetFilePointer(TheFile, 0, NULL, FILE_END);
-
- if(FileEnd == INVALID_SET_FILE_POINTER)
- {
- Trace("ERROR: SetFilePointer failed to set the file pointer to the "
- "end of the file. GetLastError() returned %d.\n",
- GetLastError());
- ParentRetCode = 1;
- }
-
- if(UnlockFile(TheFile, 0, 0, FileEnd, 0) == 0)
- {
- Trace("ERROR: The call to UnlockFile returned 0 when attempting to "
- "unlock the file within the parent. This should have "
- "succeeded. GetLastError returned %d.\n",GetLastError());
- ParentRetCode = 1;
- }
-
- /* Switch back to the child so that it can ensure the unlock worked
- properly.
- */
-
- SignalFinish(WaitFile);
- WaitForSingleObject(pi.hProcess,INFINITE);
-
- /* Get the return value from the helper process */
- if (GetExitCodeProcess(pi.hProcess, &ChildRetCode) == 0)
- {
- Fail("ERROR: GetExitCodeProccess failed when attempting to retrieve "
- "the exit code of the child process.\n");
- }
-
- if(CloseHandle( pi.hProcess ) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the process.\n");
- }
-
- if(CloseHandle( pi.hThread ) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the thread.\n");
- }
-
- return (ChildRetCode || ParentRetCode);
-}
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE TheFile = NULL;
- HANDLE WaitFile = NULL;
- char* WriteBuffer = "12345678901234567890123456";
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Open up the file we'll be using for some crude IPC */
- WaitFile = CreateFile(WAITFILENAME,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (WaitFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.\n",WAITFILENAME,GetLastError());
- }
-
- /* Call the helper function to Create a file, write 'WriteBuffer' to
- the file, and lock the file from start to end.
- */
- TheFile = CreateAndLockFile(TheFile, FILENAME, WriteBuffer,
- 0, strlen(WriteBuffer));
-
- /* Run the test. Better errors are displayed by Trace throughout. */
- if(RunTest(HELPER, TheFile, WaitFile))
- {
- Fail("ERROR: Checking to ensure that Unlock successfully unlocked "
- "a file failed.\n");
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file used for "
- "testing the locks. GetLastError() returns %d.\n",
- GetLastError());
- }
-
- if(CloseHandle(WaitFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the wait file. "
- "GetLastError() returns %d.\n",GetLastError());
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/testinfo.dat
deleted file mode 100644
index 4d0ad6afc9..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test1/testinfo.dat
+++ /dev/null
@@ -1,17 +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.
-
-Version = 1.0
-Section = Filemapping_memmgt
-Function = UnlockFile
-Name = Positive test for UnlockFile API
-TYPE = DEFAULT
-EXE1 = test1
-EXE2 = helper
-Description
-= Have the parent Lock a file, then have the child check the lock, then
-= have the parent unlock the file, and the child check again.
-= This requires some IPC, which is done here with a crude busy wait on a
-= file (waiting for the file size to change) to avoid too many more
-= dependencies.
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/CMakeLists.txt
deleted file mode 100644
index 251d21ba65..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- test2.cpp
-)
-
-add_executable(paltest_unlockfile_test2
- ${SOURCES}
-)
-
-add_dependencies(paltest_unlockfile_test2 coreclrpal)
-
-target_link_libraries(paltest_unlockfile_test2
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/test2.cpp b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/test2.cpp
deleted file mode 100644
index 22c2cce2fb..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/test2.cpp
+++ /dev/null
@@ -1,154 +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.
-
-/*=============================================================
-**
-** Source: test2.c
-**
-** Purpose: Open a file, and call Unlock on the file, even though it has yet
-** to be locked. Then lock a portion of the file, and attempt to call unlock
-** on a larger portion of the file. Also, try to unlock a smaller portion
-** than was locked.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../UnlockFile.h"
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE TheFile = NULL;
- const char lpBuffer[] = "This is a test file.";
- DWORD bytesWritten;
- BOOL bRc = TRUE;
- char fileName[] = "testfile.tmp";
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Open a file which is in the directory */
- TheFile = CreateFile(fileName,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.\n",fileName,GetLastError());
- }
-
- bRc = WriteFile(
- TheFile, // handle to file
- lpBuffer, // data buffer
- (DWORD)sizeof(lpBuffer), // number of bytes to write
- &bytesWritten, // number of bytes written
- NULL // overlapped buffer
- );
-
- if(!bRc)
- {
- Trace("ERROR: Could not write to file '%s' with WriteFile.",fileName);
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
-
- }
- else if(bytesWritten != (DWORD)sizeof(lpBuffer))
- {
- Trace("ERROR: Could not write the correct number of bytes to the "
- "file '%s' with WriteFile.",fileName);
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Call unlock file on an unlocked file, this should return 0 */
- if(UnlockFile(TheFile, 0, 0, 5, 0) != 0)
- {
- Trace("ERROR: Attempted to unlock a file which was not locked and "
- "the UnlockFile call was successful.\n");
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Lock the file */
- if(LockFile(TheFile, 0, 0, 5, 0) == 0)
- {
- Trace("ERROR: Failed to call LockFile on a valid file handle. "
- "GetLastError returned %d.\n",GetLastError());
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Try to unlock more of the file than was locked by LockFile */
- if(UnlockFile(TheFile, 0, 0, 10, 0) != 0)
- {
- Trace("ERROR: Attempted to unlock bytes 0 to 9, but only bytes "
- "0 to 4 are locked. But, UnlockFile was successful, when it "
- "should have failed.\n");
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Try to unlock less of the file than was locked by LockFile */
- if(UnlockFile(TheFile, 0, 0, 3, 0) != 0)
- {
- Trace("ERROR: Attempted to unlock bytes 0 to 2, but the bytes 0 to "
- "4 were locked by LockFile. Unlockfile should have failed "
- "when attempting this operation.\n");
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Properly unlock the file */
- if(UnlockFile(TheFile, 0, 0, 5, 0) == 0)
- {
- Trace("ERROR: UnlockFile failed to unlock bytes 0 to 4 of the file. "
- "GetLastError returned %d.\n",GetLastError());
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.\n");
- }
-
- PAL_Terminate();
- return PASS;
-}
-
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/testinfo.dat
deleted file mode 100644
index 932a4a2b8a..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test2/testinfo.dat
+++ /dev/null
@@ -1,16 +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.
-
-Version = 1.0
-Section = Filemapping_memmgt
-Function = UnlockFile
-Name = Positive test for UnlockFile API
-TYPE = DEFAULT
-EXE1 = test2
-Description
-= Open a file, and call Unlock on the file, even though it has yet
-= to be locked. Then lock a portion of the file, and attempt to call unlock
-= on a larger portion of the file. Also, try to unlock a smaller portion
-= than was locked.
-
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/CMakeLists.txt
deleted file mode 100644
index 980a9b0f75..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(TESTSOURCES
- test3.cpp
-)
-
-add_executable(paltest_unlockfile_test3
- ${TESTSOURCES}
-)
-
-add_dependencies(paltest_unlockfile_test3 coreclrpal)
-
-target_link_libraries(paltest_unlockfile_test3
- ${COMMON_TEST_LIBRARIES}
-)
-
-
-set(HELPERSOURCES
- helper.cpp
-)
-
-add_executable(paltest_unlockfile_test3_helper
- ${HELPERSOURCES}
-)
-
-add_dependencies(paltest_unlockfile_test3_helper coreclrpal)
-
-target_link_libraries(paltest_unlockfile_test3_helper
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/helper.cpp b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/helper.cpp
deleted file mode 100644
index 650abf49ad..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/helper.cpp
+++ /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.
-
-/*=============================================================
-**
-** Source: helper.c
-**
-** Purpose: A child process which will lock a portion of the file,
-** then try to unlock a portion of the file which was locked by the parent.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../UnlockFile.h"
-
-#define FILENAME "testfile.txt"
-#define WAITFILENAME "waitfile"
-#define BUF_SIZE 128
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE TheFile, WaitFile;
- int result = 0;
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Open the same file that the parent has opened and locked */
- TheFile = CreateFile(FILENAME,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Trace("ERROR: Could not open file '%s' with CreateFile.\n",FILENAME);
- result = 1;
- }
-
- /* Open up the WaitFile that we're using for IPC */
- WaitFile = CreateFile(WAITFILENAME,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (WaitFile == INVALID_HANDLE_VALUE)
- {
- Trace("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.\n",WAITFILENAME,GetLastError());
- result = 1;
- }
-
- /* Lock a section of the file different from which was locked in the
- parent proccess
- */
- if(LockFile(TheFile, 10, 0, 10, 0) == 0)
- {
- Trace("ERROR: The LockFile call within the child failed to lock "
- "the file. GetLastError() returned %d.\n",GetLastError());
- result = 1;
- }
-
- /* Attempt to unlock the portion of the file which was locked within the
- parent process.
- */
- if(UnlockFile(TheFile, 0, 0, 10, 0) != 0)
- {
- Trace("ERROR: The UnlockFile call within the child succeeded in "
- "calling UnlockFile on the portion of the file which was "
- "locked by the parent.\n");
- result = 1;
- }
-
- // Sleep for a bit to give the parent a chance to block before we do.
- Sleep(1000);
-
- /* Switch back to the parent, so it can check the child lock */
- SignalAndBusyWait(WaitFile);
-
- /* Finally, clean up the lock which was done within this proccess and
- exit.
- */
- if(UnlockFile(TheFile, 10, 0, 10, 0) == 0)
- {
- Trace("ERROR: The UnlockFile call within the child failed to unlock "
- "the portion of the file which was locked by the child. "
- "GetLastError() returned %d.\n", GetLastError());
- result = 1;
- }
-
- PAL_TerminateEx(result);
- return result;
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/test3.cpp b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/test3.cpp
deleted file mode 100644
index cf27aba0a3..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/test3.cpp
+++ /dev/null
@@ -1,142 +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.
-
-/*=============================================================
-**
-** Source: test3.c
-**
-** Purpose:
-** Lock a portion of the file with the parent. Then have the child lock
-** another portion. Have the child attempt to call Unlock on the parent's
-** locked data, and the parent do the same to the child. Ensure that the
-** locks are respected.
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../UnlockFile.h"
-
-#define HELPER "helper"
-#define FILENAME "testfile.txt"
-#define WAITFILENAME "waitfile"
-#define BUF_SIZE 128
-
-int RunTest(char* Helper, HANDLE TheFile, HANDLE WaitFile)
-{
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- DWORD ChildRetCode = 0;
- DWORD ParentRetCode = 0;
-
- ZeroMemory( &si, sizeof(si) );
- si.cb = sizeof(si);
- ZeroMemory( &pi, sizeof(pi) );
-
- /* Load up the helper Process, and then Wait until it signals that it
- is finished locking.
- */
- if(!CreateProcess( NULL, Helper, NULL,
- NULL, FALSE, 0,
- NULL, NULL, &si, &pi))
- {
- Fail("ERROR: CreateProcess failed to load executable '%s'.\n",Helper);
- }
-
- SignalAndBusyWait(WaitFile);
-
- /* When the child proccess is finished setting its lock and testing the
- parent lock, then the parent can test the child's lock.
- */
-
- if(UnlockFile(TheFile, 10, 0, 10, 0) != 0)
- {
- Trace("ERROR: The parent proccess called Unlock on the child "
- "proccesses lock, and the function returned non-zero, when "
- "it should have failed.\n");
- ParentRetCode = 1;
- }
-
- /* Switch back to the child so that it can unlock its portion and
- cleanup.
- */
-
- SignalFinish(WaitFile);
- WaitForSingleObject(pi.hProcess,INFINITE);
-
- /* Get the return value from the helper process */
- if (GetExitCodeProcess(pi.hProcess, &ChildRetCode) == 0)
- {
- Fail("ERROR: GetExitCodeProccess failed when attempting to retrieve "
- "the exit code of the child process.\n");
- }
-
- if(CloseHandle( pi.hProcess ) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the process.\n");
- }
-
- if(CloseHandle( pi.hThread ) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the thread.\n");
- }
-
- return (ChildRetCode || ParentRetCode);
-}
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE TheFile = NULL;
- HANDLE WaitFile = NULL;
- char* WriteBuffer = "12345678901234567890123456";
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Open up the file we'll be using for some crude IPC */
- WaitFile = CreateFile(WAITFILENAME,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (WaitFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.\n",WAITFILENAME,GetLastError());
- }
-
- /* Call the helper function to Create a file, write 'WriteBuffer' to
- the file, and lock the file from bytes 0-9.
- */
- TheFile = CreateAndLockFile(TheFile, FILENAME, WriteBuffer,
- 0, 10);
-
- /* Run the test. Better errors are displayed by Trace throughout. */
- if(RunTest(HELPER, TheFile, WaitFile))
- {
- Fail("ERROR: The test to check that the Unlock will not work on "
- "on locks set by other proccesses failed.\n");
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file used for "
- "testing the locks. GetLastError() returns %d.\n",
- GetLastError());
- }
-
- if(CloseHandle(WaitFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the wait file. "
- "GetLastError() returns %d.\n",GetLastError());
- }
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/testinfo.dat
deleted file mode 100644
index bf7ec5f809..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test3/testinfo.dat
+++ /dev/null
@@ -1,17 +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.
-
-Version = 1.0
-Section = Filemapping_memmgt
-Function = UnlockFile
-Name = Positive test for UnlockFile API
-TYPE = DEFAULT
-EXE1 = test3
-EXE2 = helper
-Description
-= Lock a portion of the file with the parent. Then have the child lock
-= another portion. Have the child attempt to call Unlock on the parent's
-= locked data, and the parent do the same to the child. Ensure that the
-= locks are respected.
-
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/CMakeLists.txt b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/CMakeLists.txt
deleted file mode 100644
index e721b04b6c..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- test4.cpp
-)
-
-add_executable(paltest_unlockfile_test4
- ${SOURCES}
-)
-
-add_dependencies(paltest_unlockfile_test4 coreclrpal)
-
-target_link_libraries(paltest_unlockfile_test4
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/test4.cpp b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/test4.cpp
deleted file mode 100644
index 55abcd24bc..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/test4.cpp
+++ /dev/null
@@ -1,187 +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.
-
-/*=============================================================
-**
-** Source: test4.c
-**
-** Purpose: Pass an invalid handle to UnlockFile. Pass a null handle to
-** UnlockFile. Create a file and lock two consecuative regions and call
-** UnlockFile on the whole region (this should fail, see msdn)
-**
-**
-**============================================================*/
-
-#include <palsuite.h>
-#include "../UnlockFile.h"
-
-int __cdecl main(int argc, char *argv[])
-{
- HANDLE TheFile = NULL;
- const char lpBuffer[] = "This is a test file.";
- DWORD bytesWritten;
- BOOL bRc = TRUE;
- char fileName[] = "testfile.tmp";
-
- if(0 != (PAL_Initialize(argc, argv)))
- {
- return FAIL;
- }
-
- /* Open a file which is in the directory */
- TheFile = CreateFile(fileName,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.\n",fileName,GetLastError());
- }
-
- bRc = WriteFile(
- TheFile, // handle to file
- lpBuffer, // data buffer
- (DWORD)sizeof(lpBuffer), // number of bytes to write
- &bytesWritten, // number of bytes written
- NULL // overlapped buffer
- );
-
- if(!bRc)
- {
- Trace("ERROR: Could not write to file '%s' with WriteFile.",fileName);
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
-
- }
- else if(bytesWritten != (DWORD)sizeof(lpBuffer))
- {
- Trace("ERROR: Could not write the correct number of bytes to the "
- "file '%s' with WriteFile.",fileName);
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.\n");
- }
-
-
- /* Test an invalid handle and a NULL handle */
- if(UnlockFile(TheFile, 0, 0, 0, 0) != 0)
- {
- Fail("ERROR: Called UnlockFile on an invalid HANDLE and it "
- "returned a success value.\n");
- }
-
- if(UnlockFile(NULL, 0, 0, 0, 0) != 0)
- {
- Fail("ERROR: Called UnlockFile with NULL passed for the HANDLE and "
- "it returned a success value.\n");
- }
-
- /* Re-open the file */
- TheFile = CreateFile(fileName,
- GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,
- OPEN_ALWAYS,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- if (TheFile == INVALID_HANDLE_VALUE)
- {
- Fail("ERROR: Could not open file '%s' with CreateFile. "
- "GetLastError() returned %d.\n",fileName,GetLastError());
- }
-
- /* Lock two consecuative regions of this file */
- if(LockFile(TheFile, 0, 0, 5, 0) == 0)
- {
- Trace("ERROR: LockFile failed attempting to lock bytes 0-4. "
- "GetLastError() returned %d.\n",GetLastError());
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- if(LockFile(TheFile, 5, 0, 5, 0) == 0)
- {
- Fail("ERROR: LockFile failed attempting to lock bytes 5-9. "
- "GetLastError() returned %d.\n",GetLastError());
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- /* Attempt to unlock the entire region which was locked with one
- call to UnlockFile. This should fail.
- */
- if(UnlockFile(TheFile, 0, 0, 10, 0) != 0)
- {
- Fail("ERROR: Called UnlockFile on bytes 0-9 which were locked with "
- "two seperate LockFile calls. This should have failed. "
- "UnlockFile will not unlock consecuative locked regions.\n");
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
-
- /* Now, unlock the regions one at a time. */
- if(UnlockFile(TheFile, 0, 0, 5, 0) == 0)
- {
- Fail("ERROR: UnlockFile failed when attempting to unlock bytes "
- "0-4 of the file. GetLastError() returned %d.\n",GetLastError());
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- if(UnlockFile(TheFile, 5, 0, 5, 0) == 0)
- {
- Fail("ERROR: UnlockFile failed when attempting to unlock bytes "
- "5-9 of the file. GetLastError() returned %d.\n",GetLastError());
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.");
- }
- Fail("");
- }
-
- if(CloseHandle(TheFile) == 0)
- {
- Fail("ERROR: CloseHandle failed to close the file.\n");
- }
-
- PAL_Terminate();
- return PASS;
-}
-
diff --git a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/testinfo.dat b/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/testinfo.dat
deleted file mode 100644
index 4f3885b978..0000000000
--- a/src/pal/tests/palsuite/filemapping_memmgt/UnlockFile/test4/testinfo.dat
+++ /dev/null
@@ -1,16 +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.
-
-Version = 1.0
-Section = Filemapping_memmgt
-Function = UnlockFile
-Name = Positive test for UnlockFile API
-TYPE = DEFAULT
-EXE1 = test4
-Description
-= Pass an invalid handle to UnlockFile. Pass a null handle to
-= UnlockFile. Create a file and lock two consecuative regions and call
-= UnlockFile on the whole region (this should fail, see msdn)
-
-
diff --git a/src/pal/tests/palsuite/miscellaneous/CMakeLists.txt b/src/pal/tests/palsuite/miscellaneous/CMakeLists.txt
index 9352edef52..d437fc9320 100644
--- a/src/pal/tests/palsuite/miscellaneous/CMakeLists.txt
+++ b/src/pal/tests/palsuite/miscellaneous/CMakeLists.txt
@@ -9,7 +9,6 @@ add_subdirectory(FlushInstructionCache)
add_subdirectory(FormatMessageW)
add_subdirectory(FreeEnvironmentStringsW)
add_subdirectory(GetCommandLineW)
-add_subdirectory(GetComputerNameW)
add_subdirectory(GetEnvironmentStringsW)
add_subdirectory(GetEnvironmentVariableA)
add_subdirectory(GetEnvironmentVariableW)
diff --git a/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/CMakeLists.txt b/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/CMakeLists.txt
deleted file mode 100644
index f6aa0cb2d9..0000000000
--- a/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-
diff --git a/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/CMakeLists.txt b/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/CMakeLists.txt
deleted file mode 100644
index fe5b72071e..0000000000
--- a/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- test.cpp
-)
-
-add_executable(paltest_getcomputernamew_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_getcomputernamew_test1 coreclrpal)
-
-target_link_libraries(paltest_getcomputernamew_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/test.cpp b/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/test.cpp
deleted file mode 100644
index 7a00cad598..0000000000
--- a/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/test.cpp
+++ /dev/null
@@ -1,53 +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.
-
-/*============================================================
-**
-** Source : test.c
-**
-** Purpose: Positive Test for GetComputerName() function
-**
-**
-**=========================================================*/
-
-#define UNICODE
-#include <palsuite.h>
-
-int __cdecl main(int argc, char *argv[])
-{
-
- int HOST_NAME_MAX = 255;
- WCHAR wzComputerName[HOST_NAME_MAX+1];
- DWORD dwSize = sizeof(wzComputerName)/sizeof(wzComputerName[0]);
-
- // Initialize the PAL and return FAILURE if this fails
- if(0 != (PAL_Initialize(argc, argv)))
- {
- Fail ("ERROR: PAL_Initialize() call failed!\n");
- }
-
- if (0 == GetComputerName(wzComputerName, &dwSize))
- {
- Fail("ERROR: GetComputerName failed with %d!\n", GetLastError());
- }
-
- // dwSize is the length of wzComputerName without NULL
- if (dwSize < 0 || dwSize > (sizeof(wzComputerName)/sizeof(wzComputerName[0]) - 1))
- {
- Fail("ERROR: GetComputerName returned %S with dwSize = %u whereas the passed in buffer size is %d!\n",
- wzComputerName, dwSize, sizeof(wzComputerName)/sizeof(wzComputerName[0]));
- }
-
- // dwSize is the length of wzComputerName without NULL
- if (dwSize != wcslen(wzComputerName))
- {
- Fail("ERROR: GetComputerName returned %S of length %d which is not equal to dwSize = %u!\n",
- wzComputerName, wcslen(wzComputerName), dwSize);
- }
-
- printf ("GetComputerName returned %S of length %u\n", wzComputerName, dwSize);
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/testinfo.dat b/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/testinfo.dat
deleted file mode 100644
index d45bca1c25..0000000000
--- a/src/pal/tests/palsuite/miscellaneous/GetComputerNameW/test1/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = Miscellaneous
-Function = GetComputerNameW
-Name = Test for GetComputerNameW
-TYPE = DEFAULT
-EXE1 = test
-Description
-= Check to ensure that GetComputerNameW returnes a decent value.
-
diff --git a/src/pal/tests/palsuite/miscellaneous/GetUserNameW/CMakeLists.txt b/src/pal/tests/palsuite/miscellaneous/GetUserNameW/CMakeLists.txt
deleted file mode 100644
index f6aa0cb2d9..0000000000
--- a/src/pal/tests/palsuite/miscellaneous/GetUserNameW/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-add_subdirectory(test1)
-
diff --git a/src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/CMakeLists.txt b/src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/CMakeLists.txt
deleted file mode 100644
index 6527b83d13..0000000000
--- a/src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-cmake_minimum_required(VERSION 2.8.12.2)
-
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-set(SOURCES
- test.cpp
-)
-
-add_executable(paltest_getusernamew_test1
- ${SOURCES}
-)
-
-add_dependencies(paltest_getusernamew_test1 coreclrpal)
-
-target_link_libraries(paltest_getusernamew_test1
- ${COMMON_TEST_LIBRARIES}
-)
diff --git a/src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/test.cpp b/src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/test.cpp
deleted file mode 100644
index 809f14c12d..0000000000
--- a/src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/test.cpp
+++ /dev/null
@@ -1,52 +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.
-
-/*============================================================
-**
-** Source : test.c
-**
-** Purpose: Positive Test for GetUserName() function
-**
-**
-**=========================================================*/
-
-#define UNICODE
-#include <palsuite.h>
-
-int __cdecl main(int argc, char *argv[])
-{
-
- WCHAR wzUserName[UNLEN+1];
- DWORD dwSize = sizeof(wzUserName)/sizeof(wzUserName[0]);
-
- // Initialize the PAL and return FAILURE if this fails
- if(0 != (PAL_Initialize(argc, argv)))
- {
- Fail ("ERROR: PAL_Initialize() call failed!\n");
- }
-
- if (0 == GetUserName(wzUserName, &dwSize))
- {
- Fail("ERROR: GetUserName failed with %d!\n", GetLastError());
- }
-
- // dwSize is the length of wzUserName with NULL
- if (dwSize <= 0 || dwSize > (sizeof(wzUserName)/sizeof(wzUserName[0])))
- {
- Fail("ERROR: GetUserName returned %S with dwSize = %u whereas the passed in buffer size is %d!\n",
- wzUserName, dwSize, sizeof(wzUserName)/sizeof(wzUserName[0]));
- }
-
- // dwSize is the length of wzUserName with NULL
- if (dwSize != wcslen(wzUserName)+1)
- {
- Fail("ERROR: GetUserName returned %S of length %d which is not equal to dwSize-1 = %u!\n",
- wzUserName, wcslen(wzUserName), dwSize-1);
- }
-
- printf ("GetUserName returned %S of length %u\n", wzUserName, dwSize-1);
-
- PAL_Terminate();
- return PASS;
-}
diff --git a/src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/testinfo.dat b/src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/testinfo.dat
deleted file mode 100644
index 3e0aa48bd0..0000000000
--- a/src/pal/tests/palsuite/miscellaneous/GetUserNameW/test1/testinfo.dat
+++ /dev/null
@@ -1,13 +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.
-
-Version = 1.0
-Section = Miscellaneous
-Function = GetUserNameW
-Name = Test for GetUserNameW
-TYPE = DEFAULT
-EXE1 = test
-Description
-= Check to ensure that GetUserNameW returnes a decent value.
-
diff --git a/src/pal/tests/palsuite/paltestlist.txt b/src/pal/tests/palsuite/paltestlist.txt
index 1e580cd596..ea088d75a2 100644
--- a/src/pal/tests/palsuite/paltestlist.txt
+++ b/src/pal/tests/palsuite/paltestlist.txt
@@ -471,8 +471,6 @@ filemapping_memmgt/HeapReAlloc/test5/paltest_heaprealloc_test5
filemapping_memmgt/LocalAlloc/test1/paltest_localalloc_test1
filemapping_memmgt/LocalFree/test1/paltest_localfree_test1
filemapping_memmgt/LocalFree/test2/paltest_localfree_test2
-filemapping_memmgt/LockFile/test2/paltest_lockfile_test2
-filemapping_memmgt/LockFile/test7/paltest_lockfile_test7
filemapping_memmgt/MapViewOfFile/test1/paltest_mapviewoffile_test1
filemapping_memmgt/MapViewOfFile/test2/paltest_mapviewoffile_test2
filemapping_memmgt/MapViewOfFile/test3/paltest_mapviewoffile_test3
@@ -485,8 +483,6 @@ filemapping_memmgt/RtlMoveMemory/test1/paltest_rtlmovememory_test1
filemapping_memmgt/RtlMoveMemory/test3/paltest_rtlmovememory_test3
filemapping_memmgt/RtlMoveMemory/test4/paltest_rtlmovememory_test4
filemapping_memmgt/RtlMoveMemory/test5/paltest_rtlmovememory_test5
-filemapping_memmgt/UnlockFile/test2/paltest_unlockfile_test2
-filemapping_memmgt/UnlockFile/test4/paltest_unlockfile_test4
filemapping_memmgt/UnmapViewOfFile/test1/paltest_unmapviewoffile_test1
filemapping_memmgt/UnmapViewOfFile/test2/paltest_unmapviewoffile_test2
filemapping_memmgt/VirtualAlloc/test1/paltest_virtualalloc_test1
@@ -520,7 +516,6 @@ filemapping_memmgt/VirtualProtect/test4/paltest_virtualprotect_test4
filemapping_memmgt/VirtualProtect/test6/paltest_virtualprotect_test6
filemapping_memmgt/VirtualProtect/test7/paltest_virtualprotect_test7
filemapping_memmgt/VirtualQuery/test1/paltest_virtualquery_test1
-file_io/AreFileApisANSI/test1/paltest_arefileapisansi_test1
file_io/CompareFileTime/test1/paltest_comparefiletime_test1
file_io/CopyFileA/test1/paltest_copyfilea_test1
file_io/CopyFileA/test2/paltest_copyfilea_test2
@@ -535,7 +530,6 @@ file_io/DeleteFileW/test1/paltest_deletefilew_test1
file_io/errorpathnotfound/test2/paltest_errorpathnotfound_test2
file_io/errorpathnotfound/test3/paltest_errorpathnotfound_test3
file_io/FILECanonicalizePath/paltest_filecanonicalizepath_test1
-file_io/FileTimeToDosDateTime/test1/paltest_filetimetodosdatetime_test1
file_io/FindClose/test1/paltest_findclose_test1
file_io/FindFirstFileA/test1/paltest_findfirstfilea_test1
file_io/FindFirstFileW/test1/paltest_findfirstfilew_test1
@@ -544,27 +538,14 @@ file_io/FindNextFileA/test2/paltest_findnextfilea_test2
file_io/FindNextFileW/test1/paltest_findnextfilew_test1
file_io/FindNextFileW/test2/paltest_findnextfilew_test2
file_io/FlushFileBuffers/test1/paltest_flushfilebuffers_test1
-file_io/GetConsoleCP/test1/paltest_getconsolecp_test1
file_io/GetConsoleOutputCP/test1/paltest_getconsoleoutputcp_test1
file_io/GetCurrentDirectoryA/test1/paltest_getcurrentdirectorya_test1
file_io/GetCurrentDirectoryW/test1/paltest_getcurrentdirectoryw_test1
-file_io/GetDiskFreeSpaceW/test1/paltest_getdiskfreespacew_test1
-file_io/GetDiskFreeSpaceW/test2/paltest_getdiskfreespacew_test2
file_io/GetFileAttributesA/test1/paltest_getfileattributesa_test1
file_io/GetFileAttributesExW/test2/paltest_getfileattributesexw_test2
file_io/GetFileAttributesW/test1/paltest_getfileattributesw_test1
file_io/GetFileSize/test1/paltest_getfilesize_test1
file_io/GetFileSizeEx/test1/paltest_getfilesizeex_test1
-file_io/GetFileTime/test1/paltest_getfiletime_test1
-file_io/GetFileTime/test2/paltest_getfiletime_test2
-file_io/GetFileTime/test3/paltest_getfiletime_test3
-file_io/GetFileTime/test4/paltest_getfiletime_test4
-file_io/GetFileTime/test5/paltest_getfiletime_test5
-file_io/GetFileTime/test6/paltest_getfiletime_test6
-file_io/GetFileTime/test7/paltest_getfiletime_test7
-file_io/GetFileType/test1/paltest_getfiletype_test1
-file_io/GetFileType/test2/paltest_getfiletype_test2
-file_io/GetFileType/test3/paltest_getfiletype_test3
file_io/GetFullPathNameA/test1/paltest_getfullpathnamea_test1
file_io/GetFullPathNameA/test3/paltest_getfullpathnamea_test3
file_io/GetFullPathNameA/test4/paltest_getfullpathnamea_test4
@@ -611,10 +592,6 @@ file_io/SetFilePointer/test4/paltest_setfilepointer_test4
file_io/SetFilePointer/test5/paltest_setfilepointer_test5
file_io/SetFilePointer/test6/paltest_setfilepointer_test6
file_io/SetFilePointer/test7/paltest_setfilepointer_test7
-file_io/SetFileTime/test1/paltest_setfiletime_test1
-file_io/SetFileTime/test2/paltest_setfiletime_test2
-file_io/SetFileTime/test3/paltest_setfiletime_test3
-file_io/SetFileTime/test4/paltest_setfiletime_test4
file_io/WriteFile/test1/paltest_writefile_test1
file_io/WriteFile/test3/paltest_writefile_test3
file_io/WriteFile/test4/paltest_writefile_test4
@@ -653,7 +630,6 @@ miscellaneous/FormatMessageW/test3/paltest_formatmessagew_test3
miscellaneous/FreeEnvironmentStringsW/test1/paltest_freeenvironmentstringsw_test1
miscellaneous/FreeEnvironmentStringsW/test2/paltest_freeenvironmentstringsw_test2
miscellaneous/GetCommandLineW/test1/paltest_getcommandlinew_test1
-miscellaneous/GetComputerNameW/test1/paltest_getcomputernamew_test1
miscellaneous/GetEnvironmentStringsW/test1/paltest_getenvironmentstringsw_test1
miscellaneous/GetEnvironmentVariableA/test1/paltest_getenvironmentvariablea_test1
miscellaneous/GetEnvironmentVariableA/test2/paltest_getenvironmentvariablea_test2
diff --git a/src/pal/tests/palsuite/paltestlist_to_be_reviewed.txt b/src/pal/tests/palsuite/paltestlist_to_be_reviewed.txt
index 3f9469d254..16be5e6762 100644
--- a/src/pal/tests/palsuite/paltestlist_to_be_reviewed.txt
+++ b/src/pal/tests/palsuite/paltestlist_to_be_reviewed.txt
@@ -22,7 +22,6 @@ c_runtime/vprintf/test1/paltest_vprintf_test1
c_runtime/vswprintf/test2/paltest_vswprintf_test2
c_runtime/vswprintf/test7/paltest_vswprintf_test7
c_runtime/wprintf/test2/paltest_wprintf_test2
-c_runtime/_ecvt/test1/paltest_ecvt_test1
c_runtime/_gcvt/test1/paltest_gcvt_test1
c_runtime/_gcvt/test2/paltest_gcvt_test2
c_runtime/_getw/test1/paltest_getw_test1
@@ -72,11 +71,6 @@ filemapping_memmgt/GetModuleFileNameA/test1/paltest_getmodulefilenamea_test1
filemapping_memmgt/GetModuleFileNameW/test1/paltest_getmodulefilenamew_test1
filemapping_memmgt/GetProcAddress/test1/paltest_getprocaddress_test1
filemapping_memmgt/GetProcAddress/test2/paltest_getprocaddress_test2
-filemapping_memmgt/LockFile/test1/paltest_lockfile_test1
-filemapping_memmgt/LockFile/test3/paltest_lockfile_test3
-filemapping_memmgt/LockFile/test4/paltest_lockfile_test4
-filemapping_memmgt/LockFile/test5/paltest_lockfile_test5
-filemapping_memmgt/LockFile/test6/paltest_lockfile_test6
filemapping_memmgt/OpenFileMappingA/test1/paltest_openfilemappinga_test1
filemapping_memmgt/OpenFileMappingA/test2/paltest_openfilemappinga_test2
filemapping_memmgt/OpenFileMappingA/test3/paltest_openfilemappinga_test3
@@ -86,15 +80,12 @@ filemapping_memmgt/OpenFileMappingW/test3/paltest_openfilemappingw_test3
filemapping_memmgt/ReadProcessMemory/ReadProcessMemory_neg1/paltest_readprocessmemory_readprocessmemory_neg1
filemapping_memmgt/ReadProcessMemory/test1/paltest_readprocessmemory_test1
filemapping_memmgt/ReadProcessMemory/test2/paltest_readprocessmemory_test2
-filemapping_memmgt/UnlockFile/test1/paltest_unlockfile_test1
-filemapping_memmgt/UnlockFile/test3/paltest_unlockfile_test3
file_io/CopyFileW/test1/paltest_copyfilew_test1
file_io/CreateDirectoryA/test2/paltest_createdirectorya_test2
file_io/CreateDirectoryW/test2/paltest_createdirectoryw_test2
file_io/CreateFileA/test1/paltest_createfilea_test1
file_io/CreateFileW/test1/paltest_createfilew_test1
file_io/errorpathnotfound/test1/paltest_errorpathnotfound_test1
-file_io/errorpathnotfound/test4/paltest_errorpathnotfound_test4
file_io/GetFileAttributesExW/test1/paltest_getfileattributesexw_test1
file_io/GetFullPathNameA/test2/paltest_getfullpathnamea_test2
file_io/GetFullPathNameW/test2/paltest_getfullpathnamew_test2
@@ -103,10 +94,8 @@ file_io/GetTempFileNameW/test1/paltest_gettempfilenamew_test1
file_io/GetTempFileNameW/test2/paltest_gettempfilenamew_test2
file_io/gettemppatha/test1/paltest_gettemppatha_test1
file_io/GetTempPathW/test1/paltest_gettemppathw_test1
-file_io/MoveFileA/test1/paltest_movefilea_test1
file_io/MoveFileExA/test1/paltest_movefileexa_test1
file_io/MoveFileExW/test1/paltest_movefileexw_test1
-file_io/MoveFileW/test1/paltest_movefilew_test1
file_io/ReadFile/test1/paltest_readfile_test1
file_io/SetFileAttributesA/test1/paltest_setfileattributesa_test1
file_io/SetFileAttributesA/test4/paltest_setfileattributesa_test4
@@ -139,11 +128,9 @@ miscellaneous/FormatMessageW/test5/paltest_formatmessagew_test5
miscellaneous/FormatMessageW/test6/paltest_formatmessagew_test6
miscellaneous/GetCalendarInfoW/test1/paltest_getcalendarinfow_test1
miscellaneous/GetCalendarInfoW/test2/paltest_getcalendarinfow_test2
-miscellaneous/GetComputerNameW/test1/paltest_getcomputernamew_test1
miscellaneous/GetDateFormatW/GetDateFormatW_neg1/paltest_getdateformatw_getdateformatw_neg1
miscellaneous/GetDateFormatW/GetDateFormatW_neg2/paltest_getdateformatw_getdateformatw_neg2
miscellaneous/GetDateFormatW/test1/paltest_getdateformatw_test1
-miscellaneous/GetUserNameW/test1/paltest_getusernamew_test1
miscellaneous/InterLockedExchangeAdd/test1/paltest_interlockedexchangeadd_test1
miscellaneous/IsBadCodePtr/test1/paltest_isbadcodeptr_test1
miscellaneous/IsBadReadPtr/test1/paltest_isbadreadptr_test1
diff --git a/src/pal/tests/palsuite/palverify.dat b/src/pal/tests/palsuite/palverify.dat
index d4cb311010..3d711c1895 100644
--- a/src/pal/tests/palsuite/palverify.dat
+++ b/src/pal/tests/palsuite/palverify.dat
@@ -4,7 +4,6 @@
c_runtime/__iscsym/test1,1
c_runtime/_alloca/test1,1
-c_runtime/_ecvt/test1,1
c_runtime/_fdopen/test1,1
c_runtime/_finite/test1,1
c_runtime/_finitef/test1,1
@@ -498,7 +497,6 @@ exception_handling/pal_try_leave_finally/test1,1
exception_handling/raiseexception/test1,1
exception_handling/raiseexception/test2,1
exception_handling/raiseexception/test3,1
-file_io/arefileapisansi/test1,1
file_io/comparefiletime/test1,1
file_io/copyfilea/test1,1
file_io/copyfilea/test2,1
@@ -515,32 +513,19 @@ file_io/deletefilew/test1,1
file_io/errorpathnotfound/test1,1
file_io/errorpathnotfound/test2,1
file_io/errorpathnotfound/test3,1
-#file_io/errorpathnotfound/test4,1
-file_io/filetimetodosdatetime/test1,1
file_io/findclose/test1,1
file_io/findfirstfilea/test1,1
#file_io/findfirstfilew/test1,1
file_io/findnextfilea/test1,1
file_io/findnextfilew/test1,1
file_io/flushfilebuffers/test1,1
-file_io/getconsolecp/test1,1
file_io/getconsoleoutputcp/test1,1
file_io/getcurrentdirectorya/test1,1
file_io/getcurrentdirectoryw/test1,1
-file_io/getdiskfreespacew/test1,1
#file_io/getfileattributesa/test1,1
#file_io/getfileattributesexw/test1,1
file_io/getfileattributesexw/test2,1
#file_io/getfileattributesw/test1,1
-file_io/getfiletime/test1,1
-file_io/getfiletime/test2,1
-file_io/getfiletime/test3,1
-file_io/getfiletime/test4,1
-file_io/getfiletime/test6,1
-file_io/getfiletime/test7,1
-file_io/getfiletype/test1,1
-file_io/getfiletype/test2,1
-file_io/getfiletype/test3,1
file_io/getfullpathnamea/test1,1
file_io/getfullpathnamea/test2,1
file_io/getfullpathnamea/test3,1
@@ -560,7 +545,6 @@ file_io/gettempfilenamew/test3,1
file_io/gettemppatha/test1,1
file_io/gettemppathw/test1,1
file_io/movefileexw/test1,1
-file_io/movefilew/test1,1
#file_io/readfile/test1,1
file_io/readfile/test2,1
file_io/readfile/test3,1
@@ -585,10 +569,6 @@ file_io/setfilepointer/test1,1
file_io/setfilepointer/test2,1
file_io/setfilepointer/test3,1
file_io/setfilepointer/test4,1
-file_io/setfiletime/test1,1
-file_io/setfiletime/test2,1
-file_io/setfiletime/test3,1
-file_io/setfiletime/test4,1
file_io/writefile/test1,1
file_io/writefile/test2,1
file_io/writefile/test3,1
diff --git a/src/scripts/genEventPipe.py b/src/scripts/genEventPipe.py
index 4c802acaf3..3caa2071fc 100644
--- a/src/scripts/genEventPipe.py
+++ b/src/scripts/genEventPipe.py
@@ -110,7 +110,7 @@ def generateClrEventPipeWriteEventsImpl(
WriteEventImpl.append(
" EventPipe::WriteEvent(*EventPipeEvent" +
eventName +
- ", nullptr, 0);\n")
+ ", (BYTE*) nullptr, 0);\n")
WriteEventImpl.append("\n return ERROR_SUCCESS;\n}\n\n")
@@ -122,9 +122,9 @@ def generateClrEventPipeWriteEventsImpl(
WriteEventImpl.append(
" EventPipeProvider" +
providerPrettyName +
- " = EventPipe::CreateProvider(" +
+ " = EventPipe::CreateProvider(SL(" +
providerPrettyName +
- "GUID);\n")
+ "Name));\n")
for eventNode in eventNodes:
eventName = eventNode.getAttribute('symbol')
templateName = eventNode.getAttribute('template')
@@ -207,23 +207,9 @@ def generateWriteEventBody(template, providerName, eventName):
return header + code + checking + body + footer
-providerGUIDMap = {}
-providerGUIDMap[
- "{e13c0d23-ccbc-4e12-931b-d9cc2eee27e4}"] = "{0xe13c0d23,0xccbc,0x4e12,{0x93,0x1b,0xd9,0xcc,0x2e,0xee,0x27,0xe4}}"
-providerGUIDMap[
- "{A669021C-C450-4609-A035-5AF59AF4DF18}"] = "{0xA669021C,0xC450,0x4609,{0xA0,0x35,0x5A,0xF5,0x9A,0xF4,0xDF,0x18}}"
-providerGUIDMap[
- "{CC2BCBBA-16B6-4cf3-8990-D74C2E8AF500}"] = "{0xCC2BCBBA,0x16B6,0x4cf3,{0x89,0x90,0xD7,0x4C,0x2E,0x8A,0xF5,0x00}}"
-providerGUIDMap[
- "{763FD754-7086-4dfe-95EB-C01A46FAF4CA}"] = "{0x763FD754,0x7086,0x4dfe,{0x95,0xEB,0xC0,0x1A,0x46,0xFA,0xF4,0xCA}}"
-
-
-def generateGUID(tmpGUID):
- return providerGUIDMap[tmpGUID]
keywordMap = {}
-
def generateEventKeywords(eventKeywords):
mask = 0
# split keywords if there are multiple
@@ -375,8 +361,6 @@ def generateEventPipeImplFiles(
tree = DOM.parse(etwmanifest)
coreclrRoot = os.getcwd()
for providerNode in tree.getElementsByTagName('provider'):
- providerGUID = providerNode.getAttribute('guid')
- providerGUID = generateGUID(providerGUID)
providerName = providerNode.getAttribute('name')
providerPrettyName = providerName.replace("Windows-", '')
@@ -417,15 +401,16 @@ bool WriteToBuffer(const T &value, char *&buffer, unsigned int& offset, unsigned
eventpipeImpl.write(header)
eventpipeImpl.write(
- "GUID const " +
- providerPrettyName +
- "GUID = " +
- providerGUID +
- ";\n")
+ "const WCHAR* %sName = W(\"%s\");\n" % (
+ providerPrettyName,
+ providerName
+ )
+ )
eventpipeImpl.write(
- "EventPipeProvider *EventPipeProvider" +
- providerPrettyName +
- " = nullptr;\n")
+ "EventPipeProvider *EventPipeProvider%s = nullptr;\n" % (
+ providerPrettyName,
+ )
+ )
templateNodes = providerNode.getElementsByTagName('template')
allTemplates = parseTemplateNodes(templateNodes)
eventNodes = providerNode.getElementsByTagName('event')
diff --git a/src/scripts/pgocheck.py b/src/scripts/pgocheck.py
new file mode 100644
index 0000000000..d408e6eaba
--- /dev/null
+++ b/src/scripts/pgocheck.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#
+## 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.
+#
+##
+# Title :pgocheck.py
+#
+# A script to check whether or not a particular portable executable
+# (e.g. EXE, DLL) was compiled using PGO technology
+#
+################################################################################
+
+from glob import glob
+import sys
+import re
+import subprocess
+import argparse
+
+# This pattern matches the line which specifies if PGO, LTCG, or similar techologies were used for compilation
+# coffgrp matches the literal string. It uniquely identifies within the field in question
+# (?:\s+[0-9A-F]+){4} matches 4 hex valued fields without capturing them
+# \((\S*)\) captures the text identifier from the dump output, letting us know the technology
+pgo_pattern_str = r'coffgrp(?:\s+[0-9A-F]+){4}\s+\((\S*)\)'
+pgo_pattern = re.compile(pgo_pattern_str)
+
+def was_compiled_with_pgo(filename):
+ headers = subprocess.check_output(["link", "/dump", "/headers", filename])
+
+ match = pgo_pattern.search(headers)
+
+ result = False
+ tech = "UNKNOWN"
+ if match:
+ result = match.group(1) == 'PGU'
+ tech = match.group(1)
+
+ return result, tech
+
+if __name__ == "__main__":
+ from sys import stdout, stderr
+
+ parser = argparse.ArgumentParser(description="Check if the given PE files were compiled with PGO. Fails if the files were not.")
+ parser.add_argument('files', metavar='file', nargs='+', help="the files to check for PGO flags")
+ parser.add_argument('--negative', action='store_true', help="fail on PGO flags found")
+ parser.add_argument('--quiet', action='store_true', help="don't output; just return a code")
+
+ args = parser.parse_args()
+ # Divide up filenames which are separated by semicolons as well as the ones by spaces. Avoid duplicates
+ filenames = set()
+ for token in args.files:
+ unexpanded_filenames = token.split(';')
+ # Provide support for Unix-style filename expansion (i.e. with * and ?)
+ for unexpanded_filename in unexpanded_filenames:
+ expanded_filenames = glob(unexpanded_filename)
+ if unexpanded_filename and not expanded_filenames:
+ stderr.write("ERROR: Could not find file(s) {0}\n".format(unexpanded_filename))
+ exit(2)
+ filenames.update(expanded_filenames)
+
+ success = True
+ for filename in filenames:
+ result, tech = was_compiled_with_pgo(filename)
+ success = success and result
+
+ if not args.quiet:
+ status = "compiled with PGO" if result else "NOT compiled with PGO"
+ sys.stdout.write("{0}: {1} ({2})\n".format(filename, status, tech))
+
+ if not success:
+ if not args.quiet:
+ if not args.negative:
+ stderr.write("ERROR: The files listed above must be compiled with PGO\n")
+ else:
+ stderr.write("ERROR: The files listed above must NOT be compiled with PGO\n")
+ exit(1) \ No newline at end of file
diff --git a/src/unwinder/unwinder.cpp b/src/unwinder/unwinder.cpp
index 5ab3048905..25ad380b8e 100644
--- a/src/unwinder/unwinder.cpp
+++ b/src/unwinder/unwinder.cpp
@@ -34,7 +34,7 @@ HRESULT OOPStackUnwinder::GetModuleBase( DWORD64 address,
//---------------------------------------------------------------------------------------
//
-// Given a control PC, return the function entry of the functoin it is in.
+// Given a control PC, return the function entry of the function it is in.
//
// Arguments:
// address - the specified IP
diff --git a/src/utilcode/loaderheap.cpp b/src/utilcode/loaderheap.cpp
index 3f1063ce8e..2603a8ec33 100644
--- a/src/utilcode/loaderheap.cpp
+++ b/src/utilcode/loaderheap.cpp
@@ -10,6 +10,9 @@
#define DONOT_DEFINE_ETW_CALLBACK
#include "eventtracebase.h"
+#define LHF_EXECUTABLE 0x1
+#define LHF_ZEROINIT 0x2
+
#ifndef DACCESS_COMPILE
INDEBUG(DWORD UnlockedLoaderHeap::s_dwNumInstancesOfLoaderHeaps = 0;)
@@ -903,7 +906,8 @@ UnlockedLoaderHeap::UnlockedLoaderHeap(DWORD dwReserveBlockSize,
SIZE_T dwReservedRegionSize,
size_t *pPrivatePerfCounter_LoaderBytes,
RangeList *pRangeList,
- BOOL fMakeExecutable)
+ BOOL fMakeExecutable,
+ BOOL fZeroInit)
{
CONTRACTL
{
@@ -939,10 +943,11 @@ UnlockedLoaderHeap::UnlockedLoaderHeap(DWORD dwReserveBlockSize,
m_pPrivatePerfCounter_LoaderBytes = pPrivatePerfCounter_LoaderBytes;
+ m_Options = 0;
if (fMakeExecutable)
- m_flProtect = PAGE_EXECUTE_READWRITE;
- else
- m_flProtect = PAGE_READWRITE;
+ m_Options |= LHF_EXECUTABLE;
+ if (fZeroInit)
+ m_Options |= LHF_ZEROINIT;
m_pFirstFreeBlock = NULL;
@@ -1133,7 +1138,7 @@ BOOL UnlockedLoaderHeap::UnlockedReservePages(size_t dwSizeToCommit)
}
// Commit first set of pages, since it will contain the LoaderHeapBlock
- void *pTemp = ClrVirtualAlloc(pData, dwSizeToCommit, MEM_COMMIT, m_flProtect);
+ void *pTemp = ClrVirtualAlloc(pData, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
if (pTemp == NULL)
{
//_ASSERTE(!"Unable to ClrVirtualAlloc commit in a loaderheap");
@@ -1225,7 +1230,7 @@ BOOL UnlockedLoaderHeap::GetMoreCommittedPages(size_t dwMinSize)
dwSizeToCommit = ALIGN_UP(dwSizeToCommit, GetOsPageSize());
// Yes, so commit the desired number of reserved pages
- void *pData = ClrVirtualAlloc(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, MEM_COMMIT, m_flProtect);
+ void *pData = ClrVirtualAlloc(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
if (pData == NULL)
return FALSE;
@@ -1351,7 +1356,7 @@ again:
// Don't fill the memory we allocated - it is assumed to be zeroed - fill the memory after it
memset(pAllocatedBytes + dwRequestedSize, 0xEE, LOADER_HEAP_DEBUG_BOUNDARY);
#endif
- if (dwRequestedSize > 0)
+ if ((dwRequestedSize > 0) && (m_Options & LHF_ZEROINIT))
{
_ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0,
"LoaderHeap must return zero-initialized memory");
@@ -1529,7 +1534,8 @@ void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem,
{
// Cool. This was the last block allocated. We can just undo the allocation instead
// of going to the freelist.
- memset(pMem, 0, dwSize); // Must zero init this memory as AllocMem expect it
+ if (m_Options & LHF_ZEROINIT)
+ memset(pMem, 0x00, dwSize); // Fill freed region with 0
m_pAllocPtr = (BYTE*)pMem;
}
else
@@ -1639,6 +1645,7 @@ void *UnlockedLoaderHeap::UnlockedAllocAlignedMem_NoThrow(size_t dwRequestedSiz
((BYTE*&)pResult) += extra;
+
#ifdef _DEBUG
BYTE *pAllocatedBytes = (BYTE *)pResult;
#if LOADER_HEAP_DEBUG_BOUNDARY > 0
@@ -1646,7 +1653,7 @@ void *UnlockedLoaderHeap::UnlockedAllocAlignedMem_NoThrow(size_t dwRequestedSiz
memset(pAllocatedBytes + dwRequestedSize, 0xee, LOADER_HEAP_DEBUG_BOUNDARY);
#endif
- if (dwRequestedSize != 0)
+ if ((dwRequestedSize != 0) && (m_Options & LHF_ZEROINIT))
{
_ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0,
"LoaderHeap must return zero-initialized memory");
@@ -1766,6 +1773,16 @@ void *UnlockedLoaderHeap::UnlockedAllocMemForCode_NoThrow(size_t dwHeaderSize, s
#endif // #ifndef DACCESS_COMPILE
+BOOL UnlockedLoaderHeap::IsExecutable()
+{
+ return (m_Options & LHF_EXECUTABLE);
+}
+
+BOOL UnlockedLoaderHeap::IsZeroInit()
+{
+ return (m_Options & LHF_ZEROINIT);
+}
+
#ifdef DACCESS_COMPILE
void UnlockedLoaderHeap::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
diff --git a/src/utilcode/securityutil.cpp b/src/utilcode/securityutil.cpp
index fe15bbddd4..b5032aa9b4 100644
--- a/src/utilcode/securityutil.cpp
+++ b/src/utilcode/securityutil.cpp
@@ -336,7 +336,7 @@ HRESULT SecurityUtil::GetMandatoryLabelFromProcess(HANDLE hProcess, LPBYTE * ppb
HandleHolder hToken;
DWORD err = 0;
- if(!OpenProcessToken(hProcess, TOKEN_READ, &hToken))
+ if(!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
{
return HRESULT_FROM_GetLastError();
}
diff --git a/src/utilcode/util.cpp b/src/utilcode/util.cpp
index f0c6b1c96e..8b2ee6f8ca 100644
--- a/src/utilcode/util.cpp
+++ b/src/utilcode/util.cpp
@@ -870,7 +870,7 @@ BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr,
}
CONTRACTL_END;
-#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_)
+#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
#ifndef FEATURE_PAL
HMODULE hMod = GetModuleHandleW(WINDOWS_KERNEL32_DLLNAME_W);
#else
@@ -907,7 +907,7 @@ BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr,
#endif
}
-#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_)
+#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
// Calculate greatest common divisor
DWORD GCD(DWORD u, DWORD v)
{
@@ -938,7 +938,7 @@ DWORD LCM(DWORD u, DWORD v)
}
CONTRACTL_END;
-#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_)
+#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
BYTE *bBuffer = NULL;
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pSLPIEx = NULL;
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pRecord = NULL;
@@ -1013,7 +1013,7 @@ DWORD LCM(DWORD u, DWORD v)
{
LIMITED_METHOD_CONTRACT;
-#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_)
+#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
WORD begin = 0;
WORD nr_proc = 0;
@@ -1040,7 +1040,7 @@ DWORD LCM(DWORD u, DWORD v)
}
CONTRACTL_END;
-#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_)
+#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
BOOL enableGCCPUGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCCpuGroup) != 0;
BOOL threadUseAllCpuGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Thread_UseAllCpuGroups) != 0;
@@ -1065,7 +1065,7 @@ DWORD LCM(DWORD u, DWORD v)
BOOL hasMultipleGroups = m_nGroups > 1;
m_enableGCCPUGroups = enableGCCPUGroups && hasMultipleGroups;
m_threadUseAllCpuGroups = threadUseAllCpuGroups && hasMultipleGroups;
-#endif // _TARGET_AMD64_
+#endif // _TARGET_AMD64_ || _TARGET_ARM64_
}
/*static*/ BOOL CPUGroupInfo::IsInitialized()
@@ -1123,7 +1123,7 @@ retry:
{
LIMITED_METHOD_CONTRACT;
-#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_)
+#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
WORD bTemp = 0;
WORD bDiff = processor_number - bTemp;
@@ -1154,7 +1154,7 @@ retry:
}
CONTRACTL_END;
-#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_)
+#if !defined(FEATURE_REDHAWK) && (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
// m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
_ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
@@ -1186,7 +1186,7 @@ retry:
}
CONTRACTL_END;
-#if defined(_TARGET_AMD64_)
+#if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
WORD i, minGroup = 0;
DWORD minWeight = 0;
@@ -1229,7 +1229,7 @@ found:
/*static*/ void CPUGroupInfo::ClearCPUGroupAffinity(GROUP_AFFINITY *gf)
{
LIMITED_METHOD_CONTRACT;
-#if defined(_TARGET_AMD64_)
+#if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
// m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
_ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
@@ -1269,32 +1269,43 @@ int GetCurrentProcessCpuCount()
if (cCPUs != 0)
return cCPUs;
+ int count = 0;
DWORD_PTR pmask, smask;
if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
- return 1;
+ {
+ count = 1;
+ }
+ else
+ {
+ pmask &= smask;
- pmask &= smask;
+ while (pmask)
+ {
+ pmask &= (pmask - 1);
+ count++;
+ }
- int count = 0;
- while (pmask)
- {
- pmask &= (pmask - 1);
- count++;
+ // GetProcessAffinityMask can return pmask=0 and smask=0 on systems with more
+ // than 64 processors, which would leave us with a count of 0. Since the GC
+ // expects there to be at least one processor to run on (and thus at least one
+ // heap), we'll return 64 here if count is 0, since there are likely a ton of
+ // processors available in that case. The GC also cannot (currently) handle
+ // the case where there are more than 64 processors, so we will return a
+ // maximum of 64 here.
+ if (count == 0 || count > 64)
+ count = 64;
}
- // GetProcessAffinityMask can return pmask=0 and smask=0 on systems with more
- // than 64 processors, which would leave us with a count of 0. Since the GC
- // expects there to be at least one processor to run on (and thus at least one
- // heap), we'll return 64 here if count is 0, since there are likely a ton of
- // processors available in that case. The GC also cannot (currently) handle
- // the case where there are more than 64 processors, so we will return a
- // maximum of 64 here.
- if (count == 0 || count > 64)
- count = 64;
+#ifdef FEATURE_PAL
+ uint32_t cpuLimit;
+
+ if (PAL_GetCpuLimit(&cpuLimit) && cpuLimit < count)
+ count = cpuLimit;
+#endif
cCPUs = count;
-
+
return count;
}
diff --git a/src/utilcode/winfix.cpp b/src/utilcode/winfix.cpp
index 3a044865ec..c914fb6edd 100644
--- a/src/utilcode/winfix.cpp
+++ b/src/utilcode/winfix.cpp
@@ -426,6 +426,45 @@ lExit:
}
+typedef HRESULT(WINAPI *pfnSetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
+extern pfnSetThreadDescription g_pfnSetThreadDescription;
+
+// Dummy method if windows version does not support it
+HRESULT SetThreadDescriptionDummy(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+ return NOERROR;
+}
+
+HRESULT WINAPI InitializeSetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+ HMODULE hKernel32 = WszLoadLibrary(W("kernel32.dll"));
+
+ pfnSetThreadDescription pLocal = NULL;
+ if (hKernel32 != NULL)
+ {
+ // store to thread local variable to prevent data race
+ pLocal = (pfnSetThreadDescription)GetProcAddress(hKernel32, "SetThreadDescription");
+ }
+
+ if (pLocal == NULL) // method is only available with Windows 10 Creators Update or later
+ {
+ g_pfnSetThreadDescription = SetThreadDescriptionDummy;
+ }
+ else
+ {
+ g_pfnSetThreadDescription = pLocal;
+ }
+
+ return g_pfnSetThreadDescription(hThread, lpThreadDescription);
+}
+
+pfnSetThreadDescription g_pfnSetThreadDescription = &InitializeSetThreadDescription;
+
+// Set unmanaged thread name which will show up in ETW and Debuggers which know how to read this data.
+HRESULT SetThreadName(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+ return g_pfnSetThreadDescription(hThread, lpThreadDescription);
+}
DWORD
WszGetWorkingSet()
diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt
index adb8409558..f8790cf85d 100644
--- a/src/vm/CMakeLists.txt
+++ b/src/vm/CMakeLists.txt
@@ -30,9 +30,12 @@ if(FEATURE_GDBJIT)
set(VM_SOURCES_GDBJIT
gdbjit.cpp
)
- add_definitions(-DFEATURE_GDBJIT)
endif(FEATURE_GDBJIT)
+if(FEATURE_JIT_PITCHING)
+ add_definitions(-DFEATURE_JIT_PITCHING)
+endif(FEATURE_JIT_PITCHING)
+
set(VM_SOURCES_DAC_AND_WKS_COMMON
appdomain.cpp
array.cpp
@@ -44,6 +47,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
classhash.cpp
clsload.cpp
codeman.cpp
+ codeversion.cpp
comdelegate.cpp
contractimpl.cpp
coreassemblyspec.cpp
@@ -75,6 +79,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
generics.cpp
hash.cpp
hillclimbing.cpp
+ ilinstrumentation.cpp
ilstubcache.cpp
ilstubresolver.cpp
inlinetracking.cpp
@@ -95,7 +100,6 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
precode.cpp
prestub.cpp
rejit.cpp
- security.cpp
sigformat.cpp
siginfo.cpp
spinlock.cpp
@@ -120,16 +124,10 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
)
set( GC_SOURCES_DAC_AND_WKS_COMMON
- ../gc/gcconfig.cpp
- ../gc/gccommon.cpp
- ../gc/gcscan.cpp
- ../gc/gcsvr.cpp
- ../gc/gcwks.cpp
../gc/handletable.cpp
../gc/handletablecore.cpp
../gc/handletablescan.cpp
- ../gc/objecthandle.cpp
- ../gc/softwarewritewatch.cpp)
+ ../gc/objecthandle.cpp)
if(FEATURE_READYTORUN)
list(APPEND VM_SOURCES_DAC_AND_WKS_COMMON
@@ -137,6 +135,12 @@ if(FEATURE_READYTORUN)
)
endif(FEATURE_READYTORUN)
+if(FEATURE_JIT_PITCHING)
+ list(APPEND VM_SOURCES_DAC_AND_WKS_COMMON
+ codepitchingmanager.cpp
+ )
+endif(FEATURE_JIT_PITCHING)
+
set(VM_SOURCES_DAC
${VM_SOURCES_DAC_AND_WKS_COMMON}
contexts.cpp
@@ -196,7 +200,6 @@ set(VM_SOURCES_WKS
gccover.cpp
gcenv.ee.static.cpp
gcenv.ee.common.cpp
- gcenv.os.cpp
gchelpers.cpp
genmeth.cpp
hosting.cpp
@@ -207,7 +210,6 @@ set(VM_SOURCES_WKS
interpreter.cpp
invokeutil.cpp
jithelpers.cpp
- listlock.cpp
managedmdimport.cpp
marshalnative.cpp
marvin32.cpp
@@ -260,9 +262,15 @@ set(VM_SOURCES_WKS
set(GC_SOURCES_WKS
${GC_SOURCES_DAC_AND_WKS_COMMON}
+ ../gc/gcconfig.cpp
+ ../gc/gccommon.cpp
+ ../gc/gcscan.cpp
+ ../gc/gcsvr.cpp
+ ../gc/gcwks.cpp
../gc/gchandletable.cpp
../gc/gceesvr.cpp
../gc/gceewks.cpp
+ ../gc/softwarewritewatch.cpp
../gc/handletablecache.cpp)
if(FEATURE_EVENT_TRACE)
@@ -386,6 +394,7 @@ else(WIN32)
if(CLR_CMAKE_TARGET_ARCH_AMD64)
set(VM_SOURCES_WKS_ARCH_ASM
+ ${ARCH_SOURCES_DIR}/asmhelpers.S
${ARCH_SOURCES_DIR}/calldescrworkeramd64.S
${ARCH_SOURCES_DIR}/crthelpers.S
${ARCH_SOURCES_DIR}/externalmethodfixupthunk.S
diff --git a/src/vm/amd64/asmhelpers.S b/src/vm/amd64/asmhelpers.S
new file mode 100644
index 0000000000..78b5185eed
--- /dev/null
+++ b/src/vm/amd64/asmhelpers.S
@@ -0,0 +1,308 @@
+// 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.
+
+.intel_syntax noprefix
+#include "unixasmmacros.inc"
+#include "asmconstants.h"
+
+#define real4 dword
+#define real8 qword
+
+//
+// file: profile.cpp
+// typedef struct _PROFILE_PLATFORM_SPECIFIC_DATA
+// {
+// FunctionID *functionId; // function ID comes in the r11 register
+// void *rbp;
+// void *probersp;
+// void *ip;
+// void *profiledRsp;
+// UINT64 rax;
+// LPVOID hiddenArg;
+// UINT64 flt0;
+// UINT64 flt1;
+// UINT64 flt2;
+// UINT64 flt3;
+// UINT64 flt4;
+// UINT64 flt5;
+// UINT64 flt6;
+// UINT64 flt7;
+// UINT64 rdi;
+// UINT64 rsi;
+// UINT64 rdx;
+// UINT64 rcx;
+// UINT64 r8;
+// UINT64 r9;
+// UINT32 flags;
+// } PROFILE_PLATFORM_SPECIFIC_DATA, *PPROFILE_PLATFORM_SPECIFIC_DATA;
+//
+.equ SIZEOF_PROFILE_PLATFORM_SPECIFIC_DATA, 0x8*22 + 0x8 // includes fudge to make FP_SPILL right
+.equ SIZEOF_FP_ARG_SPILL, 0x10*2
+
+.equ SIZEOF_STACK_FRAME, SIZEOF_PROFILE_PLATFORM_SPECIFIC_DATA + SIZEOF_FP_ARG_SPILL
+
+.equ PROFILE_ENTER, 0x1
+.equ PROFILE_LEAVE, 0x2
+.equ PROFILE_TAILCALL, 0x4
+
+// ***********************************************************
+// NOTE:
+//
+// Register preservation scheme:
+//
+// Preserved:
+// - all non-volatile registers
+// - rax, rdx
+// - xmm0, xmm1
+//
+// Not Preserved:
+// - integer argument registers (rcx, rdx, r8, r9)
+// - floating point argument registers (xmm1-3)
+// - volatile integer registers (r10, r11)
+// - volatile floating point registers (xmm4-5)
+// - upper halves of ymm registers on AVX (which are volatile)
+//
+// ***********************************************************
+
+// EXTERN_C void ProfileEnterNaked(FunctionIDOrClientID functionIDOrClientID, size_t profiledRsp);
+// <NOTE>
+//
+// </NOTE>
+NESTED_ENTRY ProfileEnterNaked, _TEXT, NoHandler
+ // Upon entry :
+ // r14 = clientInfo
+ // r15 = profiledRsp
+
+ push_nonvol_reg rax
+
+ lea rax, [rsp + 0x10] // caller rsp
+ mov r10, [rax - 0x8] // return address
+
+ push_argument_register rdx
+ alloc_stack SIZEOF_STACK_FRAME
+
+ // correctness of return value in structure doesn't matter for enter probe
+
+ // setup ProfilePlatformSpecificData structure
+ xor r11, r11 // nullify r11
+ mov [rsp + 0x0], r11 // r11 is null -- struct functionId field
+ save_reg_postrsp rbp, 0x8 // -- struct rbp field
+ mov [rsp + 0x10], rax // caller rsp -- struct probeRsp field
+ mov [rsp + 0x18], r10 // return address -- struct ip field
+ mov [rsp + 0x20], r15 // -- struct profiledRsp field
+ mov [rsp + 0x28], r11 // return value -- struct rax field
+ mov [rsp + 0x30], r11 // r11 is null -- struct hiddenArg field
+ movsd real8 ptr [rsp + 0x38], xmm0 // -- struct flt0 field
+ movsd real8 ptr [rsp + 0x40], xmm1 // -- struct flt1 field
+ movsd real8 ptr [rsp + 0x48], xmm2 // -- struct flt2 field
+ movsd real8 ptr [rsp + 0x50], xmm3 // -- struct flt3 field
+ movsd real8 ptr [rsp + 0x58], xmm4 // -- struct flt4 field
+ movsd real8 ptr [rsp + 0x60], xmm5 // -- struct flt5 field
+ movsd real8 ptr [rsp + 0x68], xmm6 // -- struct flt6 field
+ movsd real8 ptr [rsp + 0x70], xmm7 // -- struct flt7 field
+ mov [rsp + 0x78], rdi // -- struct rdi field
+ mov [rsp + 0x80], rsi // -- struct rsi field
+ mov [rsp + 0x88], rdx // -- struct rdx field
+ mov [rsp + 0x90], rcx // -- struct rcx field
+ mov [rsp + 0x98], r8 // -- struct r8 field
+ mov [rsp + 0xa0], r9 // -- struct r9 field
+ mov r10, 0x1 // PROFILE_ENTER
+ mov [rsp + 0xa8], r10d // -- struct flags field
+
+ // get aligned stack ptr (rsp + FRAME_SIZE) & (-16)
+ lea rax, [rsp + 0xb8]
+ and rax, -16
+
+ // we need to be able to restore the fp return register
+ // save fp return registers
+ movdqa [rax + 0x00], xmm0
+ movdqa [rax + 0x10], xmm1
+
+ END_PROLOGUE
+
+ // rdi already contains the clientInfo
+ mov rdi, r14
+ lea rsi, [rsp + 0x0]
+ call C_FUNC(ProfileEnter)
+
+ // restore fp return registers
+ lea rax, [rsp + 0xb8]
+ and rax, -16
+ movdqa xmm0, [rax + 0x00]
+ movdqa xmm1, [rax + 0x10]
+
+ // restore arg registers
+ mov rdi, [rsp + 0x78]
+ mov rsi, [rsp + 0x80]
+ mov rdx, [rsp + 0x88]
+ mov rcx, [rsp + 0x90]
+ mov r8, [rsp + 0x98]
+ mov r9, [rsp + 0xa0]
+
+ // begin epilogue
+ free_stack SIZEOF_STACK_FRAME
+ pop_argument_register rdx
+
+ pop_nonvol_reg rax
+
+ ret
+NESTED_END ProfileEnterNaked, _TEXT
+
+// EXTERN_C void ProfileLeaveNaked(FunctionIDOrClientID functionIDOrClientID, size_t profiledRsp);
+// <NOTE>
+//
+// </NOTE>
+NESTED_ENTRY ProfileLeaveNaked, _TEXT, NoHandler
+// Upon entry :
+// rdi = clientInfo
+// rsi = profiledRsp
+
+ push_nonvol_reg rbx
+
+ lea rbx, [rsp + 0x10] // caller rsp
+ mov r10, [rbx - 0x8] // return address
+
+ // rdx should be saved here because it can be used for returning struct values
+ push_argument_register rdx
+ alloc_stack SIZEOF_STACK_FRAME
+
+ // correctness of argument registers in structure doesn't matter for leave probe
+
+ // setup ProfilePlatformSpecificData structure
+ xor r11, r11 // nullify r11
+ mov [rsp + 0x0], r11 // r11 is null -- struct functionId field
+ save_reg_postrsp rbp, 0x8 // -- struct rbp field
+ mov [rsp + 0x10], rbx // caller rsp -- struct probeRsp field
+ mov [rsp + 0x18], r10 // return address -- struct ip field
+ mov [rsp + 0x20], rsi // -- struct profiledRsp field
+ mov [rsp + 0x28], rax // return value -- struct rax field
+ mov [rsp + 0x30], r11 // r11 is null -- struct hiddenArg field
+ movsd real8 ptr [rsp + 0x38], xmm0 // -- struct flt0 field
+ movsd real8 ptr [rsp + 0x40], xmm1 // -- struct flt1 field
+ movsd real8 ptr [rsp + 0x48], xmm2 // -- struct flt2 field
+ movsd real8 ptr [rsp + 0x50], xmm3 // -- struct flt3 field
+ movsd real8 ptr [rsp + 0x58], xmm4 // -- struct flt4 field
+ movsd real8 ptr [rsp + 0x60], xmm5 // -- struct flt5 field
+ movsd real8 ptr [rsp + 0x68], xmm6 // -- struct flt6 field
+ movsd real8 ptr [rsp + 0x70], xmm7 // -- struct flt7 field
+ mov [rsp + 0x78], r11 // -- struct rdi field
+ mov [rsp + 0x80], r11 // -- struct rsi field
+ mov [rsp + 0x88], r11 // -- struct rdx field
+ mov [rsp + 0x90], r11 // -- struct rcx field
+ mov [rsp + 0x98], r11 // -- struct r8 field
+ mov [rsp + 0xa0], r11 // -- struct r9 field
+ mov r10, 0x2 // PROFILE_LEAVE
+ mov [rsp + 0xa8], r10d // flags -- struct flags field
+
+ // get aligned stack ptr (rsp + FRAME_SIZE) & (-16)
+ lea rax, [rsp + 0xb8]
+ and rax, -16
+
+ // we need to be able to restore the fp return register
+ // save fp return registers
+ movdqa [rax + 0x00], xmm0
+ movdqa [rax + 0x10], xmm1
+
+ END_PROLOGUE
+
+ // rdi already contains the clientInfo
+ lea rsi, [rsp + 0x0]
+ call C_FUNC(ProfileLeave)
+
+ // restore fp return registers
+ lea rax, [rsp + 0xb8]
+ and rax, -16
+ movdqa xmm0, [rax + 0x00]
+ movdqa xmm1, [rax + 0x10]
+
+ // restore int return register
+ mov rax, [rsp + 0x28]
+
+ // begin epilogue
+ free_stack SIZEOF_STACK_FRAME
+ pop_argument_register rdx
+
+ pop_nonvol_reg rbx
+
+ ret
+NESTED_END ProfileLeaveNaked, _TEXT
+
+// EXTERN_C void ProfileTailcallNaked(FunctionIDOrClientID functionIDOrClientID, size_t profiledRsp);
+// <NOTE>
+//
+// </NOTE>
+NESTED_ENTRY ProfileTailcallNaked, _TEXT, NoHandler
+// Upon entry :
+// rdi = clientInfo
+// rsi = profiledRsp
+
+ push_nonvol_reg rbx
+
+ lea rbx, [rsp + 0x10] // caller rsp
+ mov r10, [rbx - 0x8] // return address
+
+ // rdx should be saved here because it can be used for returning struct values
+ push_argument_register rdx
+ alloc_stack SIZEOF_STACK_FRAME
+
+ // correctness of argument registers in structure doesn't matter for tailcall probe
+
+ // setup ProfilePlatformSpecificData structure
+ xor r11, r11 // nullify r11
+ mov [rsp + 0x0], r11 // r11 is null -- struct functionId field
+ save_reg_postrsp rbp, 0x8 // -- struct rbp field
+ mov [rsp + 0x10], rbx // caller rsp -- struct probeRsp field
+ mov [rsp + 0x18], r10 // return address -- struct ip field
+ mov [rsp + 0x20], rsi // -- struct profiledRsp field
+ mov [rsp + 0x28], rax // return value -- struct rax field
+ mov [rsp + 0x30], r11 // r11 is null -- struct hiddenArg field
+ movsd real8 ptr [rsp + 0x38], xmm0 // -- struct flt0 field
+ movsd real8 ptr [rsp + 0x40], xmm1 // -- struct flt1 field
+ movsd real8 ptr [rsp + 0x48], xmm2 // -- struct flt2 field
+ movsd real8 ptr [rsp + 0x50], xmm3 // -- struct flt3 field
+ movsd real8 ptr [rsp + 0x58], xmm4 // -- struct flt4 field
+ movsd real8 ptr [rsp + 0x60], xmm5 // -- struct flt5 field
+ movsd real8 ptr [rsp + 0x68], xmm6 // -- struct flt6 field
+ movsd real8 ptr [rsp + 0x70], xmm7 // -- struct flt7 field
+ mov [rsp + 0x78], r11 // -- struct rdi field
+ mov [rsp + 0x80], r11 // -- struct rsi field
+ mov [rsp + 0x88], r11 // -- struct rdx field
+ mov [rsp + 0x90], r11 // -- struct rcx field
+ mov [rsp + 0x98], r11 // -- struct r8 field
+ mov [rsp + 0xa0], r11 // -- struct r9 field
+ mov r10, 0x2 // PROFILE_LEAVE
+ mov [rsp + 0xa8], r10d // flags -- struct flags field
+
+ // get aligned stack ptr (rsp + FRAME_SIZE) & (-16)
+ lea rax, [rsp + 0xc0]
+ and rax, -16
+
+ // we need to be able to restore the fp return register
+ // save fp return registers
+ movdqa [rax + 0x00], xmm0
+ movdqa [rax + 0x10], xmm1
+
+ END_PROLOGUE
+
+ // rdi already contains the clientInfo
+ lea rsi, [rsp + 0x0]
+ call C_FUNC(ProfileTailcall)
+
+ // restore fp return registers
+ lea rax, [rsp + 0xc0]
+ and rax, -16
+ movdqa xmm0, [rax + 0x00]
+ movdqa xmm1, [rax + 0x10]
+
+ // restore int return register
+ mov rax, [rsp + 0x28]
+
+ // begin epilogue
+ free_stack SIZEOF_STACK_FRAME
+ pop_argument_register rdx
+
+ pop_nonvol_reg rbx
+
+ ret
+NESTED_END ProfileTailcallNaked, _TEXT
diff --git a/src/vm/amd64/cgenamd64.cpp b/src/vm/amd64/cgenamd64.cpp
index 497abcd502..20dca22e36 100644
--- a/src/vm/amd64/cgenamd64.cpp
+++ b/src/vm/amd64/cgenamd64.cpp
@@ -670,6 +670,19 @@ void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
_ASSERTE(DbgIsExecutable(&m_movR10[0], &m_jmpRAX[3]-&m_movR10[0]));
}
+void UMEntryThunkCode::Poison()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_movR10[0] = X86_INSTR_INT3;
+}
+
UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback)
{
LIMITED_METHOD_CONTRACT;
diff --git a/src/vm/amd64/cgencpu.h b/src/vm/amd64/cgencpu.h
index 64a6501dc0..b74e3ca7d3 100644
--- a/src/vm/amd64/cgencpu.h
+++ b/src/vm/amd64/cgencpu.h
@@ -472,6 +472,7 @@ struct DECLSPEC_ALIGN(8) UMEntryThunkCode
BYTE m_padding2[5];
void Encode(BYTE* pTargetCode, void* pvSecretParam);
+ void Poison();
LPCBYTE GetEntryPoint() const
{
diff --git a/src/vm/amd64/profiler.cpp b/src/vm/amd64/profiler.cpp
index e88cbba9ee..d43df944d6 100644
--- a/src/vm/amd64/profiler.cpp
+++ b/src/vm/amd64/profiler.cpp
@@ -36,6 +36,18 @@ typedef struct _PROFILE_PLATFORM_SPECIFIC_DATA
UINT64 flt1;
UINT64 flt2;
UINT64 flt3;
+#if defined(UNIX_AMD64_ABI)
+ UINT64 flt4;
+ UINT64 flt5;
+ UINT64 flt6;
+ UINT64 flt7;
+ UINT64 rdi;
+ UINT64 rsi;
+ UINT64 rdx;
+ UINT64 rcx;
+ UINT64 r8;
+ UINT64 r9;
+#endif
UINT32 flags;
} PROFILE_PLATFORM_SPECIFIC_DATA, *PPROFILE_PLATFORM_SPECIFIC_DATA;
@@ -157,7 +169,16 @@ ProfileArgIterator::ProfileArgIterator(MetaSig * pSig, void * platformSpecificHa
index++;
}
+#ifdef UNIX_AMD64_ABI
+ switch (index)
+ {
+ case 0: pData->hiddenArg = (LPVOID)pData->rdi; break;
+ case 1: pData->hiddenArg = (LPVOID)pData->rsi; break;
+ case 2: pData->hiddenArg = (LPVOID)pData->rdx; break;
+ }
+#else
pData->hiddenArg = *(LPVOID*)((LPBYTE)pData->profiledRsp + (index * sizeof(SIZE_T)));
+#endif // UNIX_AMD64_ABI
}
}
else
@@ -309,7 +330,11 @@ LPVOID ProfileArgIterator::GetThis(void)
{
if (m_argIterator.HasThis())
{
+#ifdef UNIX_AMD64_ABI
+ return (LPVOID)pData->rdi;
+#else
return *(LPVOID*)((LPBYTE)pData->profiledRsp);
+#endif // UNIX_AMD64_ABI
}
}
diff --git a/src/vm/amd64/unixstubs.cpp b/src/vm/amd64/unixstubs.cpp
index 76d3cf1890..83764e0a22 100644
--- a/src/vm/amd64/unixstubs.cpp
+++ b/src/vm/amd64/unixstubs.cpp
@@ -11,21 +11,6 @@ extern "C"
PORTABILITY_ASSERT("Implement for PAL");
}
- void ProfileEnterNaked(FunctionIDOrClientID functionIDOrClientID)
- {
- PORTABILITY_ASSERT("Implement for PAL");
- }
-
- void ProfileLeaveNaked(FunctionIDOrClientID functionIDOrClientID)
- {
- PORTABILITY_ASSERT("Implement for PAL");
- }
-
- void ProfileTailcallNaked(FunctionIDOrClientID functionIDOrClientID)
- {
- PORTABILITY_ASSERT("Implement for PAL");
- }
-
DWORD getcpuid(DWORD arg, unsigned char result[16])
{
DWORD eax;
diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp
index 946009ac06..7b0da7f5a2 100644
--- a/src/vm/appdomain.cpp
+++ b/src/vm/appdomain.cpp
@@ -8,7 +8,6 @@
#include "appdomain.hpp"
#include "peimagelayout.inl"
#include "field.h"
-#include "security.h"
#include "strongnameinternal.h"
#include "excep.h"
#include "eeconfig.h"
@@ -58,7 +57,6 @@
#include "typeequivalencehash.hpp"
#endif
-#include "listlock.inl"
#include "appdomain.inl"
#include "typeparse.h"
#include "mdaassistants.h"
@@ -80,12 +78,10 @@
#include "clrprivtypecachewinrt.h"
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
#pragma warning(push)
#pragma warning(disable:4324)
#include "marvin32.h"
#pragma warning(pop)
-#endif
// this file handles string conversion errors for itself
#undef MAKE_TRANSLATIONFAILED
@@ -711,10 +707,6 @@ OBJECTHANDLE ThreadStaticHandleTable::AllocateHandles(DWORD nRequested)
//*****************************************************************************
void BaseDomain::Attach()
{
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
- // Randomized string hashing is on by default for String.GetHashCode in coreclr.
- COMNlsHashProvider::s_NlsHashProvider.SetUseRandomHashing((CorHost2::GetStartupFlags() & STARTUP_DISABLE_RANDOMIZED_STRING_HASHING) == 0);
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
m_SpecialStaticsCrst.Init(CrstSpecialStatics);
}
@@ -758,8 +750,8 @@ BaseDomain::BaseDomain()
m_ClassInitLock.PreInit();
m_ILStubGenLock.PreInit();
-#ifdef FEATURE_REJIT
- m_reJitMgr.PreInit(this == (BaseDomain *) g_pSharedDomainMemory);
+#ifdef FEATURE_CODE_VERSIONING
+ m_codeVersionManager.PreInit(this == (BaseDomain *)g_pSharedDomainMemory);
#endif
} //BaseDomain::BaseDomain
@@ -874,22 +866,23 @@ void BaseDomain::Terminate()
m_DomainLocalBlockCrst.Destroy();
m_InteropDataCrst.Destroy();
+ JitListLockEntry* pJitElement;
ListLockEntry* pElement;
// All the threads that are in this domain had better be stopped by this
// point.
//
// We might be jitting or running a .cctor so we need to empty that queue.
- pElement = m_JITLock.Pop(TRUE);
- while (pElement)
+ pJitElement = m_JITLock.Pop(TRUE);
+ while (pJitElement)
{
#ifdef STRICT_JITLOCK_ENTRY_LEAK_DETECTION
_ASSERTE ((m_JITLock.m_pHead->m_dwRefCount == 1
&& m_JITLock.m_pHead->m_hrResultCode == E_FAIL) ||
dbg_fDrasticShutdown || g_fInControlC);
#endif // STRICT_JITLOCK_ENTRY_LEAK_DETECTION
- delete(pElement);
- pElement = m_JITLock.Pop(TRUE);
+ delete(pJitElement);
+ pJitElement = m_JITLock.Pop(TRUE);
}
m_JITLock.Destroy();
@@ -2715,9 +2708,6 @@ void SystemDomain::LoadBaseSystemClasses()
// Load Object
g_pObjectClass = MscorlibBinder::GetClass(CLASS__OBJECT);
- // get the Object::.ctor method desc so we can special-case it
- g_pObjectCtorMD = MscorlibBinder::GetMethod(METHOD__OBJECT__CTOR);
-
// Now that ObjectClass is loaded, we can set up
// the system for finalizers. There is no point in deferring this, since we need
// to know this before we allocate our first object.
@@ -3741,10 +3731,6 @@ StackWalkAction SystemDomain::CallersMethodCallback(CrawlFrame* pCf, VOID* data)
/* We asked to be called back only for functions */
_ASSERTE(pFunc);
- // Ignore intercepted frames
- if(pFunc->IsInterceptedForDeclSecurity())
- return SWA_CONTINUE;
-
CallersData* pCaller = (CallersData*) data;
if(pCaller->skip == 0) {
pCaller->pMethod = pFunc;
@@ -4280,7 +4266,6 @@ void AppDomain::Init()
#endif //FEATURE_COMINTEROP
#ifdef FEATURE_TIERED_COMPILATION
- m_callCounter.SetTieredCompilationManager(GetTieredCompilationManager());
m_tieredCompilationManager.Init(GetId());
#endif
#endif // CROSSGEN_COMPILE
@@ -5040,7 +5025,7 @@ FileLoadLock::~FileLoadLock()
MODE_ANY;
}
CONTRACTL_END;
- ((PEFile *) m_pData)->Release();
+ ((PEFile *) m_data)->Release();
}
DomainFile *FileLoadLock::GetDomainFile()
@@ -7078,18 +7063,26 @@ EndTry2:;
}
else if (!fIsWellKnown)
{
- // Trigger the resolve event also for non-throw situation.
- // However, this code path will behave as if the resolve handler has thrown,
- // that is, not trigger an MDA.
_ASSERTE(fThrowOnFileNotFound == FALSE);
- AssemblySpec NewSpec(this);
- AssemblySpec *pFailedSpec = NULL;
+ // Don't trigger the resolve event for the CoreLib satellite assembly. A misbehaving resolve event may
+ // return an assembly that does not match, and this can cause recursive resource lookups during error
+ // reporting. The CoreLib satellite assembly is loaded from relative locations based on the culture, see
+ // AssemblySpec::Bind().
+ if (!pSpec->IsMscorlibSatellite())
+ {
+ // Trigger the resolve event also for non-throw situation.
+ // However, this code path will behave as if the resolve handler has thrown,
+ // that is, not trigger an MDA.
+
+ AssemblySpec NewSpec(this);
+ AssemblySpec *pFailedSpec = NULL;
- fForceReThrow = TRUE; // Managed resolve event handler can throw
+ fForceReThrow = TRUE; // Managed resolve event handler can throw
- // Purposly ignore return value
- PostBindResolveAssembly(pSpec, &NewSpec, hrBindResult, &pFailedSpec);
+ // Purposly ignore return value
+ PostBindResolveAssembly(pSpec, &NewSpec, hrBindResult, &pFailedSpec);
+ }
}
}
}
@@ -8104,7 +8097,7 @@ void AppDomain::Exit(BOOL fRunFinalizers, BOOL fAsyncExit)
// have exited the domain.
//
#ifdef FEATURE_TIERED_COMPILATION
- m_tieredCompilationManager.OnAppDomainShutdown();
+ m_tieredCompilationManager.Shutdown(FALSE);
#endif
//
@@ -8145,14 +8138,14 @@ void AppDomain::Exit(BOOL fRunFinalizers, BOOL fAsyncExit)
LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Domain [%d] %#08x %ls is exited.\n",
GetId().m_dwId, this, GetFriendlyNameForLogging()));
- ReJitManager::OnAppDomainExit(this);
-
// Send ETW events for this domain's unload and potentially iterate through this
// domain's modules & assemblies to send events for their unloads as well. This
// needs to occur before STAGE_FINALIZED (to ensure everything is there), so we do
// this before any finalization occurs at all.
ETW::LoaderLog::DomainUnload(this);
+ CodeVersionManager::OnAppDomainExit(this);
+
//
// Spin running finalizers until we flush them all. We need to make multiple passes
// in case the finalizers create more finalizable objects. This is important to clear
diff --git a/src/vm/appdomain.hpp b/src/vm/appdomain.hpp
index 18bc73e5a5..c5af6e79bc 100644
--- a/src/vm/appdomain.hpp
+++ b/src/vm/appdomain.hpp
@@ -49,6 +49,8 @@
#include "callcounter.h"
#endif
+#include "codeversion.h"
+
class BaseDomain;
class SystemDomain;
class SharedDomain;
@@ -839,7 +841,7 @@ public:
pEntry != NULL;
pEntry = pEntry->m_pNext)
{
- if (((PEFile *)pEntry->m_pData)->Equals(pFile))
+ if (((PEFile *)pEntry->m_data)->Equals(pFile))
{
return pEntry;
}
@@ -949,6 +951,9 @@ typedef FileLoadLock::Holder FileLoadLockHolder;
typedef ReleaseHolder<FileLoadLock> FileLoadLockRefHolder;
#endif // DACCESS_COMPILE
+ typedef ListLockBase<NativeCodeVersion> JitListLock;
+ typedef ListLockEntryBase<NativeCodeVersion> JitListLockEntry;
+
#ifdef _MSC_VER
#pragma warning(push)
@@ -1204,7 +1209,7 @@ public:
return &m_ClassInitLock;
}
- ListLock* GetJitLock()
+ JitListLock* GetJitLock()
{
LIMITED_METHOD_CONTRACT;
return &m_JITLock;
@@ -1398,7 +1403,7 @@ protected:
CrstExplicitInit m_crstAssemblyList;
BOOL m_fDisableInterfaceCache; // RCW COM interface cache
ListLock m_ClassInitLock;
- ListLock m_JITLock;
+ JitListLock m_JITLock;
ListLock m_ILStubGenLock;
// Fusion context, used for adding assemblies to the is domain. It defines
@@ -1547,12 +1552,21 @@ public:
return m_dwSizedRefHandles;
}
- // Profiler rejit
+#ifdef FEATURE_CODE_VERSIONING
+private:
+ CodeVersionManager m_codeVersionManager;
+
+public:
+ CodeVersionManager* GetCodeVersionManager() { return &m_codeVersionManager; }
+#endif //FEATURE_CODE_VERSIONING
+
+#ifdef FEATURE_TIERED_COMPILATION
private:
- ReJitManager m_reJitMgr;
+ CallCounter m_callCounter;
public:
- ReJitManager * GetReJitManager() { return &m_reJitMgr; }
+ CallCounter* GetCallCounter() { return &m_callCounter; }
+#endif
#ifdef DACCESS_COMPILE
public:
@@ -3823,15 +3837,6 @@ public:
private:
TieredCompilationManager m_tieredCompilationManager;
-public:
- CallCounter * GetCallCounter()
- {
- LIMITED_METHOD_CONTRACT;
- return &m_callCounter;
- }
-
-private:
- CallCounter m_callCounter;
#endif
#ifdef FEATURE_COMINTEROP
diff --git a/src/vm/appdomainnative.cpp b/src/vm/appdomainnative.cpp
index 41259897c0..0ee950c85b 100644
--- a/src/vm/appdomainnative.cpp
+++ b/src/vm/appdomainnative.cpp
@@ -7,7 +7,6 @@
#include "common.h"
#include "appdomain.hpp"
#include "appdomainnative.hpp"
-#include "security.h"
#include "vars.hpp"
#include "eeconfig.h"
#include "appdomain.inl"
diff --git a/src/vm/arm/armsinglestepper.cpp b/src/vm/arm/armsinglestepper.cpp
index e000959ef9..bfe88244f8 100644
--- a/src/vm/arm/armsinglestepper.cpp
+++ b/src/vm/arm/armsinglestepper.cpp
@@ -97,17 +97,25 @@ ArmSingleStepper::ArmSingleStepper()
ArmSingleStepper::~ArmSingleStepper()
{
-#if !defined(DACCESS_COMPILE) && !defined(FEATURE_PAL)
+#if !defined(DACCESS_COMPILE)
+#ifdef FEATURE_PAL
+ SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->BackoutMem(m_rgCode, kMaxCodeBuffer * sizeof(WORD));
+#else
DeleteExecutable(m_rgCode);
#endif
+#endif
}
void ArmSingleStepper::Init()
{
-#if !defined(DACCESS_COMPILE) && !defined(FEATURE_PAL)
+#if !defined(DACCESS_COMPILE)
if (m_rgCode == NULL)
{
+#ifdef FEATURE_PAL
+ m_rgCode = (WORD *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(kMaxCodeBuffer * sizeof(WORD)));
+#else
m_rgCode = new (executable) WORD[kMaxCodeBuffer];
+#endif
}
#endif
}
diff --git a/src/vm/arm/cgencpu.h b/src/vm/arm/cgencpu.h
index 181d5f10eb..2a369d8f02 100644
--- a/src/vm/arm/cgencpu.h
+++ b/src/vm/arm/cgencpu.h
@@ -566,7 +566,11 @@ public:
// a reasonable breakpoint substitute (it's what DebugBreak uses). Bkpt #0, on the other hand, always
// seems to flow directly to the kernel debugger (even if we ignore it there it doesn't seem to be
// picked up by the user mode debugger).
+#ifdef __linux__
+ Emit16(0xde01);
+#else
Emit16(0xdefe);
+#endif
}
void ThumbEmitMovConstant(ThumbReg dest, int constant)
@@ -988,6 +992,7 @@ struct DECLSPEC_ALIGN(4) UMEntryThunkCode
TADDR m_pvSecretParam;
void Encode(BYTE* pTargetCode, void* pvSecretParam);
+ void Poison();
LPCBYTE GetEntryPoint() const
{
diff --git a/src/vm/arm/stubs.cpp b/src/vm/arm/stubs.cpp
index 2e8bb19d49..4bc1b2c1ea 100644
--- a/src/vm/arm/stubs.cpp
+++ b/src/vm/arm/stubs.cpp
@@ -19,7 +19,6 @@
#include "eeconfig.h"
#include "cgensys.h"
#include "asmconstants.h"
-#include "security.h"
#include "virtualcallstub.h"
#include "gcdump.h"
#include "rtlfunctions.h"
@@ -2522,6 +2521,12 @@ void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
FlushInstructionCache(GetCurrentProcess(),&m_code,sizeof(m_code));
}
+void UMEntryThunkCode::Poison()
+{
+ // Insert 'udf 0xff' at the entry point
+ m_code[0] = 0xdeff;
+}
+
///////////////////////////// UNIMPLEMENTED //////////////////////////////////
#ifndef DACCESS_COMPILE
@@ -3599,10 +3604,8 @@ PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCOD
END_DYNAMIC_HELPER_EMIT();
}
-PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
+void DynamicHelpers::EmitHelperWithArg(BYTE*& p, LoaderAllocator * pAllocator, TADDR arg, PCODE target)
{
- BEGIN_DYNAMIC_HELPER_EMIT(18);
-
// mov r1, arg
MovRegImm(p, 1, arg);
p += 8;
@@ -3614,6 +3617,13 @@ PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR ar
// bx r12
*(WORD *)p = 0x4760;
p += 2;
+}
+
+PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
+{
+ BEGIN_DYNAMIC_HELPER_EMIT(18);
+
+ EmitHelperWithArg(p, pAllocator, arg, target);
END_DYNAMIC_HELPER_EMIT();
}
@@ -3762,8 +3772,26 @@ PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator,
{
STANDARD_VM_CONTRACT;
- // TODO (NYI)
- ThrowHR(E_NOTIMPL);
+ PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
+ GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
+ GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
+
+ GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
+ pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot;
+ pArgs->signature = pLookup->signature;
+ pArgs->module = (CORINFO_MODULE_HANDLE)pModule;
+
+ // It's available only via the run-time helper function,
+ // since optimization cases are not yet implemented.
+ assert(pLookup->indirections == CORINFO_USEHELPER);
+
+ BEGIN_DYNAMIC_HELPER_EMIT(18);
+
+ EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
+
+ END_DYNAMIC_HELPER_EMIT();
+
+ // @TODO : Additional implementation is required for optimization cases.
}
#endif // FEATURE_READYTORUN
diff --git a/src/vm/arm64/asmconstants.h b/src/vm/arm64/asmconstants.h
index 12b72f9249..dca845d000 100644
--- a/src/vm/arm64/asmconstants.h
+++ b/src/vm/arm64/asmconstants.h
@@ -23,6 +23,13 @@
#define ASMCONSTANTS_RUNTIME_ASSERT(cond)
#endif
+// Some contants are different in _DEBUG builds. This macro factors out ifdefs from below.
+#ifdef _DEBUG
+#define DBG_FRE(dbg,fre) dbg
+#else
+#define DBG_FRE(dbg,fre) fre
+#endif
+
#define DynamicHelperFrameFlags_Default 0
#define DynamicHelperFrameFlags_ObjectArg 1
#define DynamicHelperFrameFlags_ObjectArg2 2
@@ -75,6 +82,11 @@ ASMCONSTANTS_C_ASSERT( CORINFO_NullReferenceException_ASM
== CORINFO_NullReferenceException);
+#define CORINFO_IndexOutOfRangeException_ASM 3
+ASMCONSTANTS_C_ASSERT( CORINFO_IndexOutOfRangeException_ASM
+ == CORINFO_IndexOutOfRangeException);
+
+
// Offset of the array containing the address of captured registers in MachState
#define MachState__captureX19_X29 0x0
ASMCONSTANTS_C_ASSERT(MachState__captureX19_X29 == offsetof(MachState, captureX19_X29))
@@ -114,6 +126,28 @@ ASMCONSTANTS_C_ASSERT(SIZEOF__Frame == sizeof(Frame));
ASMCONSTANTS_C_ASSERT(SIZEOF__CONTEXT == sizeof(T_CONTEXT));
+//=========================================
+#define MethodTable__m_dwFlags 0x0
+ASMCONSTANTS_C_ASSERT(MethodTable__m_dwFlags == offsetof(MethodTable, m_dwFlags));
+
+#define MethodTable__m_BaseSize 0x04
+ASMCONSTANTS_C_ASSERT(MethodTable__m_BaseSize == offsetof(MethodTable, m_BaseSize));
+
+#define MethodTable__m_ElementType DBG_FRE(0x38, 0x30)
+ASMCONSTANTS_C_ASSERT(MethodTable__m_ElementType == offsetof(MethodTable, m_pMultipurposeSlot1));
+
+#define ArrayBase__m_NumComponents 0x8
+ASMCONSTANTS_C_ASSERT(ArrayBase__m_NumComponents == offsetof(ArrayBase, m_NumComponents));
+
+#define PtrArray__m_Array 0x10
+ASMCONSTANTS_C_ASSERT(PtrArray__m_Array == offsetof(PtrArray, m_Array));
+
+#define TypeHandle_CanCast 0x1 // TypeHandle::CanCast
+
+//=========================================
+
+
+
#ifdef FEATURE_COMINTEROP
#define SIZEOF__ComMethodFrame 0x70
diff --git a/src/vm/arm64/asmhelpers.S b/src/vm/arm64/asmhelpers.S
index 2e1d0299ed..8179388c34 100644
--- a/src/vm/arm64/asmhelpers.S
+++ b/src/vm/arm64/asmhelpers.S
@@ -207,6 +207,106 @@ LEAF_END ThePreStubPatch, _TEXT
LEAF_END_MARKED \name, _TEXT
.endmacro
+// ------------------------------------------------------------------
+// Start of the writeable code region
+LEAF_ENTRY JIT_PatchedCodeStart, _TEXT
+ ret lr
+LEAF_END JIT_PatchedCodeStart, _TEXT
+
+// void JIT_UpdateWriteBarrierState(bool skipEphemeralCheck)
+//
+// Update shadow copies of the various state info required for barrier
+//
+// State info is contained in a literal pool at the end of the function
+// Placed in text section so that it is close enough to use ldr literal and still
+// be relocatable. Eliminates need for PREPARE_EXTERNAL_VAR in hot code.
+//
+// Align and group state info together so it fits in a single cache line
+// and each entry can be written atomically
+//
+WRITE_BARRIER_ENTRY JIT_UpdateWriteBarrierState
+ PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -16
+
+ // x0-x7 will contain intended new state
+ // x8 will preserve skipEphemeralCheck
+ // x12 will be used for pointers
+
+ mov x8, x0
+
+ PREPARE_EXTERNAL_VAR g_card_table, x12
+ ldr x0, [x12]
+
+#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
+ PREPARE_EXTERNAL_VAR g_card_bundle_table, x12
+ ldr x1, [x12]
+#endif
+
+#ifdef WRITE_BARRIER_CHECK
+ PREPARE_EXTERNAL_VAR g_GCShadow, x12
+ ldr x2, [x12]
+#endif
+
+#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
+ PREPARE_EXTERNAL_VAR g_sw_ww_table, x12
+ ldr x3, [x12]
+#endif
+
+ PREPARE_EXTERNAL_VAR g_ephemeral_low, x12
+ ldr x4, [x12]
+
+ PREPARE_EXTERNAL_VAR g_ephemeral_high, x12
+ ldr x5, [x12]
+
+ cbz x8, LOCAL_LABEL(EphemeralCheckEnabled)
+ movz x4, #0
+ movn x5, #0
+LOCAL_LABEL(EphemeralCheckEnabled):
+
+ PREPARE_EXTERNAL_VAR g_lowest_address, x12
+ ldr x6, [x12]
+
+ PREPARE_EXTERNAL_VAR g_highest_address, x12
+ ldr x7, [x12]
+
+ // Update wbs state
+ adr x12, LOCAL_LABEL(wbs_begin)
+
+ stp x0, x1, [x12], 16
+ stp x2, x3, [x12], 16
+ stp x4, x5, [x12], 16
+ stp x6, x7, [x12], 16
+
+ EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 16
+ EPILOG_RETURN
+
+ // Begin patchable literal pool
+ .balign 64 // Align to power of two at least as big as patchable literal pool so that it fits optimally in cache line
+LOCAL_LABEL(wbs_begin):
+LOCAL_LABEL(wbs_card_table):
+ .quad 0
+LOCAL_LABEL(wbs_card_bundle_table):
+ .quad 0
+LOCAL_LABEL(wbs_GCShadow):
+ .quad 0
+LOCAL_LABEL(wbs_sw_ww_table):
+ .quad 0
+LOCAL_LABEL(wbs_ephemeral_low):
+ .quad 0
+LOCAL_LABEL(wbs_ephemeral_high):
+ .quad 0
+LOCAL_LABEL(wbs_lowest_address):
+ .quad 0
+LOCAL_LABEL(wbs_highest_address):
+ .quad 0
+WRITE_BARRIER_END JIT_UpdateWriteBarrierState
+
+
+// ------------------------------------------------------------------
+// End of the writeable code region
+LEAF_ENTRY JIT_PatchedCodeLast, _TEXT
+ ret lr
+LEAF_END JIT_PatchedCodeLast, _TEXT
+
// void JIT_ByRefWriteBarrier
// On entry:
// x13 : the source address (points to object reference to write)
@@ -235,19 +335,16 @@ WRITE_BARRIER_END JIT_ByRefWriteBarrier
//
// On exit:
// x12 : trashed
-// x14 : incremented by 8
+// x14 : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract)
// x15 : trashed
// x17 : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
//
WRITE_BARRIER_ENTRY JIT_CheckedWriteBarrier
- PREPARE_EXTERNAL_VAR g_lowest_address, x12
- ldr x12, [x12]
+ ldr x12, LOCAL_LABEL(wbs_lowest_address)
cmp x14, x12
- blt LOCAL_LABEL(NotInHeap)
- PREPARE_EXTERNAL_VAR g_highest_address, x12
- ldr x12, [x12]
- cmp x14, x12
+ ldr x12, LOCAL_LABEL(wbs_highest_address)
+ ccmp x14, x12, #0x0, ge
blt C_FUNC(JIT_WriteBarrier)
LOCAL_LABEL(NotInHeap):
@@ -262,7 +359,7 @@ WRITE_BARRIER_END JIT_CheckedWriteBarrier
//
// On exit:
// x12 : trashed
-// x14 : incremented by 8
+// x14 : trashed (incremented by 8 to implement JIT_ByRefWriteBarrier contract)
// x15 : trashed
// x17 : trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
//
@@ -272,23 +369,24 @@ WRITE_BARRIER_ENTRY JIT_WriteBarrier
#ifdef WRITE_BARRIER_CHECK
// Update GC Shadow Heap
- // need temporary registers. Save them before using.
- stp x12, x13, [sp, #-16]!
+ // Do not perform the work if g_GCShadow is 0
+ ldr x12, LOCAL_LABEL(wbs_GCShadow)
+ cbz x12, LOCAL_LABEL(ShadowUpdateDisabled)
+
+ // need temporary register. Save before using.
+ str x13, [sp, #-16]!
// Compute address of shadow heap location:
// pShadow = g_GCShadow + (x14 - g_lowest_address)
- PREPARE_EXTERNAL_VAR g_lowest_address, x12
- ldr x12, [x12]
- sub x12, x14, x12
- PREPARE_EXTERNAL_VAR g_GCShadow, x13
- ldr x13, [x13]
+ ldr x13, LOCAL_LABEL(wbs_lowest_address)
+ sub x13, x14, x13
add x12, x13, x12
// if (pShadow >= g_GCShadowEnd) goto end
PREPARE_EXTERNAL_VAR g_GCShadowEnd, x13
ldr x13, [x13]
cmp x12, x13
- bhs LOCAL_LABEL(shadowupdateend)
+ bhs LOCAL_LABEL(ShadowUpdateEnd)
// *pShadow = x15
str x15, [x12]
@@ -300,25 +398,22 @@ WRITE_BARRIER_ENTRY JIT_WriteBarrier
// if ([x14] == x15) goto end
ldr x13, [x14]
cmp x13, x15
- beq LOCAL_LABEL(shadowupdateend)
+ beq LOCAL_LABEL(ShadowUpdateEnd)
// *pShadow = INVALIDGCVALUE (0xcccccccd)
- mov x13, #0
- movk x13, #0xcccd
+ movz x13, #0xcccd
movk x13, #0xcccc, LSL #16
str x13, [x12]
-LOCAL_LABEL(shadowupdateend):
- ldp x12, x13, [sp],#16
+LOCAL_LABEL(ShadowUpdateEnd):
+ ldr x13, [sp], #16
+LOCAL_LABEL(ShadowUpdateDisabled):
#endif
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
// Update the write watch table if necessary
- PREPARE_EXTERNAL_VAR g_sw_ww_enabled_for_gc_heap, x12
- ldrb w12, [x12]
+ ldr x12, LOCAL_LABEL(wbs_sw_ww_table)
cbz x12, LOCAL_LABEL(CheckCardTable)
- PREPARE_EXTERNAL_VAR g_sw_ww_table, x12
- ldr x12, [x12]
add x12, x12, x14, lsr #0xc // SoftwareWriteWatch::AddressToTableByteIndexShift
ldrb w17, [x12]
cbnz x17, LOCAL_LABEL(CheckCardTable)
@@ -329,20 +424,18 @@ LOCAL_LABEL(shadowupdateend):
LOCAL_LABEL(CheckCardTable):
// Branch to Exit if the reference is not in the Gen0 heap
//
- PREPARE_EXTERNAL_VAR g_ephemeral_low, x12
- ldr x12, [x12]
+ ldr x12, LOCAL_LABEL(wbs_ephemeral_low)
+ cbz x12, LOCAL_LABEL(SkipEphemeralCheck)
cmp x15, x12
- blt LOCAL_LABEL(Exit)
- PREPARE_EXTERNAL_VAR g_ephemeral_high, x12
- ldr x12, [x12]
- cmp x15, x12
+ ldr x12, LOCAL_LABEL(wbs_ephemeral_high)
+ ccmp x15, x12, 0x0, ge
bgt LOCAL_LABEL(Exit)
+LOCAL_LABEL(SkipEphemeralCheck):
// Check if we need to update the card table
- PREPARE_EXTERNAL_VAR g_card_table, x12
- ldr x12, [x12]
- add x15, x12, x14, lsr #11
+ ldr x12, LOCAL_LABEL(wbs_card_table)
+ add x15, x12, x14, lsr #11
ldrb w12, [x15]
cmp x12, 0xFF
beq LOCAL_LABEL(Exit)
@@ -352,10 +445,9 @@ LOCAL_LABEL(UpdateCardTable):
strb w12, [x15]
#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
- // Check if we need to update the card table
- PREPARE_EXTERNAL_VAR g_card_bundle_table, x12
- ldr x12, [x12]
- add x15, x12, x14, lsr #21
+ // Check if we need to update the card bundle table
+ ldr x12, LOCAL_LABEL(wbs_card_bundle_table)
+ add x15, x12, x14, lsr #21
ldrb w12, [x15]
cmp x12, 0xFF
beq LOCAL_LABEL(Exit)
@@ -370,18 +462,6 @@ LOCAL_LABEL(Exit):
ret lr
WRITE_BARRIER_END JIT_WriteBarrier
-// ------------------------------------------------------------------
-// Start of the writeable code region
-LEAF_ENTRY JIT_PatchedCodeStart, _TEXT
- ret lr
-LEAF_END JIT_PatchedCodeStart, _TEXT
-
-// ------------------------------------------------------------------
-// End of the writeable code region
-LEAF_ENTRY JIT_PatchedCodeLast, _TEXT
- ret lr
-LEAF_END JIT_PatchedCodeLast, _TEXT
-
//------------------------------------------------
// VirtualMethodFixupStub
//
@@ -1112,6 +1192,7 @@ LOCAL_LABEL(Promote):
mov x16, #256
str x16, [x13] // be quick to reset the counter so we don't get a bunch of contending threads
orr x11, x11, #PROMOTE_CHAIN_FLAG // set PROMOTE_CHAIN_FLAG
+ mov x12, x9 // We pass the ResolveCacheElem to ResolveWorkerAsmStub instead of the DispatchToken
LOCAL_LABEL(Fail):
b ResolveWorkerAsmStub // call the ResolveWorkerAsmStub method to transition into the VM
@@ -1301,3 +1382,100 @@ LEAF_ENTRY JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT
ldr x0, [x0, #DomainLocalModule__m_pGCStatics]
ret lr
LEAF_END JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain, _TEXT
+
+// ------------------------------------------------------------------
+// __declspec(naked) void F_CALL_CONV JIT_Stelem_Ref(PtrArray* array, unsigned idx, Object* val)
+LEAF_ENTRY JIT_Stelem_Ref, _TEXT
+ // We retain arguments as they were passed and use x0 == array x1 == idx x2 == val
+
+ // check for null array
+ cbz x0, LOCAL_LABEL(ThrowNullReferenceException)
+
+ // idx bounds check
+ ldr x3,[x0,#ArrayBase__m_NumComponents]
+ cmp x3, x1
+ bls LOCAL_LABEL(ThrowIndexOutOfRangeException)
+
+ // fast path to null assignment (doesn't need any write-barriers)
+ cbz x2, LOCAL_LABEL(AssigningNull)
+
+ // Verify the array-type and val-type matches before writing
+ ldr x12, [x0] // x12 = array MT
+ ldr x3, [x2] // x3 = val->GetMethodTable()
+ ldr x12, [x12, #MethodTable__m_ElementType] // array->GetArrayElementTypeHandle()
+ cmp x3, x12
+ beq C_FUNC(JIT_Stelem_DoWrite)
+
+ // Types didnt match but allow writing into an array of objects
+ ldr x3, =g_pObjectClass
+ ldr x3, [x3] // x3 = *g_pObjectClass
+ cmp x3, x12 // array type matches with Object*
+ beq C_FUNC(JIT_Stelem_DoWrite)
+
+ // array type and val type do not exactly match. Raise frame and do detailed match
+ b C_FUNC(JIT_Stelem_Ref_NotExactMatch)
+
+LOCAL_LABEL(AssigningNull):
+ // Assigning null doesn't need write barrier
+ add x0, x0, x1, LSL #3 // x0 = x0 + (x1 x 8) = array->m_array[idx]
+ str x2, [x0, #PtrArray__m_Array] // array->m_array[idx] = val
+ ret
+
+LOCAL_LABEL(ThrowNullReferenceException):
+ // Tail call JIT_InternalThrow(NullReferenceException)
+ ldr x0, =CORINFO_NullReferenceException_ASM
+ b C_FUNC(JIT_InternalThrow)
+
+LOCAL_LABEL(ThrowIndexOutOfRangeException):
+ // Tail call JIT_InternalThrow(NullReferenceException)
+ ldr x0, =CORINFO_IndexOutOfRangeException_ASM
+ b C_FUNC(JIT_InternalThrow)
+
+LEAF_END JIT_Stelem_Ref, _TEXT
+
+// ------------------------------------------------------------------
+// __declspec(naked) void F_CALL_CONV JIT_Stelem_Ref_NotExactMatch(PtrArray* array,
+// unsigned idx, Object* val)
+// x12 = array->GetArrayElementTypeHandle()
+//
+NESTED_ENTRY JIT_Stelem_Ref_NotExactMatch, _TEXT, NoHandler
+ PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -48
+ // Spill callee saved registers
+ PROLOG_SAVE_REG_PAIR x0, x1, 16
+ PROLOG_SAVE_REG x2, 32
+
+ // allow in case val can be casted to array element type
+ // call ObjIsInstanceOfNoGC(val, array->GetArrayElementTypeHandle())
+ mov x1, x12 // array->GetArrayElementTypeHandle()
+ mov x0, x2
+ bl C_FUNC(ObjIsInstanceOfNoGC)
+ cmp x0, TypeHandle_CanCast
+ beq LOCAL_LABEL(DoWrite) // ObjIsInstance returned TypeHandle::CanCast
+
+ // check via raising frame
+LOCAL_LABEL(NeedFrame):
+ add x1, sp, #16 // x1 = &array
+ add x0, sp, #32 // x0 = &val
+
+ bl C_FUNC(ArrayStoreCheck) // ArrayStoreCheck(&val, &array)
+
+LOCAL_LABEL(DoWrite):
+ EPILOG_RESTORE_REG_PAIR x0, x1, 16
+ EPILOG_RESTORE_REG x2, 32
+ EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 48
+ b C_FUNC(JIT_Stelem_DoWrite)
+NESTED_END JIT_Stelem_Ref_NotExactMatch, _TEXT
+
+// ------------------------------------------------------------------
+// __declspec(naked) void F_CALL_CONV JIT_Stelem_DoWrite(PtrArray* array, unsigned idx, Object* val)
+LEAF_ENTRY JIT_Stelem_DoWrite, _TEXT
+
+ // Setup args for JIT_WriteBarrier. x14 = &array->m_array[idx] x15 = val
+ add x14, x0, #PtrArray__m_Array // x14 = &array->m_array
+ add x14, x14, x1, LSL #3
+ mov x15, x2 // x15 = val
+
+ // Branch to the write barrier (which is already correctly overwritten with
+ // single or multi-proc code based on the current CPU
+ b C_FUNC(JIT_WriteBarrier)
+LEAF_END JIT_Stelem_DoWrite, _TEXT
diff --git a/src/vm/arm64/asmhelpers.asm b/src/vm/arm64/asmhelpers.asm
index 8da2151459..2605a67f73 100644
--- a/src/vm/arm64/asmhelpers.asm
+++ b/src/vm/arm64/asmhelpers.asm
@@ -36,6 +36,11 @@
IMPORT DynamicHelperWorker
#endif
+ IMPORT ObjIsInstanceOfNoGC
+ IMPORT ArrayStoreCheck
+ SETALIAS g_pObjectClass, ?g_pObjectClass@@3PEAVMethodTable@@EA
+ IMPORT $g_pObjectClass
+
IMPORT g_ephemeral_low
IMPORT g_ephemeral_high
IMPORT g_lowest_address
@@ -1198,8 +1203,9 @@ Success
Promote
; Move this entry to head postion of the chain
mov x16, #256
- str x16, [x13] ; be quick to reset the counter so we don't get a bunch of contending threads
+ str x16, [x13] ; be quick to reset the counter so we don't get a bunch of contending threads
orr x11, x11, #PROMOTE_CHAIN_FLAG ; set PROMOTE_CHAIN_FLAG
+ mov x12, x9 ; We pass the ResolveCacheElem to ResolveWorkerAsmStub instead of the DispatchToken
Fail
b ResolveWorkerAsmStub ; call the ResolveWorkerAsmStub method to transition into the VM
@@ -1385,5 +1391,101 @@ CallHelper2
ret lr
LEAF_END
+; ------------------------------------------------------------------
+;__declspec(naked) void F_CALL_CONV JIT_Stelem_Ref(PtrArray* array, unsigned idx, Object* val)
+ LEAF_ENTRY JIT_Stelem_Ref
+ ; We retain arguments as they were passed and use x0 == array x1 == idx x2 == val
+
+ ; check for null array
+ cbz x0, ThrowNullReferenceException
+
+ ; idx bounds check
+ ldr x3,[x0,#ArrayBase__m_NumComponents]
+ cmp x3, x1
+ bls ThrowIndexOutOfRangeException
+
+ ; fast path to null assignment (doesn't need any write-barriers)
+ cbz x2, AssigningNull
+
+ ; Verify the array-type and val-type matches before writing
+ ldr x12, [x0] ; x12 = array MT
+ ldr x3, [x2] ; x3 = val->GetMethodTable()
+ ldr x12, [x12, #MethodTable__m_ElementType] ; array->GetArrayElementTypeHandle()
+ cmp x3, x12
+ beq JIT_Stelem_DoWrite
+
+ ; Types didnt match but allow writing into an array of objects
+ ldr x3, =$g_pObjectClass
+ ldr x3, [x3] ; x3 = *g_pObjectClass
+ cmp x3, x12 ; array type matches with Object*
+ beq JIT_Stelem_DoWrite
+
+ ; array type and val type do not exactly match. Raise frame and do detailed match
+ b JIT_Stelem_Ref_NotExactMatch
+
+AssigningNull
+ ; Assigning null doesn't need write barrier
+ add x0, x0, x1, LSL #3 ; x0 = x0 + (x1 x 8) = array->m_array[idx]
+ str x2, [x0, #PtrArray__m_Array] ; array->m_array[idx] = val
+ ret
+
+ThrowNullReferenceException
+ ; Tail call JIT_InternalThrow(NullReferenceException)
+ ldr x0, =CORINFO_NullReferenceException_ASM
+ b JIT_InternalThrow
+
+ThrowIndexOutOfRangeException
+ ; Tail call JIT_InternalThrow(NullReferenceException)
+ ldr x0, =CORINFO_IndexOutOfRangeException_ASM
+ b JIT_InternalThrow
+
+ LEAF_END
+
+; ------------------------------------------------------------------
+; __declspec(naked) void F_CALL_CONV JIT_Stelem_Ref_NotExactMatch(PtrArray* array,
+; unsigned idx, Object* val)
+; x12 = array->GetArrayElementTypeHandle()
+;
+ NESTED_ENTRY JIT_Stelem_Ref_NotExactMatch
+ PROLOG_SAVE_REG_PAIR fp, lr, #-0x48!
+ stp x0, x1, [sp, #16]
+ str x2, [sp, #32]
+
+ ; allow in case val can be casted to array element type
+ ; call ObjIsInstanceOfNoGC(val, array->GetArrayElementTypeHandle())
+ mov x1, x12 ; array->GetArrayElementTypeHandle()
+ mov x0, x2
+ bl ObjIsInstanceOfNoGC
+ cmp x0, TypeHandle_CanCast
+ beq DoWrite ; ObjIsInstance returned TypeHandle::CanCast
+
+ ; check via raising frame
+NeedFrame
+ add x1, sp, #16 ; x1 = &array
+ add x0, sp, #32 ; x0 = &val
+
+ bl ArrayStoreCheck ; ArrayStoreCheck(&val, &array)
+
+DoWrite
+ ldp x0, x1, [sp], #16
+ ldr x2, [sp], #32
+ EPILOG_RESTORE_REG_PAIR fp, lr, #0x48!
+ EPILOG_BRANCH JIT_Stelem_DoWrite
+ NESTED_END
+
+; ------------------------------------------------------------------
+; __declspec(naked) void F_CALL_CONV JIT_Stelem_DoWrite(PtrArray* array, unsigned idx, Object* val)
+ LEAF_ENTRY JIT_Stelem_DoWrite
+
+ ; Setup args for JIT_WriteBarrier. x14 = &array->m_array[idx] x15 = val
+ add x14, x0, #PtrArray__m_Array ; x14 = &array->m_array
+ add x14, x14, x1, LSL #3
+ mov x15, x2 ; x15 = val
+
+ ; Branch to the write barrier (which is already correctly overwritten with
+ ; single or multi-proc code based on the current CPU
+ b JIT_WriteBarrier
+ LEAF_END
+
; Must be at very end of file
END
diff --git a/src/vm/arm64/cgencpu.h b/src/vm/arm64/cgencpu.h
index d8bbcf7d1d..90e871a16d 100644
--- a/src/vm/arm64/cgencpu.h
+++ b/src/vm/arm64/cgencpu.h
@@ -79,6 +79,16 @@ typedef INT64 StackElemType;
// !! This expression assumes STACK_ELEM_SIZE is a power of 2.
#define StackElemSize(parmSize) (((parmSize) + STACK_ELEM_SIZE - 1) & ~((ULONG)(STACK_ELEM_SIZE - 1)))
+#ifdef FEATURE_PAL // TODO-ARM64-WINDOWS Add JIT_Stelem_Ref support
+//
+// JIT HELPERS.
+//
+// Create alias for optimized implementations of helpers provided on this platform
+//
+// optimized static helpers
+#define JIT_Stelem_Ref JIT_Stelem_Ref
+#endif
+
//**********************************************************************
// Frames
//**********************************************************************
@@ -481,6 +491,7 @@ struct DECLSPEC_ALIGN(16) UMEntryThunkCode
TADDR m_pvSecretParam;
void Encode(BYTE* pTargetCode, void* pvSecretParam);
+ void Poison();
LPCBYTE GetEntryPoint() const
{
diff --git a/src/vm/arm64/stubs.cpp b/src/vm/arm64/stubs.cpp
index 40d274959f..7e7c2e8088 100644
--- a/src/vm/arm64/stubs.cpp
+++ b/src/vm/arm64/stubs.cpp
@@ -14,11 +14,14 @@
#include "asmconstants.h"
#include "virtualcallstub.h"
#include "jitinterface.h"
+#include "ecall.h"
EXTERN_C void JIT_GetSharedNonGCStaticBase_SingleAppDomain();
EXTERN_C void JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain();
EXTERN_C void JIT_GetSharedGCStaticBase_SingleAppDomain();
EXTERN_C void JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain();
+EXTERN_C void JIT_UpdateWriteBarrierState(bool skipEphemeralCheck);
+
#ifndef DACCESS_COMPILE
//-----------------------------------------------------------------------
@@ -1086,6 +1089,31 @@ void JIT_TailCall()
#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
void InitJITHelpers1()
{
+#ifdef FEATURE_PAL // TODO
+ STANDARD_VM_CONTRACT;
+
+ _ASSERTE(g_SystemInfo.dwNumberOfProcessors != 0);
+
+ // Allocation helpers, faster but non-logging
+ if (!((TrackAllocationsEnabled()) ||
+ (LoggingOn(LF_GCALLOC, LL_INFO10))
+#ifdef _DEBUG
+ || (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP) != 0)
+#endif // _DEBUG
+ ))
+ {
+ if (GCHeapUtilities::UseThreadAllocationContexts())
+ {
+ SetJitHelperFunction(CORINFO_HELP_NEWSFAST, JIT_NewS_MP_FastPortable);
+ SetJitHelperFunction(CORINFO_HELP_NEWSFAST_ALIGN8, JIT_NewS_MP_FastPortable);
+ SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1VC_MP_FastPortable);
+ SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable);
+
+ ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateString_MP_FastPortable), ECall::FastAllocateString);
+ }
+ }
+#endif
+
if(IsSingleAppDomain())
{
SetJitHelperFunction(CORINFO_HELP_GETSHARED_GCSTATIC_BASE, JIT_GetSharedGCStaticBase_SingleAppDomain);
@@ -1093,7 +1121,15 @@ void InitJITHelpers1()
SetJitHelperFunction(CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR, JIT_GetSharedGCStaticBaseNoCtor_SingleAppDomain);
SetJitHelperFunction(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR,JIT_GetSharedNonGCStaticBaseNoCtor_SingleAppDomain);
}
+
+ JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
}
+#ifndef FEATURE_PAL // TODO-ARM64-WINDOWS #13592
+EXTERN_C void JIT_UpdateWriteBarrierState(bool) {}
+#endif
+
+#else
+EXTERN_C void JIT_UpdateWriteBarrierState(bool) {}
#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
EXTERN_C void __stdcall ProfileEnterNaked(UINT_PTR clientData)
@@ -1244,6 +1280,11 @@ void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
FlushInstructionCache(GetCurrentProcess(),&m_code,sizeof(m_code));
}
+void UMEntryThunkCode::Poison()
+{
+ // Insert 'brk 0xbe' at the entry point
+ m_code[0] = 0xd42017c0;
+}
#ifdef PROFILING_SUPPORTED
#include "proftoeeinterfaceimpl.h"
@@ -1307,28 +1348,29 @@ LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv)
return EXCEPTION_CONTINUE_SEARCH;
}
+#ifndef CROSSGEN_COMPILE
void StompWriteBarrierEphemeral(bool isRuntimeSuspended)
{
- return;
+ JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
}
void StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
{
- return;
+ JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
}
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
void SwitchToWriteWatchBarrier(bool isRuntimeSuspended)
{
- return;
+ JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
}
void SwitchToNonWriteWatchBarrier(bool isRuntimeSuspended)
{
- return;
+ JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
}
#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
-
+#endif // CROSSGEN_COMPILE
#ifdef DACCESS_COMPILE
BOOL GetAnyThunkTarget (T_CONTEXT *pctx, TADDR *pTarget, TADDR *pTargetMethodDesc)
diff --git a/src/vm/arm64/virtualcallstubcpu.hpp b/src/vm/arm64/virtualcallstubcpu.hpp
index b7c52091de..68c9125b00 100644
--- a/src/vm/arm64/virtualcallstubcpu.hpp
+++ b/src/vm/arm64/virtualcallstubcpu.hpp
@@ -217,8 +217,8 @@ struct ResolveHolder
//w13- this._hashedToken
//ldr w13, [x10 + DATA_OFFSET(_hashedToken)]
offset = DATA_OFFSET(_hashedToken);
- _ASSERTE(offset >=0 && offset%8 == 0);
- _stub._resolveEntryPoint[n++] = 0xB940014D | offset<<7;
+ _ASSERTE(offset >=0 && offset%4 == 0);
+ _stub._resolveEntryPoint[n++] = 0xB940014D | offset<<8;
//eor x9,x9,x13
_stub._resolveEntryPoint[n++] = 0xCA0D0129;
diff --git a/src/vm/armsinglestepper.h b/src/vm/armsinglestepper.h
index 53a10195cc..88935256c2 100644
--- a/src/vm/armsinglestepper.h
+++ b/src/vm/armsinglestepper.h
@@ -88,7 +88,11 @@ private:
kMaxCodeBuffer = 2 + 3 + 1, // WORD slots in our redirect buffer (2 for current instruction, 3 for
// breakpoint instructions used to pad out slots in an IT block and one
// for the final breakpoint)
+#ifdef __linux__
+ kBreakpointOp = 0xde01, // Opcode for the breakpoint instruction used on ARM Linux
+#else
kBreakpointOp = 0xdefe, // Opcode for the breakpoint instruction used on CoreARM
+#endif
};
// Bit numbers of the condition flags in the CPSR.
diff --git a/src/vm/array.cpp b/src/vm/array.cpp
index d6792942e7..3a33aff43a 100644
--- a/src/vm/array.cpp
+++ b/src/vm/array.cpp
@@ -310,7 +310,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
DWORD numNonVirtualSlots = numCtors + 3; // 3 for the proper rank Get, Set, Address
size_t cbMT = sizeof(MethodTable);
- cbMT += MethodTable::GetNumVtableIndirections(numVirtuals) * sizeof(PTR_PCODE);
+ cbMT += MethodTable::GetNumVtableIndirections(numVirtuals) * sizeof(MethodTable::VTableIndir_t);
// GC info
size_t cbCGCDescData = 0;
@@ -509,8 +509,11 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
#endif // !defined(_WIN64) && (DATA_ALIGNMENT > 4)
pMT->SetBaseSize(baseSize);
// Because of array method table persisting, we need to copy the map
- memcpy(pMTHead + imapOffset, pParentClass->GetInterfaceMap(),
- pParentClass->GetNumInterfaces() * sizeof(InterfaceInfo_t));
+ for (unsigned index = 0; index < pParentClass->GetNumInterfaces(); ++index)
+ {
+ InterfaceInfo_t *pIntInfo = (InterfaceInfo_t *) (pMTHead + imapOffset + index * sizeof(InterfaceInfo_t));
+ pIntInfo->SetMethodTable((pParentClass->GetInterfaceMap() + index)->GetMethodTable());
+ }
pMT->SetInterfaceMap(pParentClass->GetNumInterfaces(), (InterfaceInfo_t *)(pMTHead + imapOffset));
// Copy down flags for these interfaces as well. This is simplified a bit since we know that System.Array
@@ -536,7 +539,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
if (canShareVtableChunks)
{
// Share the parent chunk
- it.SetIndirectionSlot(pParentClass->GetVtableIndirections()[it.GetIndex()]);
+ it.SetIndirectionSlot(pParentClass->GetVtableIndirections()[it.GetIndex()].GetValueMaybeNull());
}
else
{
diff --git a/src/vm/assembly.cpp b/src/vm/assembly.cpp
index c9a995452c..32a7cd9969 100644
--- a/src/vm/assembly.cpp
+++ b/src/vm/assembly.cpp
@@ -18,7 +18,6 @@
#include "assembly.hpp"
#include "appdomain.hpp"
-#include "security.h"
#include "perfcounters.h"
#include "assemblyname.hpp"
diff --git a/src/vm/assemblyname.cpp b/src/vm/assemblyname.cpp
index bc6034ae63..f0ed60bb6d 100644
--- a/src/vm/assemblyname.cpp
+++ b/src/vm/assemblyname.cpp
@@ -20,7 +20,6 @@
#include <shlwapi.h>
#include "assemblyname.hpp"
-#include "security.h"
#include "field.h"
#include "strongname.h"
#include "eeconfig.h"
@@ -111,7 +110,7 @@ FCIMPL1(Object*, AssemblyNameNative::GetPublicKeyToken, Object* refThisUNSAFE)
{
FCALL_CONTRACT;
- OBJECTREF orOutputArray = NULL;
+ U1ARRAYREF orOutputArray = NULL;
OBJECTREF refThis = (OBJECTREF) refThisUNSAFE;
HELPER_METHOD_FRAME_BEGIN_RET_1(refThis);
@@ -137,7 +136,8 @@ FCIMPL1(Object*, AssemblyNameNative::GetPublicKeyToken, Object* refThisUNSAFE)
}
}
- Security::CopyEncodingToByteArray(pbToken, cb, &orOutputArray);
+ orOutputArray = (U1ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_U1, cb);
+ memcpyNoGCRefs(orOutputArray->m_Array, pbToken, cb);
}
HELPER_METHOD_FRAME_END();
diff --git a/src/vm/assemblynative.cpp b/src/vm/assemblynative.cpp
index e4f148a712..f0cfe9376c 100644
--- a/src/vm/assemblynative.cpp
+++ b/src/vm/assemblynative.cpp
@@ -23,7 +23,6 @@
#include "field.h"
#include "assemblyname.hpp"
#include "eeconfig.h"
-#include "security.h"
#include "strongname.h"
#include "interoputil.h"
#include "frames.h"
@@ -430,6 +429,40 @@ void QCALLTYPE AssemblyNative::GetType(QCall::AssemblyHandle pAssembly, LPCWSTR
return;
}
+void QCALLTYPE AssemblyNative::GetForwardedType(QCall::AssemblyHandle pAssembly, mdToken mdtExternalType, QCall::ObjectHandleOnStack retType)
+{
+ CONTRACTL
+ {
+ QCALL_CHECK;
+ }
+ CONTRACTL_END;
+
+ BEGIN_QCALL;
+
+ HRESULT hr;
+ LPCSTR pszNameSpace;
+ LPCSTR pszClassName;
+ mdToken mdImpl;
+
+ Assembly * pAsm = pAssembly->GetAssembly();
+ Module *pManifestModule = pAsm->GetManifestModule();
+ IfFailThrow(pManifestModule->GetMDImport()->GetExportedTypeProps(mdtExternalType, &pszNameSpace, &pszClassName, &mdImpl, NULL, NULL));
+ if (TypeFromToken(mdImpl) == mdtAssemblyRef)
+ {
+ NameHandle typeName(pszNameSpace, pszClassName);
+ typeName.SetTypeToken(pManifestModule, mdtExternalType);
+ TypeHandle typeHnd = pAsm->GetLoader()->LoadTypeHandleThrowIfFailed(&typeName);
+ {
+ GCX_COOP();
+ retType.Set(typeHnd.GetManagedClassObject());
+ }
+ }
+
+ END_QCALL;
+
+ return;
+}
+
FCIMPL1(FC_BOOL_RET, AssemblyNative::IsDynamic, AssemblyBaseObject* pAssemblyUNSAFE)
{
FCALL_CONTRACT;
diff --git a/src/vm/assemblynative.hpp b/src/vm/assemblynative.hpp
index 267231bd99..3f7362188d 100644
--- a/src/vm/assemblynative.hpp
+++ b/src/vm/assemblynative.hpp
@@ -90,7 +90,10 @@ public:
static
void QCALLTYPE GetType(QCall::AssemblyHandle pAssembly, LPCWSTR wszName, BOOL bThrowOnError, BOOL bIgnoreCase, QCall::ObjectHandleOnStack retType, QCall::ObjectHandleOnStack keepAlive);
-
+
+ static
+ void QCALLTYPE GetForwardedType(QCall::AssemblyHandle pAssembly, mdToken mdtExternalType, QCall::ObjectHandleOnStack retType);
+
static
INT32 QCALLTYPE GetManifestResourceInfo(QCall::AssemblyHandle pAssembly, LPCWSTR wszName, QCall::ObjectHandleOnStack retAssembly, QCall::StringHandleOnStack retFileName, QCall::StackCrawlMarkHandle stackMark);
diff --git a/src/vm/assemblyspec.cpp b/src/vm/assemblyspec.cpp
index 9ec1d97086..7f2829de99 100644
--- a/src/vm/assemblyspec.cpp
+++ b/src/vm/assemblyspec.cpp
@@ -19,7 +19,6 @@
#include <stdlib.h>
#include "assemblyspec.hpp"
-#include "security.h"
#include "eeconfig.h"
#include "strongname.h"
#include "strongnameholders.h"
@@ -495,18 +494,68 @@ void AssemblySpec::AssemblyNameInit(ASSEMBLYNAMEREF* pAsmName, PEImage* pImageIn
// version
gc.Version = AllocateObject(pVersion);
-
- MethodDescCallSite ctorMethod(METHOD__VERSION__CTOR);
-
- ARG_SLOT VersionArgs[5] =
+ // BaseAssemblySpec and AssemblyName properties store uint16 components for the version. Version and AssemblyVersion
+ // store int32 or uint32. When the former are initialized from the latter, the components are truncated to uint16 size.
+ // When the latter are initialized from the former, they are zero-extended to int32 size. For uint16 components, the max
+ // value is used to indicate an unspecified component. For int32 components, -1 is used. Since we're initializing a
+ // Version from an assembly version, map the uint16 unspecified value to the int32 size.
+ int componentCount = 2;
+ if (m_context.usBuildNumber != (USHORT)-1)
{
- ObjToArgSlot(gc.Version),
- (ARG_SLOT) m_context.usMajorVersion,
- (ARG_SLOT) m_context.usMinorVersion,
- (ARG_SLOT) m_context.usBuildNumber,
- (ARG_SLOT) m_context.usRevisionNumber,
- };
- ctorMethod.Call(VersionArgs);
+ ++componentCount;
+ if (m_context.usRevisionNumber != (USHORT)-1)
+ {
+ ++componentCount;
+ }
+ }
+ switch (componentCount)
+ {
+ case 2:
+ {
+ // Call Version(int, int) because Version(int, int, int, int) does not allow passing the unspecified value -1
+ MethodDescCallSite ctorMethod(METHOD__VERSION__CTOR_Ix2);
+ ARG_SLOT VersionArgs[] =
+ {
+ ObjToArgSlot(gc.Version),
+ (ARG_SLOT) m_context.usMajorVersion,
+ (ARG_SLOT) m_context.usMinorVersion
+ };
+ ctorMethod.Call(VersionArgs);
+ break;
+ }
+
+ case 3:
+ {
+ // Call Version(int, int, int) because Version(int, int, int, int) does not allow passing the unspecified value -1
+ MethodDescCallSite ctorMethod(METHOD__VERSION__CTOR_Ix3);
+ ARG_SLOT VersionArgs[] =
+ {
+ ObjToArgSlot(gc.Version),
+ (ARG_SLOT) m_context.usMajorVersion,
+ (ARG_SLOT) m_context.usMinorVersion,
+ (ARG_SLOT) m_context.usBuildNumber
+ };
+ ctorMethod.Call(VersionArgs);
+ break;
+ }
+
+ default:
+ {
+ // Call Version(int, int, int, int)
+ _ASSERTE(componentCount == 4);
+ MethodDescCallSite ctorMethod(METHOD__VERSION__CTOR_Ix4);
+ ARG_SLOT VersionArgs[] =
+ {
+ ObjToArgSlot(gc.Version),
+ (ARG_SLOT) m_context.usMajorVersion,
+ (ARG_SLOT) m_context.usMinorVersion,
+ (ARG_SLOT) m_context.usBuildNumber,
+ (ARG_SLOT) m_context.usRevisionNumber
+ };
+ ctorMethod.Call(VersionArgs);
+ break;
+ }
+ }
}
// cultureinfo
@@ -527,13 +576,13 @@ void AssemblySpec::AssemblyNameInit(ASSEMBLYNAMEREF* pAsmName, PEImage* pImageIn
strCtor.Call(args);
}
-
// public key or token byte array
if (m_pbPublicKeyOrToken)
- Security::CopyEncodingToByteArray((BYTE*) m_pbPublicKeyOrToken,
- m_cbPublicKeyOrToken,
- (OBJECTREF*) &gc.PublicKeyOrToken);
+ {
+ gc.PublicKeyOrToken = (U1ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_U1, m_cbPublicKeyOrToken);
+ memcpyNoGCRefs(gc.PublicKeyOrToken->m_Array, m_pbPublicKeyOrToken, m_cbPublicKeyOrToken);
+ }
// simple name
if(GetName())
diff --git a/src/vm/binder.cpp b/src/vm/binder.cpp
index 9ce1584c31..6a60712e3f 100644
--- a/src/vm/binder.cpp
+++ b/src/vm/binder.cpp
@@ -311,6 +311,58 @@ Signature MscorlibBinder::GetSignatureLocal(LPHARDCODEDMETASIG pHardcodedSig)
#ifndef DACCESS_COMPILE
+bool MscorlibBinder::ConvertType(const BYTE*& pSig, SigBuilder * pSigBuilder)
+{
+ bool bSomethingResolved = false;
+
+ CorElementType type = (CorElementType)*pSig++;
+
+ switch (type)
+ {
+ case ELEMENT_TYPE_GENERICINST:
+ {
+ pSigBuilder->AppendElementType(type);
+ if (ConvertType(pSig, pSigBuilder))
+ bSomethingResolved = true;
+ int arity = *pSig++;
+ pSigBuilder->AppendData(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ if (ConvertType(pSig, pSigBuilder))
+ bSomethingResolved = true;
+ }
+ }
+ break;
+
+ case ELEMENT_TYPE_BYREF:
+ case ELEMENT_TYPE_PTR:
+ case ELEMENT_TYPE_SZARRAY:
+ pSigBuilder->AppendElementType(type);
+ if (ConvertType(pSig, pSigBuilder))
+ bSomethingResolved = true;
+ break;
+
+ case ELEMENT_TYPE_CLASS:
+ case ELEMENT_TYPE_VALUETYPE:
+ {
+ // The binder class id may overflow 1 byte. Use 2 bytes to encode it.
+ BinderClassID id = (BinderClassID)(*pSig + 0x100 * *(pSig + 1));
+ pSig += 2;
+
+ pSigBuilder->AppendElementType(type);
+ pSigBuilder->AppendToken(GetClassLocal(id)->GetCl());
+ bSomethingResolved = true;
+ }
+ break;
+
+ default:
+ pSigBuilder->AppendElementType(type);
+ break;
+ }
+
+ return bSomethingResolved;
+}
+
//------------------------------------------------------------------
// Resolve type references in the hardcoded metasig.
// Returns a new signature with type refences resolved.
@@ -327,7 +379,7 @@ void MscorlibBinder::BuildConvertedSignature(const BYTE* pSig, SigBuilder * pSig
unsigned argCount;
unsigned callConv;
- INDEBUG(bool bSomethingResolved = false;)
+ bool bSomethingResolved = false;
// calling convention
callConv = *pSig++;
@@ -346,51 +398,8 @@ void MscorlibBinder::BuildConvertedSignature(const BYTE* pSig, SigBuilder * pSig
// <= because we want to include the return value or the field
for (unsigned i = 0; i <= argCount; i++) {
-
- for (;;) {
- BinderClassID id = CLASS__NIL;
- bool again = false;
-
- CorElementType type = (CorElementType)*pSig++;
-
- switch (type)
- {
- case ELEMENT_TYPE_BYREF:
- case ELEMENT_TYPE_PTR:
- case ELEMENT_TYPE_SZARRAY:
- again = true;
- break;
-
- case ELEMENT_TYPE_CLASS:
- case ELEMENT_TYPE_VALUETYPE:
- // The binder class id may overflow 1 byte. Use 2 bytes to encode it.
- id = (BinderClassID) (*pSig + 0x100 * *(pSig + 1));
- pSig += 2;
- break;
-
- case ELEMENT_TYPE_VOID:
- if (i != 0) {
- if (pSig[-2] != ELEMENT_TYPE_PTR)
- THROW_BAD_FORMAT(BFA_ONLY_VOID_PTR_IN_ARGS, (Module*)NULL); // only pointer to void allowed in arguments
- }
- break;
-
- default:
- break;
- }
-
- pSigBuilder->AppendElementType(type);
-
- if (id != CLASS__NIL)
- {
- pSigBuilder->AppendToken(GetClassLocal(id)->GetCl());
-
- INDEBUG(bSomethingResolved = true;)
- }
-
- if (!again)
- break;
- }
+ if (ConvertType(pSig, pSigBuilder))
+ bSomethingResolved = true;
}
_ASSERTE(bSomethingResolved);
diff --git a/src/vm/binder.h b/src/vm/binder.h
index 8e26bdd255..ad80292ea0 100644
--- a/src/vm/binder.h
+++ b/src/vm/binder.h
@@ -277,6 +277,7 @@ private:
Signature GetSignatureLocal(LPHARDCODEDMETASIG pHardcodedSig);
+ bool ConvertType(const BYTE*& pSig, SigBuilder * pSigBuilder);
void BuildConvertedSignature(const BYTE* pSig, SigBuilder * pSigBuilder);
const BYTE* ConvertSignature(LPHARDCODEDMETASIG pHardcodedSig, const BYTE* pSig);
diff --git a/src/vm/callcounter.cpp b/src/vm/callcounter.cpp
index 90013c79fb..14d9e6e6a4 100644
--- a/src/vm/callcounter.cpp
+++ b/src/vm/callcounter.cpp
@@ -23,23 +23,6 @@ CallCounter::CallCounter()
m_lock.Init(LOCK_TYPE_DEFAULT);
}
-// Init our connection to the tiered compilation manager during
-// AppDomain startup. This pointer will remain valid for the lifetime
-// of the AppDomain.
-void CallCounter::SetTieredCompilationManager(TieredCompilationManager* pTieredCompilationManager)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- CAN_TAKE_LOCK;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- m_pTieredCompilationManager.Store(pTieredCompilationManager);
-}
-
// This is called by the prestub each time the method is invoked in a particular
// AppDomain (the AppDomain for which AppDomain.GetCallCounter() == this). These
// calls continue until we backpatch the prestub to avoid future calls. This allows
@@ -92,7 +75,7 @@ BOOL CallCounter::OnMethodCalled(MethodDesc* pMethodDesc)
}
}
- return m_pTieredCompilationManager.Load()->OnMethodCalled(pMethodDesc, callCount);
+ return GetAppDomain()->GetTieredCompilationManager()->OnMethodCalled(pMethodDesc, callCount);
}
#endif // FEATURE_TIERED_COMPILATION
diff --git a/src/vm/callcounter.h b/src/vm/callcounter.h
index 82d14b76d9..ed98ccb1c8 100644
--- a/src/vm/callcounter.h
+++ b/src/vm/callcounter.h
@@ -70,13 +70,10 @@ public:
CallCounter();
#endif
- void SetTieredCompilationManager(TieredCompilationManager* pTieredCompilationManager);
BOOL OnMethodCalled(MethodDesc* pMethodDesc);
private:
- VolatilePtr<TieredCompilationManager> m_pTieredCompilationManager;
-
// fields protected by lock
SpinLock m_lock;
CallCounterHash m_methodToCallCount;
diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp
index 68af979b2f..c64d9e9042 100644
--- a/src/vm/ceeload.cpp
+++ b/src/vm/ceeload.cpp
@@ -20,7 +20,6 @@
#include "reflectclasswriter.h"
#include "method.hpp"
#include "stublink.h"
-#include "security.h"
#include "cgensys.h"
#include "excep.h"
#include "dbginterface.h"
@@ -99,85 +98,6 @@
#define NGEN_STATICS_ALLCLASSES_WERE_LOADED -1
-
-//---------------------------------------------------------------------------------------
-InstrumentedILOffsetMapping::InstrumentedILOffsetMapping()
-{
- LIMITED_METHOD_DAC_CONTRACT;
-
- m_cMap = 0;
- m_rgMap = NULL;
- _ASSERTE(IsNull());
-}
-
-//---------------------------------------------------------------------------------------
-//
-// Check whether there is any mapping information stored in this object.
-//
-// Notes:
-// The memory should be alive throughout the process lifetime until
-// the Module containing the instrumented method is destructed.
-//
-
-BOOL InstrumentedILOffsetMapping::IsNull()
-{
- LIMITED_METHOD_DAC_CONTRACT;
-
- _ASSERTE((m_cMap == 0) == (m_rgMap == NULL));
- return (m_cMap == 0);
-}
-
-#if !defined(DACCESS_COMPILE)
-//---------------------------------------------------------------------------------------
-//
-// Release the memory used by the array of COR_IL_MAPs.
-//
-// Notes:
-// * The memory should be alive throughout the process lifetime until the Module containing
-// the instrumented method is destructed.
-// * This struct should be read-only in DAC builds.
-//
-
-void InstrumentedILOffsetMapping::Clear()
-{
- LIMITED_METHOD_CONTRACT;
-
- if (m_rgMap != NULL)
- {
- delete [] m_rgMap;
- }
-
- m_cMap = 0;
- m_rgMap = NULL;
-}
-#endif // !DACCESS_COMPILE
-
-#if !defined(DACCESS_COMPILE)
-void InstrumentedILOffsetMapping::SetMappingInfo(SIZE_T cMap, COR_IL_MAP * rgMap)
-{
- WRAPPER_NO_CONTRACT;
- _ASSERTE((cMap == 0) == (rgMap == NULL));
- m_cMap = cMap;
- m_rgMap = ARRAY_PTR_COR_IL_MAP(rgMap);
-}
-#endif // !DACCESS_COMPILE
-
-SIZE_T InstrumentedILOffsetMapping::GetCount() const
-{
- LIMITED_METHOD_DAC_CONTRACT;
-
- _ASSERTE((m_cMap == 0) == (m_rgMap == NULL));
- return m_cMap;
-}
-
-ARRAY_PTR_COR_IL_MAP InstrumentedILOffsetMapping::GetOffsets() const
-{
- LIMITED_METHOD_DAC_CONTRACT;
-
- _ASSERTE((m_cMap == 0) == (m_rgMap == NULL));
- return m_rgMap;
-}
-
BOOL Module::HasInlineTrackingMap()
{
LIMITED_METHOD_DAC_CONTRACT;
@@ -2883,7 +2803,7 @@ BOOL Module::IsPreV4Assembly()
}
-ArrayDPTR(FixupPointer<PTR_MethodTable>) ModuleCtorInfo::GetGCStaticMTs(DWORD index)
+ArrayDPTR(RelativeFixupPointer<PTR_MethodTable>) ModuleCtorInfo::GetGCStaticMTs(DWORD index)
{
LIMITED_METHOD_CONTRACT;
@@ -8007,6 +7927,55 @@ void Module::ExpandAll(DataImage *image)
_ASSERTE(pBlobEntry->type == EndOfBlobStream);
}
+ //
+ // Record references to all of the hot methods specifiled by MethodProfilingData array
+ // We call MethodReferencedByCompiledCode to indicate that we plan on compiling this method
+ //
+ CORBBTPROF_TOKEN_INFO * pMethodProfilingData = profileData->GetTokenFlagsData(MethodProfilingData);
+ DWORD cMethodProfilingData = profileData->GetTokenFlagsCount(MethodProfilingData);
+ for (unsigned int i = 0; (i < cMethodProfilingData); i++)
+ {
+ mdToken token = pMethodProfilingData[i].token;
+ DWORD profilingFlags = pMethodProfilingData[i].flags;
+
+ // We call MethodReferencedByCompiledCode only when the profile data indicates that
+ // we executed (i.e read) the code for the method
+ //
+ if (profilingFlags & (1 << ReadMethodCode))
+ {
+ if (TypeFromToken(token) == mdtMethodDef)
+ {
+ MethodDesc * pMD = LookupMethodDef(token);
+ //
+ // Record a reference to a hot non-generic method
+ //
+ image->GetPreloader()->MethodReferencedByCompiledCode((CORINFO_METHOD_HANDLE)pMD);
+ }
+ else if (TypeFromToken(token) == ibcMethodSpec)
+ {
+ CORBBTPROF_BLOB_PARAM_SIG_ENTRY *pBlobSigEntry = profileData->GetBlobSigEntry(token);
+
+ if (pBlobSigEntry != NULL)
+ {
+ _ASSERTE(pBlobSigEntry->blob.token == token);
+ MethodDesc * pMD = LoadIBCMethodHelper(image, pBlobSigEntry);
+
+ if (pMD != NULL)
+ {
+ // Occasionally a non-instantiated generic method shows up in the IBC data, we should NOT compile it.
+ if (!pMD->IsTypicalMethodDefinition())
+ {
+ //
+ // Record a reference to a hot instantiated generic method
+ //
+ image->GetPreloader()->MethodReferencedByCompiledCode((CORINFO_METHOD_HANDLE)pMD);
+ }
+ }
+ }
+ }
+ }
+ }
+
{
//
// Fill out MemberRef RID map and va sig cookies for
@@ -8131,6 +8100,8 @@ void Module::SaveTypeHandle(DataImage * image,
#endif // _DEBUG
}
+#ifndef DACCESS_COMPILE
+
void ModuleCtorInfo::Save(DataImage *image, CorProfileData *profileData)
{
STANDARD_VM_CONTRACT;
@@ -8152,7 +8123,7 @@ void ModuleCtorInfo::Save(DataImage *image, CorProfileData *profileData)
// items numElementsHot...i-1 are cold
for (i = 0; i < numElements; i++)
{
- MethodTable *ppMTTemp = ppMT[i];
+ MethodTable *ppMTTemp = ppMT[i].GetValue();
// Count the number of boxed statics along the way
totalBoxedStatics += ppMTTemp->GetNumBoxedRegularStatics();
@@ -8166,8 +8137,8 @@ void ModuleCtorInfo::Save(DataImage *image, CorProfileData *profileData)
if (hot)
{
// swap ppMT[i] and ppMT[numElementsHot] to maintain the loop invariant
- ppMT[i] = ppMT[numElementsHot];
- ppMT[numElementsHot] = ppMTTemp;
+ ppMT[i].SetValue(ppMT[numElementsHot].GetValue());
+ ppMT[numElementsHot].SetValue(ppMTTemp);
numElementsHot++;
}
@@ -8192,11 +8163,11 @@ void ModuleCtorInfo::Save(DataImage *image, CorProfileData *profileData)
for (i = 0; i < numElementsHot; i++)
{
- hashArray[i] = GenerateHash(ppMT[i], HOT);
+ hashArray[i] = GenerateHash(ppMT[i].GetValue(), HOT);
}
for (i = numElementsHot; i < numElements; i++)
{
- hashArray[i] = GenerateHash(ppMT[i], COLD);
+ hashArray[i] = GenerateHash(ppMT[i].GetValue(), COLD);
}
// Sort the two arrays by hash values to create regions with the same hash values.
@@ -8259,7 +8230,7 @@ void ModuleCtorInfo::Save(DataImage *image, CorProfileData *profileData)
// make cctorInfoCold point to the first cold element
cctorInfoCold = cctorInfoHot + numElementsHot;
- ppHotGCStaticsMTs = (totalBoxedStatics != 0) ? new FixupPointer<PTR_MethodTable>[totalBoxedStatics] : NULL;
+ ppHotGCStaticsMTs = (totalBoxedStatics != 0) ? new RelativeFixupPointer<PTR_MethodTable>[totalBoxedStatics] : NULL;
numHotGCStaticsMTs = totalBoxedStatics;
DWORD iGCStaticMT = 0;
@@ -8275,7 +8246,7 @@ void ModuleCtorInfo::Save(DataImage *image, CorProfileData *profileData)
ppColdGCStaticsMTs = ppHotGCStaticsMTs + numHotGCStaticsMTs;
}
- MethodTable* pMT = ppMT[i];
+ MethodTable* pMT = ppMT[i].GetValue();
ClassCtorInfoEntry* pEntry = &cctorInfoHot[i];
WORD numBoxedStatics = pMT->GetNumBoxedRegularStatics();
@@ -8305,7 +8276,7 @@ void ModuleCtorInfo::Save(DataImage *image, CorProfileData *profileData)
== (iGCStaticMT - pEntry->firstBoxedStaticMTIndex) * sizeof(MethodTable*));
TypeHandle th = pField->GetFieldTypeHandleThrowing();
- ppHotGCStaticsMTs[iGCStaticMT++].SetValue(th.GetMethodTable());
+ ppHotGCStaticsMTs[iGCStaticMT++].SetValueMaybeNull(th.GetMethodTable());
numFoundBoxedStatics++;
}
@@ -8328,7 +8299,7 @@ void ModuleCtorInfo::Save(DataImage *image, CorProfileData *profileData)
if (numElements > 0)
image->StoreStructure(ppMT,
- sizeof(MethodTable *) * numElements,
+ sizeof(RelativePointer<MethodTable *>) * numElements,
DataImage::ITEM_MODULE_CCTOR_INFO_HOT);
if (numElements > numElementsHot)
@@ -8345,7 +8316,7 @@ void ModuleCtorInfo::Save(DataImage *image, CorProfileData *profileData)
if ( numHotGCStaticsMTs )
{
// Save the mt templates
- image->StoreStructure( ppHotGCStaticsMTs, numHotGCStaticsMTs * sizeof(MethodTable*),
+ image->StoreStructure( ppHotGCStaticsMTs, numHotGCStaticsMTs * sizeof(RelativeFixupPointer<MethodTable*>),
DataImage::ITEM_GC_STATIC_HANDLES_HOT);
}
else
@@ -8356,7 +8327,7 @@ void ModuleCtorInfo::Save(DataImage *image, CorProfileData *profileData)
if ( numColdGCStaticsMTs )
{
// Save the hot mt templates
- image->StoreStructure( ppColdGCStaticsMTs, numColdGCStaticsMTs * sizeof(MethodTable*),
+ image->StoreStructure( ppColdGCStaticsMTs, numColdGCStaticsMTs * sizeof(RelativeFixupPointer<MethodTable*>),
DataImage::ITEM_GC_STATIC_HANDLES_COLD);
}
else
@@ -8365,6 +8336,7 @@ void ModuleCtorInfo::Save(DataImage *image, CorProfileData *profileData)
}
}
+#endif // !DACCESS_COMPILE
bool Module::AreAllClassesFullyLoaded()
{
@@ -9165,13 +9137,20 @@ void Module::PlaceType(DataImage *image, TypeHandle th, DWORD profilingFlags)
{
if (pMT->HasPerInstInfo())
{
- Dictionary ** pPerInstInfo = pMT->GetPerInstInfo();
+ DPTR(MethodTable::PerInstInfoElem_t) pPerInstInfo = pMT->GetPerInstInfo();
BOOL fIsEagerBound = pMT->CanEagerBindToParentDictionaries(image, NULL);
if (fIsEagerBound)
{
- image->PlaceInternedStructureForAddress(pPerInstInfo, CORCOMPILE_SECTION_READONLY_SHARED_HOT, CORCOMPILE_SECTION_READONLY_HOT);
+ if (MethodTable::PerInstInfoElem_t::isRelative)
+ {
+ image->PlaceStructureForAddress(pPerInstInfo, CORCOMPILE_SECTION_READONLY_HOT);
+ }
+ else
+ {
+ image->PlaceInternedStructureForAddress(pPerInstInfo, CORCOMPILE_SECTION_READONLY_SHARED_HOT, CORCOMPILE_SECTION_READONLY_HOT);
+ }
}
else
{
@@ -9501,7 +9480,7 @@ void ModuleCtorInfo::Fixup(DataImage *image)
for (DWORD i=0; i<numElements; i++)
{
- image->FixupPointerField(ppMT, i * sizeof(ppMT[0]));
+ image->FixupRelativePointerField(ppMT, i * sizeof(ppMT[0]));
}
}
else
@@ -10123,11 +10102,37 @@ void Module::RestoreMethodTablePointer(RelativeFixupPointer<PTR_MethodTable> * p
if (ppMT->IsTagged((TADDR)ppMT))
{
- RestoreMethodTablePointerRaw(ppMT->GetValuePtr((TADDR)ppMT), pContainingModule, level);
+ RestoreMethodTablePointerRaw(ppMT->GetValuePtr(), pContainingModule, level);
+ }
+ else
+ {
+ ClassLoader::EnsureLoaded(ppMT->GetValue(), level);
+ }
+}
+
+/*static*/
+void Module::RestoreMethodTablePointer(PlainPointer<PTR_MethodTable> * ppMT,
+ Module *pContainingModule,
+ ClassLoadLevel level)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (ppMT->IsNull())
+ return;
+
+ if (ppMT->IsTagged())
+ {
+ RestoreMethodTablePointerRaw(ppMT->GetValuePtr(), pContainingModule, level);
}
else
{
- ClassLoader::EnsureLoaded(ppMT->GetValue((TADDR)ppMT), level);
+ ClassLoader::EnsureLoaded(ppMT->GetValue(), level);
}
}
@@ -10262,7 +10267,7 @@ PTR_Module Module::RestoreModulePointerIfLoaded(DPTR(RelativeFixupPointer<PTR_Mo
return ppModule->GetValue(dac_cast<TADDR>(ppModule));
#ifndef DACCESS_COMPILE
- PTR_Module * ppValue = ppModule->GetValuePtr(dac_cast<TADDR>(ppModule));
+ PTR_Module * ppValue = ppModule->GetValuePtr();
// Ensure that the compiler won't fetch the value twice
TADDR fixup = VolatileLoadWithoutBarrier((TADDR *)ppValue);
@@ -10315,7 +10320,7 @@ void Module::RestoreModulePointer(RelativeFixupPointer<PTR_Module> * ppModule, M
if (!ppModule->IsTagged((TADDR)ppModule))
return;
- PTR_Module * ppValue = ppModule->GetValuePtr((TADDR)ppModule);
+ PTR_Module * ppValue = ppModule->GetValuePtr();
// Ensure that the compiler won't fetch the value twice
TADDR fixup = VolatileLoadWithoutBarrier((TADDR *)ppValue);
@@ -10469,7 +10474,7 @@ void Module::RestoreTypeHandlePointer(RelativeFixupPointer<TypeHandle> * pHandle
if (pHandle->IsTagged((TADDR)pHandle))
{
- RestoreTypeHandlePointerRaw(pHandle->GetValuePtr((TADDR)pHandle), pContainingModule, level);
+ RestoreTypeHandlePointerRaw(pHandle->GetValuePtr(), pContainingModule, level);
}
else
{
@@ -10571,7 +10576,7 @@ void Module::RestoreMethodDescPointer(RelativeFixupPointer<PTR_MethodDesc> * ppM
if (ppMD->IsTagged((TADDR)ppMD))
{
- RestoreMethodDescPointerRaw(ppMD->GetValuePtr((TADDR)ppMD), pContainingModule, level);
+ RestoreMethodDescPointerRaw(ppMD->GetValuePtr(), pContainingModule, level);
}
else
{
@@ -13896,7 +13901,7 @@ ModuleCtorInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
// This class is contained so do not enumerate 'this'.
DacEnumMemoryRegion(dac_cast<TADDR>(ppMT), numElements *
- sizeof(TADDR));
+ sizeof(RelativePointer<MethodTable *>));
DacEnumMemoryRegion(dac_cast<TADDR>(cctorInfoHot), numElementsHot *
sizeof(ClassCtorInfoEntry));
DacEnumMemoryRegion(dac_cast<TADDR>(cctorInfoCold),
@@ -14113,97 +14118,6 @@ LPCWSTR Module::GetPathForErrorMessages()
}
}
-#ifndef DACCESS_COMPILE
-BOOL IsVerifiableWrapper(MethodDesc* pMD)
-{
- BOOL ret = FALSE;
- //EX_TRY contains _alloca, so I can't use this inside of a loop. 4wesome.
- EX_TRY
- {
- ret = pMD->IsVerifiable();
- }
- EX_CATCH
- {
- //if the method has a security exception, it will fly through IsVerifiable. Shunt
- //to the unverifiable path below.
- }
- EX_END_CATCH(RethrowTerminalExceptions)
- return ret;
-}
-#endif //DACCESS_COMPILE
-void Module::VerifyAllMethods()
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;
-#ifndef DACCESS_COMPILE
- //If the EE isn't started yet, it's not safe to jit. We fail in COM jitting a p/invoke.
- if (!g_fEEStarted)
- return;
-
- struct Local
- {
- static bool VerifyMethodsForTypeDef(Module * pModule, mdTypeDef td)
- {
- bool ret = true;
- TypeHandle th = ClassLoader::LoadTypeDefThrowing(pModule, td, ClassLoader::ThrowIfNotFound,
- ClassLoader::PermitUninstDefOrRef);
-
- MethodTable * pMT = th.GetMethodTable();
- MethodTable::MethodIterator it(pMT);
- for (; it.IsValid(); it.Next())
- {
- MethodDesc * pMD = it.GetMethodDesc();
- if (pMD->HasILHeader() && Security::IsMethodTransparent(pMD)
- && (g_pObjectCtorMD != pMD))
- {
- if (!IsVerifiableWrapper(pMD))
- {
-#ifdef _DEBUG
- SString s;
- if (LoggingOn(LF_VERIFIER, LL_ERROR))
- TypeString::AppendMethodDebug(s, pMD);
- LOG((LF_VERIFIER, LL_ERROR, "Transparent Method (0x%p), %S is unverifiable\n",
- pMD, s.GetUnicode()));
-#endif
- ret = false;
- }
- }
- }
- return ret;
- }
- };
- //Verify all methods in a module eagerly, forcing them to get loaded.
-
- IMDInternalImport * pMDI = GetMDImport();
- HENUMTypeDefInternalHolder hEnum(pMDI);
- mdTypeDef td;
- hEnum.EnumTypeDefInit();
-
- bool isAllVerifiable = true;
- //verify global methods
- if (GetGlobalMethodTable())
- {
- //verify everything in the MT.
- if (!Local::VerifyMethodsForTypeDef(this, COR_GLOBAL_PARENT_TOKEN))
- isAllVerifiable = false;
- }
- while (pMDI->EnumTypeDefNext(&hEnum, &td))
- {
- //verify everything
- if (!Local::VerifyMethodsForTypeDef(this, td))
- isAllVerifiable = false;
- }
- if (!isAllVerifiable)
- EEFileLoadException::Throw(GetFile(), COR_E_VERIFICATION);
-#endif //DACCESS_COMPILE
-}
-
-
#if defined(_DEBUG) && !defined(DACCESS_COMPILE) && !defined(CROSS_COMPILE)
void Module::ExpandAll()
{
@@ -14235,16 +14149,7 @@ void Module::ExpandAll()
|| pMD->HasClassInstantiation())
&& (pMD->MayHaveNativeCode() && !pMD->IsFCallOrIntrinsic()))
{
- COR_ILMETHOD * ilHeader = pMD->GetILHeader();
- COR_ILMETHOD_DECODER::DecoderStatus ignored;
- NewHolder<COR_ILMETHOD_DECODER> pHeader(new COR_ILMETHOD_DECODER(ilHeader,
- pMD->GetMDImport(),
- &ignored));
-#ifdef FEATURE_INTERPRETER
- pMD->MakeJitWorker(pHeader, CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_MAKEFINALCODE));
-#else
- pMD->MakeJitWorker(pHeader, CORJIT_FLAGS());
-#endif
+ pMD->PrepareInitialCode();
}
}
static void CompileMethodsForMethodTable(MethodTable * pMT)
@@ -14314,9 +14219,6 @@ void Module::ExpandAll()
};
//Jit all methods eagerly
- /* XXX Thu 4/26/2007
- * This code is lifted mostly from code:Module::VerifyAllMethods
- */
IMDInternalImport * pMDI = GetMDImport();
HENUMTypeDefInternalHolder hEnum(pMDI);
mdTypeDef td;
diff --git a/src/vm/ceeload.h b/src/vm/ceeload.h
index bc9937a828..b70ea51feb 100644
--- a/src/vm/ceeload.h
+++ b/src/vm/ceeload.h
@@ -47,6 +47,8 @@
#include "readytoruninfo.h"
#endif
+#include "ilinstrumentation.h"
+
class PELoader;
class Stub;
class MethodDesc;
@@ -77,7 +79,9 @@ class CerNgenRootTable;
struct MethodContextElement;
class TypeHandleList;
class ProfileEmitter;
-class ReJitManager;
+class CodeVersionManager;
+class CallCounter;
+class TieredCompilationManager;
class TrackingMap;
struct MethodInModule;
class PersistentInlineTrackingMapNGen;
@@ -618,7 +622,7 @@ struct ModuleCtorInfo
DWORD numElements;
DWORD numLastAllocated;
DWORD numElementsHot;
- DPTR(PTR_MethodTable) ppMT; // size is numElements
+ DPTR(RelativePointer<PTR_MethodTable>) ppMT; // size is numElements
PTR_ClassCtorInfoEntry cctorInfoHot; // size is numElementsHot
PTR_ClassCtorInfoEntry cctorInfoCold; // size is numElements-numElementsHot
@@ -627,8 +631,8 @@ struct ModuleCtorInfo
DWORD numHotHashes;
DWORD numColdHashes;
- ArrayDPTR(FixupPointer<PTR_MethodTable>) ppHotGCStaticsMTs; // hot table
- ArrayDPTR(FixupPointer<PTR_MethodTable>) ppColdGCStaticsMTs; // cold table
+ ArrayDPTR(RelativeFixupPointer<PTR_MethodTable>) ppHotGCStaticsMTs; // hot table
+ ArrayDPTR(RelativeFixupPointer<PTR_MethodTable>) ppColdGCStaticsMTs; // cold table
DWORD numHotGCStaticsMTs;
DWORD numColdGCStaticsMTs;
@@ -664,7 +668,13 @@ struct ModuleCtorInfo
return hashVal;
};
- ArrayDPTR(FixupPointer<PTR_MethodTable>) GetGCStaticMTs(DWORD index);
+ ArrayDPTR(RelativeFixupPointer<PTR_MethodTable>) GetGCStaticMTs(DWORD index);
+
+ PTR_MethodTable GetMT(DWORD i)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return ppMT[i].GetValue(dac_cast<TADDR>(ppMT) + i * sizeof(RelativePointer<PTR_MethodTable>));
+ }
#ifdef FEATURE_PREJIT
@@ -675,11 +685,11 @@ struct ModuleCtorInfo
class ClassCtorInfoEntryArraySort : public CQuickSort<DWORD>
{
private:
- PTR_MethodTable *m_pBase1;
+ DPTR(RelativePointer<PTR_MethodTable>) m_pBase1;
public:
//Constructor
- ClassCtorInfoEntryArraySort(DWORD *base, PTR_MethodTable *base1, int count)
+ ClassCtorInfoEntryArraySort(DWORD *base, DPTR(RelativePointer<PTR_MethodTable>) base1, int count)
: CQuickSort<DWORD>(base, count)
{
WRAPPER_NO_CONTRACT;
@@ -700,6 +710,7 @@ struct ModuleCtorInfo
return 1;
}
+#ifndef DACCESS_COMPILE
// Swap is overwriten so that we can sort both the MethodTable pointer
// array and the ClassCtorInfoEntry array in parrallel.
FORCEINLINE void Swap(SSIZE_T iFirst, SSIZE_T iSecond)
@@ -715,10 +726,11 @@ struct ModuleCtorInfo
m_pBase[iFirst] = m_pBase[iSecond];
m_pBase[iSecond] = sTemp;
- sTemp1 = m_pBase1[iFirst];
- m_pBase1[iFirst] = m_pBase1[iSecond];
- m_pBase1[iSecond] = sTemp1;
+ sTemp1 = m_pBase1[iFirst].GetValueMaybeNull();
+ m_pBase1[iFirst].SetValueMaybeNull(m_pBase1[iSecond].GetValueMaybeNull());
+ m_pBase1[iSecond].SetValueMaybeNull(sTemp1);
}
+#endif // !DACCESS_COMPILE
};
#endif // FEATURE_PREJIT
};
@@ -1085,104 +1097,6 @@ typedef SHash<DynamicILBlobTraits> DynamicILBlobTable;
typedef DPTR(DynamicILBlobTable) PTR_DynamicILBlobTable;
-// declare an array type of COR_IL_MAP entries
-typedef ArrayDPTR(COR_IL_MAP) ARRAY_PTR_COR_IL_MAP;
-
-//---------------------------------------------------------------------------------------
-//
-// A profiler may instrument a method by changing the IL. This is typically done when the profiler receives
-// a JITCompilationStarted notification. The profiler also has the option to provide the runtime with
-// a mapping between original IL offsets and instrumented IL offsets. This struct is a simple container
-// for storing the mapping information. We store the mapping information on the Module class, where it can
-// be accessed by the debugger from out-of-process.
-//
-
-class InstrumentedILOffsetMapping
-{
-public:
- InstrumentedILOffsetMapping();
-
- // Check whether there is any mapping information stored in this object.
- BOOL IsNull();
-
-#if !defined(DACCESS_COMPILE)
- // Release the memory used by the array of COR_IL_MAPs.
- void Clear();
-
- void SetMappingInfo(SIZE_T cMap, COR_IL_MAP * rgMap);
-#endif // !DACCESS_COMPILE
-
- SIZE_T GetCount() const;
- ARRAY_PTR_COR_IL_MAP GetOffsets() const;
-
-private:
- SIZE_T m_cMap; // the number of elements in m_rgMap
- ARRAY_PTR_COR_IL_MAP m_rgMap; // an array of COR_IL_MAPs
-};
-
-//---------------------------------------------------------------------------------------
-//
-// Hash table entry for storing InstrumentedILOffsetMapping. This is keyed by the MethodDef token.
-//
-
-struct ILOffsetMappingEntry
-{
- ILOffsetMappingEntry()
- {
- LIMITED_METHOD_DAC_CONTRACT;
-
- m_methodToken = mdMethodDefNil;
- // No need to initialize m_mapping. The default ctor of InstrumentedILOffsetMapping does the job.
- }
-
- ILOffsetMappingEntry(mdMethodDef token, InstrumentedILOffsetMapping mapping)
- {
- LIMITED_METHOD_DAC_CONTRACT;
-
- m_methodToken = token;
- m_mapping = mapping;
- }
-
- mdMethodDef m_methodToken;
- InstrumentedILOffsetMapping m_mapping;
-};
-
-//---------------------------------------------------------------------------------------
-//
-// This class is used to create the hash table for the instrumented IL offset mapping.
-// It encapsulates the desired behaviour of the templated hash table and implements
-// the various functions needed by the hash table.
-//
-
-class ILOffsetMappingTraits : public NoRemoveSHashTraits<DefaultSHashTraits<ILOffsetMappingEntry> >
-{
-public:
- typedef mdMethodDef key_t;
-
- static key_t GetKey(element_t e)
- {
- LIMITED_METHOD_DAC_CONTRACT;
- return e.m_methodToken;
- }
- static BOOL Equals(key_t k1, key_t k2)
- {
- LIMITED_METHOD_DAC_CONTRACT;
- return (k1 == k2);
- }
- static count_t Hash(key_t k)
- {
- LIMITED_METHOD_DAC_CONTRACT;
- return (count_t)(size_t)k;
- }
- static const element_t Null()
- {
- LIMITED_METHOD_DAC_CONTRACT;
- ILOffsetMappingEntry e;
- return e;
- }
- static bool IsNull(const element_t &e) { LIMITED_METHOD_DAC_CONTRACT; return e.m_methodToken == mdMethodDefNil; }
-};
-
// ESymbolFormat specified the format used by a symbol stream
typedef enum
{
@@ -1192,11 +1106,6 @@ typedef enum
}ESymbolFormat;
-// Hash table of profiler-provided instrumented IL offset mapping, keyed by the MethodDef token
-typedef SHash<ILOffsetMappingTraits> ILOffsetMappingTable;
-typedef DPTR(ILOffsetMappingTable) PTR_ILOffsetMappingTable;
-
-
#ifdef FEATURE_COMINTEROP
//---------------------------------------------------------------------------------------
@@ -1885,7 +1794,12 @@ protected:
ClassLoader *GetClassLoader();
PTR_BaseDomain GetDomain();
- ReJitManager * GetReJitManager();
+#ifdef FEATURE_CODE_VERSIONING
+ CodeVersionManager * GetCodeVersionManager();
+#endif
+#ifdef FEATURE_TIERED_COMPILATION
+ CallCounter * GetCallCounter();
+#endif
mdFile GetModuleRef()
{
@@ -2902,6 +2816,10 @@ public:
ClassLoadLevel level = CLASS_LOADED);
static void RestoreFieldDescPointer(RelativeFixupPointer<PTR_FieldDesc> * ppFD);
+ static void RestoreMethodTablePointer(PlainPointer<PTR_MethodTable> * ppMT,
+ Module *pContainingModule = NULL,
+ ClassLoadLevel level = CLASS_LOADED);
+
static void RestoreModulePointer(RelativeFixupPointer<PTR_Module> * ppModule, Module *pContainingModule);
static PTR_Module RestoreModulePointerIfLoaded(DPTR(RelativeFixupPointer<PTR_Module>) ppModule, Module *pContainingModule);
@@ -3353,8 +3271,6 @@ protected:
public:
- void VerifyAllMethods();
-
CrstBase *GetLookupTableCrst()
{
LIMITED_METHOD_CONTRACT;
diff --git a/src/vm/ceeload.inl b/src/vm/ceeload.inl
index 8226dce7d7..3afef732cc 100644
--- a/src/vm/ceeload.inl
+++ b/src/vm/ceeload.inl
@@ -656,10 +656,20 @@ inline MethodTable* Module::GetDynamicClassMT(DWORD dynamicClassID)
return m_pDynamicStaticsInfo[dynamicClassID].pEnclosingMT;
}
-inline ReJitManager * Module::GetReJitManager()
+#ifdef FEATURE_CODE_VERSIONING
+inline CodeVersionManager * Module::GetCodeVersionManager()
{
LIMITED_METHOD_CONTRACT;
- return GetDomain()->GetReJitManager();
+ return GetDomain()->GetCodeVersionManager();
}
+#endif // FEATURE_CODE_VERSIONING
+
+#ifdef FEATURE_TIERED_COMPILATION
+inline CallCounter * Module::GetCallCounter()
+{
+ LIMITED_METHOD_CONTRACT;
+ return GetDomain()->GetCallCounter();
+}
+#endif // FEATURE_TIERED_COMPILATION
#endif // CEELOAD_INL_
diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp
index f28785bc21..011caf0997 100644
--- a/src/vm/ceemain.cpp
+++ b/src/vm/ceemain.cpp
@@ -137,7 +137,6 @@
#include "stackwalk.h"
#include "gcheaputilities.h"
#include "interoputil.h"
-#include "security.h"
#include "fieldmarshaler.h"
#include "dbginterface.h"
#include "eedbginterfaceimpl.h"
@@ -900,10 +899,6 @@ void EEStartupHelper(COINITIEE fFlags)
#ifndef CROSSGEN_COMPILE
- // This isn't done as part of InitializeGarbageCollector() above because thread
- // creation requires AppDomains to have been set up.
- FinalizerThread::FinalizerThreadCreate();
-
#ifndef FEATURE_PAL
// Watson initialization must precede InitializeDebugger() and InstallUnhandledExceptionFilter()
// because on CoreCLR when Waston is enabled, debugging service needs to be enabled and UEF will be used.
@@ -1010,6 +1005,10 @@ void EEStartupHelper(COINITIEE fFlags)
hr = g_pGCHeap->Initialize();
IfFailGo(hr);
+ // This isn't done as part of InitializeGarbageCollector() above because thread
+ // creation requires AppDomains to have been set up.
+ FinalizerThread::FinalizerThreadCreate();
+
// Now we really have fully initialized the garbage collector
SetGarbageCollectorFullyInitialized();
@@ -1111,12 +1110,6 @@ void EEStartupHelper(COINITIEE fFlags)
SystemDomain::SystemModule()->ExpandAll();
}
- //For a similar reason, let's not run VerifyAllOnLoad either.
- if (g_pConfig->VerifyModulesOnLoad())
- {
- SystemDomain::SystemModule()->VerifyAllMethods();
- }
-
// Perform mscorlib consistency check if requested
g_Mscorlib.CheckExtended();
@@ -1630,6 +1623,13 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading)
// Indicate the EE is the shut down phase.
g_fEEShutDown |= ShutDown_Start;
+#ifdef FEATURE_TIERED_COMPILATION
+ {
+ GCX_PREEMP();
+ TieredCompilationManager::ShutdownAllDomains();
+ }
+#endif
+
fFinalizeOK = TRUE;
// Terminate the BBSweep thread
@@ -2509,6 +2509,7 @@ void LoadGarbageCollector()
#endif // FEATURE_STANDALONE_GC
+#ifndef FEATURE_STANDALONE_GC_ONLY
void LoadStaticGarbageCollector()
{
CONTRACTL{
@@ -2531,6 +2532,7 @@ void LoadStaticGarbageCollector()
g_pGCHandleManager = pGcHandleManager;
g_gcDacGlobals = &g_gc_dac_vars;
}
+#endif // FEATURE_STANDALONE_GC_ONLY
void InitializeGarbageCollector()
@@ -2567,7 +2569,9 @@ void InitializeGarbageCollector()
else
#endif // FEATURE_STANDALONE_GC
{
+#ifndef FEATURE_STANDALONE_GC_ONLY
LoadStaticGarbageCollector();
+#endif // FEATURE_STANDALONE_GC_ONLY
}
// Apparently the Windows linker removes global variables if they are never
diff --git a/src/vm/class.cpp b/src/vm/class.cpp
index 6697b23a9a..e4268b57ec 100644
--- a/src/vm/class.cpp
+++ b/src/vm/class.cpp
@@ -5,12 +5,6 @@
// File: CLASS.CPP
//
-
-//
-
-//
-// ============================================================================
-
#include "common.h"
#include "dllimport.h"
@@ -889,7 +883,15 @@ ClassLoader::LoadExactParentAndInterfacesTransitively(MethodTable *pMT)
LOG((LF_CLASSLOADER, LL_INFO1000, "GENERICS: Replaced approximate parent %s with exact parent %s from token %x\n", pParentMT->GetDebugClassName(), pNewParentMT->GetDebugClassName(), crExtends));
// SetParentMethodTable is not used here since we want to update the indirection cell in the NGen case
- *EnsureWritablePages(pMT->GetParentMethodTablePtr()) = pNewParentMT;
+ if (pMT->GetParentMethodTablePlainOrRelativePointerPtr()->IsIndirectPtrMaybeNull())
+ {
+ *EnsureWritablePages(pMT->GetParentMethodTablePlainOrRelativePointerPtr()->GetValuePtr()) = pNewParentMT;
+ }
+ else
+ {
+ EnsureWritablePages(pMT->GetParentMethodTablePlainOrRelativePointerPtr());
+ pMT->GetParentMethodTablePlainOrRelativePointerPtr()->SetValueMaybeNull(pNewParentMT);
+ }
pParentMT = pNewParentMT;
}
@@ -908,8 +910,11 @@ ClassLoader::LoadExactParentAndInterfacesTransitively(MethodTable *pMT)
DWORD nDicts = pParentMT->GetNumDicts();
for (DWORD iDict = 0; iDict < nDicts; iDict++)
{
- if (pMT->GetPerInstInfo()[iDict] != pParentMT->GetPerInstInfo()[iDict])
- *EnsureWritablePages(&pMT->GetPerInstInfo()[iDict]) = pParentMT->GetPerInstInfo()[iDict];
+ if (pMT->GetPerInstInfo()[iDict].GetValueMaybeNull() != pParentMT->GetPerInstInfo()[iDict].GetValueMaybeNull())
+ {
+ EnsureWritablePages(&pMT->GetPerInstInfo()[iDict]);
+ pMT->GetPerInstInfo()[iDict].SetValueMaybeNull(pParentMT->GetPerInstInfo()[iDict].GetValueMaybeNull());
+ }
}
}
@@ -1626,9 +1631,21 @@ MethodDesc* MethodTable::GetExistingUnboxedEntryPointMD(MethodDesc *pMD)
);
}
-#endif // !DACCESS_COMPILE
+#endif // !DACCESS_COMPILE
+
+//*******************************************************************************
+#if !defined(FEATURE_HFA)
+bool MethodTable::IsHFA()
+{
+ LIMITED_METHOD_CONTRACT;
+#ifdef DACCESS_COMPILE
+ return false;
+#else
+ return GetClass()->CheckForHFA();
+#endif
+}
+#endif // !FEATURE_HFA
-#ifdef FEATURE_HFA
//*******************************************************************************
CorElementType MethodTable::GetHFAType()
{
@@ -1683,6 +1700,214 @@ CorElementType MethodTable::GetNativeHFAType()
LIMITED_METHOD_CONTRACT;
return HasLayout() ? GetLayoutInfo()->GetNativeHFAType() : GetHFAType();
}
+
+//---------------------------------------------------------------------------------------
+//
+// When FEATURE_HFA is defined, we cache the value; otherwise we recompute it with each
+// call. The latter is only for the armaltjit and the arm64altjit.
+bool
+#if defined(FEATURE_HFA)
+EEClass::CheckForHFA(MethodTable ** pByValueClassCache)
+#else
+EEClass::CheckForHFA()
+#endif
+{
+ STANDARD_VM_CONTRACT;
+
+ // This method should be called for valuetypes only
+ _ASSERTE(GetMethodTable()->IsValueType());
+
+ // No HFAs with explicit layout. There may be cases where explicit layout may be still
+ // eligible for HFA, but it is hard to tell the real intent. Make it simple and just
+ // unconditionally disable HFAs for explicit layout.
+ if (HasExplicitFieldOffsetLayout())
+ return false;
+
+ CorElementType hfaType = ELEMENT_TYPE_END;
+
+ FieldDesc *pFieldDescList = GetFieldDescList();
+ for (UINT i = 0; i < GetNumInstanceFields(); i++)
+ {
+ FieldDesc *pFD = &pFieldDescList[i];
+ CorElementType fieldType = pFD->GetFieldType();
+
+ switch (fieldType)
+ {
+ case ELEMENT_TYPE_VALUETYPE:
+#if defined(FEATURE_HFA)
+ fieldType = pByValueClassCache[i]->GetHFAType();
+#else
+ fieldType = pFD->LookupApproxFieldTypeHandle().AsMethodTable()->GetHFAType();
+#endif
+ break;
+
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_R8:
+ break;
+
+ default:
+ // Not HFA
+ return false;
+ }
+
+ // Field type should be a valid HFA type.
+ if (fieldType == ELEMENT_TYPE_END)
+ {
+ return false;
+ }
+
+ // Initialize with a valid HFA type.
+ if (hfaType == ELEMENT_TYPE_END)
+ {
+ hfaType = fieldType;
+ }
+ // All field types should be equal.
+ else if (fieldType != hfaType)
+ {
+ return false;
+ }
+ }
+
+ if (hfaType == ELEMENT_TYPE_END)
+ return false;
+
+ int elemSize = (hfaType == ELEMENT_TYPE_R8) ? sizeof(double) : sizeof(float);
+
+ // Note that we check the total size, but do not perform any checks on number of fields:
+ // - Type of fields can be HFA valuetype itself
+ // - Managed C++ HFA valuetypes have just one <alignment member> of type float to signal that
+ // the valuetype is HFA and explicitly specified size
+
+ DWORD totalSize = GetMethodTable()->GetNumInstanceFieldBytes();
+
+ if (totalSize % elemSize != 0)
+ return false;
+
+ // On ARM, HFAs can have a maximum of four fields regardless of whether those are float or double.
+ if (totalSize / elemSize > 4)
+ return false;
+
+ // All the above tests passed. It's HFA!
+#if defined(FEATURE_HFA)
+ GetMethodTable()->SetIsHFA();
+#endif
+ return true;
+}
+
+CorElementType EEClassLayoutInfo::GetNativeHFATypeRaw()
+{
+ UINT numReferenceFields = GetNumCTMFields();
+
+ CorElementType hfaType = ELEMENT_TYPE_END;
+
+#ifndef DACCESS_COMPILE
+ const FieldMarshaler *pFieldMarshaler = GetFieldMarshalers();
+ while (numReferenceFields--)
+ {
+ CorElementType fieldType = ELEMENT_TYPE_END;
+
+ switch (pFieldMarshaler->GetNStructFieldType())
+ {
+ case NFT_COPY4:
+ case NFT_COPY8:
+ fieldType = pFieldMarshaler->GetFieldDesc()->GetFieldType();
+ if (fieldType != ELEMENT_TYPE_R4 && fieldType != ELEMENT_TYPE_R8)
+ return ELEMENT_TYPE_END;
+ break;
+
+ case NFT_NESTEDLAYOUTCLASS:
+ fieldType = ((FieldMarshaler_NestedLayoutClass *)pFieldMarshaler)->GetMethodTable()->GetNativeHFAType();
+ break;
+
+ case NFT_NESTEDVALUECLASS:
+ fieldType = ((FieldMarshaler_NestedValueClass *)pFieldMarshaler)->GetMethodTable()->GetNativeHFAType();
+ break;
+
+ case NFT_FIXEDARRAY:
+ fieldType = ((FieldMarshaler_FixedArray *)pFieldMarshaler)->GetElementTypeHandle().GetMethodTable()->GetNativeHFAType();
+ break;
+
+ case NFT_DATE:
+ fieldType = ELEMENT_TYPE_R8;
+ break;
+
+ default:
+ // Not HFA
+ return ELEMENT_TYPE_END;
+ }
+
+ // Field type should be a valid HFA type.
+ if (fieldType == ELEMENT_TYPE_END)
+ {
+ return ELEMENT_TYPE_END;
+ }
+
+ // Initialize with a valid HFA type.
+ if (hfaType == ELEMENT_TYPE_END)
+ {
+ hfaType = fieldType;
+ }
+ // All field types should be equal.
+ else if (fieldType != hfaType)
+ {
+ return ELEMENT_TYPE_END;
+ }
+
+ ((BYTE*&)pFieldMarshaler) += MAXFIELDMARSHALERSIZE;
+ }
+
+ if (hfaType == ELEMENT_TYPE_END)
+ return ELEMENT_TYPE_END;
+
+ int elemSize = (hfaType == ELEMENT_TYPE_R8) ? sizeof(double) : sizeof(float);
+
+ // Note that we check the total size, but do not perform any checks on number of fields:
+ // - Type of fields can be HFA valuetype itself
+ // - Managed C++ HFA valuetypes have just one <alignment member> of type float to signal that
+ // the valuetype is HFA and explicitly specified size
+
+ DWORD totalSize = GetNativeSize();
+
+ if (totalSize % elemSize != 0)
+ return ELEMENT_TYPE_END;
+
+ // On ARM, HFAs can have a maximum of four fields regardless of whether those are float or double.
+ if (totalSize / elemSize > 4)
+ return ELEMENT_TYPE_END;
+
+#endif // !DACCESS_COMPILE
+
+ return hfaType;
+}
+
+#ifdef FEATURE_HFA
+//
+// The managed and unmanaged views of the types can differ for non-blitable types. This method
+// mirrors the HFA type computation for the unmanaged view.
+//
+VOID EEClass::CheckForNativeHFA()
+{
+ STANDARD_VM_CONTRACT;
+
+ // No HFAs with inheritance
+ if (!(GetMethodTable()->IsValueType() || (GetMethodTable()->GetParentMethodTable() == g_pObjectClass)))
+ return;
+
+ // No HFAs with explicit layout. There may be cases where explicit layout may be still
+ // eligible for HFA, but it is hard to tell the real intent. Make it simple and just
+ // unconditionally disable HFAs for explicit layout.
+ if (HasExplicitFieldOffsetLayout())
+ return;
+
+ CorElementType hfaType = GetLayoutInfo()->GetNativeHFATypeRaw();
+ if (hfaType == ELEMENT_TYPE_END)
+ {
+ return;
+ }
+
+ // All the above tests passed. It's HFA!
+ GetLayoutInfo()->SetNativeHFAType(hfaType);
+}
#endif // FEATURE_HFA
#ifdef FEATURE_64BIT_ALIGNMENT
diff --git a/src/vm/class.h b/src/vm/class.h
index 60cab67707..8bbbf55b9b 100644
--- a/src/vm/class.h
+++ b/src/vm/class.h
@@ -2,19 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-// ==++==
-//
-//
-
-//
// ==--==
//
// File: CLASS.H
//
-
-//
-
//
// NOTE: Even though EEClass is considered to contain cold data (relative to MethodTable), these data
// structures *are* touched (especially during startup as part of soft-binding). As a result, and given the
@@ -526,6 +518,7 @@ class EEClassLayoutInfo
}
#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+ CorElementType GetNativeHFATypeRaw();
#ifdef FEATURE_HFA
bool IsNativeHFA()
{
@@ -540,7 +533,16 @@ class EEClassLayoutInfo
return (m_bFlags & e_R4_HFA) ? ELEMENT_TYPE_R4 : ELEMENT_TYPE_R8;
return ELEMENT_TYPE_END;
}
-#endif
+#else // !FEATURE_HFA
+ bool IsNativeHFA()
+ {
+ return GetNativeHFATypeRaw() != ELEMENT_TYPE_END;
+ }
+ CorElementType GetNativeHFAType()
+ {
+ return GetNativeHFATypeRaw();
+ }
+#endif // !FEATURE_HFA
private:
void SetIsBlittable(BOOL isBlittable)
@@ -1300,77 +1302,6 @@ public:
}
#endif
- inline BOOL IsCritical()
- {
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(HasCriticalTransparentInfo());
- return (m_VMFlags & VMFLAG_TRANSPARENCY_MASK) != VMFLAG_TRANSPARENCY_TRANSPARENT
- && !IsAllTransparent();
- }
-
- inline BOOL IsTreatAsSafe()
- {
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(HasCriticalTransparentInfo());
- return (m_VMFlags & VMFLAG_TRANSPARENCY_MASK) == VMFLAG_TRANSPARENCY_ALLCRITICAL_TAS ||
- (m_VMFlags & VMFLAG_TRANSPARENCY_MASK) == VMFLAG_TRANSPARENCY_TAS_NOTCRITICAL
- ;
- }
-
- inline BOOL IsAllTransparent()
- {
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(HasCriticalTransparentInfo());
- return (m_VMFlags & VMFLAG_TRANSPARENCY_MASK) == VMFLAG_TRANSPARENCY_ALL_TRANSPARENT;
- }
-
- inline BOOL IsAllCritical()
- {
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(HasCriticalTransparentInfo());
- return (m_VMFlags & VMFLAG_TRANSPARENCY_MASK) == VMFLAG_TRANSPARENCY_ALLCRITICAL
- || (m_VMFlags & VMFLAG_TRANSPARENCY_MASK) == VMFLAG_TRANSPARENCY_ALLCRITICAL_TAS;
- }
-
- inline BOOL HasCriticalTransparentInfo()
- {
- LIMITED_METHOD_CONTRACT;
- return (m_VMFlags & VMFLAG_TRANSPARENCY_MASK) != VMFLAG_TRANSPARENCY_UNKNOWN;
- }
-
- void SetCriticalTransparentInfo(
- BOOL fIsTreatAsSafe,
- BOOL fIsAllTransparent,
- BOOL fIsAllCritical)
- {
- WRAPPER_NO_CONTRACT;
-
- // TAS wihtout critical doesn't make sense - although it was allowed in the v2 desktop model,
- // so we need to allow it for compatibility reasons on the desktop.
- _ASSERTE(!fIsTreatAsSafe || fIsAllCritical);
-
- //if nothing is set, then we're transparent.
- unsigned flags = VMFLAG_TRANSPARENCY_TRANSPARENT;
-
- if (fIsAllTransparent)
- {
- flags = VMFLAG_TRANSPARENCY_ALL_TRANSPARENT;
- }
- else if (fIsAllCritical)
- {
- flags = fIsTreatAsSafe ? VMFLAG_TRANSPARENCY_ALLCRITICAL_TAS :
- VMFLAG_TRANSPARENCY_ALLCRITICAL;
- }
- else
- {
- flags = fIsTreatAsSafe ? VMFLAG_TRANSPARENCY_TAS_NOTCRITICAL :
- VMFLAG_TRANSPARENCY_TRANSPARENT;
- }
-
- FastInterlockOr(EnsureWritablePages(&m_VMFlags), flags);
-
- _ASSERTE(HasCriticalTransparentInfo());
- }
inline DWORD IsUnsafeValueClass()
{
LIMITED_METHOD_CONTRACT;
@@ -1398,29 +1329,6 @@ public:
}
public:
-
- inline void SetDoesNotHaveSuppressUnmanagedCodeAccessAttr()
- {
- WRAPPER_NO_CONTRACT;
- FastInterlockOr(EnsureWritablePages(&m_VMFlags),VMFLAG_NOSUPPRESSUNMGDCODEACCESS);
- }
-
- inline BOOL HasSuppressUnmanagedCodeAccessAttr()
- {
- LIMITED_METHOD_CONTRACT;
- return !(m_VMFlags & VMFLAG_NOSUPPRESSUNMGDCODEACCESS);
- }
-
- inline BOOL HasRemotingProxyAttribute()
- {
- LIMITED_METHOD_CONTRACT;
- return m_VMFlags & VMFLAG_REMOTING_PROXY_ATTRIBUTE;
- }
- inline void SetHasRemotingProxyAttribute()
- {
- LIMITED_METHOD_CONTRACT;
- m_VMFlags |= (DWORD)VMFLAG_REMOTING_PROXY_ATTRIBUTE;
- }
inline BOOL IsAlign8Candidate()
{
LIMITED_METHOD_CONTRACT;
@@ -1519,11 +1427,6 @@ public:
m_VMFlags |= VMFLAG_DELEGATE;
}
- // This is only applicable to interfaces. This method does not
- // provide correct information for non-interface types.
- DWORD SomeMethodsRequireInheritanceCheck();
- void SetSomeMethodsRequireInheritanceCheck();
-
BOOL HasFixedAddressVTStatics()
{
LIMITED_METHOD_CONTRACT;
@@ -1725,6 +1628,13 @@ public:
}
#endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if defined(FEATURE_HFA)
+ bool CheckForHFA(MethodTable ** pByValueClassCache);
+ VOID CheckForNativeHFA();
+#else // !FEATURE_HFA
+ bool CheckForHFA();
+#endif // FEATURE_HFA
+
#ifdef FEATURE_COMINTEROP
inline TypeHandle GetCoClassForInterface()
{
@@ -2014,35 +1924,7 @@ public:
#endif
VMFLAG_DELEGATE = 0x00000002,
- //Desktop
- // --------------
- //Flag | All Transparent | Critical | All Critical | TreatAsSafe
- //TRANSPARENT | 0 | 0 | 0 | 0
- //ALL_TRANSPARENT | 1 | 0 | 0 | 0
- //CRITICAL | 0 | 1 | 0 | 0
- //TAS_CRITICAL | 0 | 1 | 0 | 1
- //ALLCRITICAL | 0 | 0 | 1 | 0
- //ALLCRITICAL_TAS | 0 | 0 | 1 | 1
- //TAS_NOTCRITICAL | 0 | 0 | 0 | 1
- //
- //
- //On CoreCLR TAS implies Critical and "All Critical" and "Critical" are the same thing.
- //CoreCLR
- // --------------
- //Flag | All Transparent | Critical | TreatAsSafe
- //TRANSPARENT | 0 | 0 | 0
- //ALL_TRANSPARENT | 1 | 0 | 0
- //CRITICAL | 0 | 1 | 0
- //TAS_CRITICAL | 0 | 1 | 1
- VMFLAG_TRANSPARENCY_MASK = 0x0000001c,
- VMFLAG_TRANSPARENCY_UNKNOWN = 0x00000000,
- VMFLAG_TRANSPARENCY_TRANSPARENT = 0x00000004,
- VMFLAG_TRANSPARENCY_ALL_TRANSPARENT = 0x00000008,
- VMFLAG_TRANSPARENCY_CRITICAL = 0x0000000c,
- VMFLAG_TRANSPARENCY_CRITICAL_TAS = 0x00000010,
- VMFLAG_TRANSPARENCY_ALLCRITICAL = 0x00000014,
- VMFLAG_TRANSPARENCY_ALLCRITICAL_TAS = 0x00000018,
- VMFLAG_TRANSPARENCY_TAS_NOTCRITICAL = 0x0000001c,
+ // VMFLAG_UNUSED = 0x0000001c,
VMFLAG_FIXED_ADDRESS_VT_STATICS = 0x00000020, // Value type Statics in this class will be pinned
VMFLAG_HASLAYOUT = 0x00000040,
@@ -2068,13 +1950,13 @@ public:
VMFLAG_BESTFITMAPPING = 0x00004000, // BestFitMappingAttribute.Value
VMFLAG_THROWONUNMAPPABLECHAR = 0x00008000, // BestFitMappingAttribute.ThrowOnUnmappableChar
- VMFLAG_NOSUPPRESSUNMGDCODEACCESS = 0x00010000,
+ // unused = 0x00010000,
VMFLAG_NO_GUID = 0x00020000,
VMFLAG_HASNONPUBLICFIELDS = 0x00040000,
- VMFLAG_REMOTING_PROXY_ATTRIBUTE = 0x00080000,
+ // unused = 0x00080000,
VMFLAG_CONTAINS_STACK_PTR = 0x00100000,
VMFLAG_PREFER_ALIGN8 = 0x00200000, // Would like to have 8-byte alignment
- VMFLAG_METHODS_REQUIRE_INHERITANCE_CHECKS = 0x00400000,
+ // unused = 0x00400000,
#ifdef FEATURE_COMINTEROP
VMFLAG_SPARSE_FOR_COMINTEROP = 0x00800000,
diff --git a/src/vm/class.inl b/src/vm/class.inl
index 78e05cdd14..d411f817d2 100644
--- a/src/vm/class.inl
+++ b/src/vm/class.inl
@@ -5,12 +5,6 @@
// File: CLASS.INL
//
-
-//
-
-//
-// ============================================================================
-
#ifndef _CLASS_INL_
#define _CLASS_INL_
//***************************************************************************************
@@ -20,18 +14,6 @@ inline PTR_MethodDescChunk EEClass::GetChunks()
return m_pChunks.GetValueMaybeNull(PTR_HOST_MEMBER_TADDR(EEClass, this, m_pChunks));
}
-//***************************************************************************************
-inline DWORD EEClass::SomeMethodsRequireInheritanceCheck()
-{
- return (m_VMFlags & VMFLAG_METHODS_REQUIRE_INHERITANCE_CHECKS);
-}
-
-//***************************************************************************************
-inline void EEClass::SetSomeMethodsRequireInheritanceCheck()
-{
- m_VMFlags = m_VMFlags | VMFLAG_METHODS_REQUIRE_INHERITANCE_CHECKS;
-}
-
//*******************************************************************************
#ifndef DACCESS_COMPILE
// Set default values for optional fields.
diff --git a/src/vm/classcompat.cpp b/src/vm/classcompat.cpp
index 031604bc8e..fb97a79e85 100644
--- a/src/vm/classcompat.cpp
+++ b/src/vm/classcompat.cpp
@@ -31,7 +31,6 @@
#include "fieldmarshaler.h"
#include "cgensys.h"
#include "gcheaputilities.h"
-#include "security.h"
#include "dbginterface.h"
#include "comdelegate.h"
#include "sigformat.h"
@@ -54,7 +53,6 @@
#include "clrtocomcall.h"
#include "runtimecallablewrapper.h"
-#include "listlock.inl"
#include "generics.h"
#include "contractimpl.h"
@@ -1308,11 +1306,6 @@ VOID MethodTableBuilder::BuildInteropVTable_PlaceVtableMethods(
// The interface we are attempting to place
MethodTable *pInterface = pCurItfInfo->m_pMethodTable;
- _ASSERTE(!(pCurItfInfo->IsDeclaredOnClass() &&
- !pInterface->IsExternallyVisible() &&
- pInterface->GetAssembly() != bmtType->pModule->GetAssembly() &&
- !Security::CanSkipVerification(GetAssembly()->GetDomainAssembly())));
-
// Did we place this interface already due to the parent class's interface placement?
if (pCurItfInfo->GetInteropStartSlot() != MethodTable::NO_SLOT)
{
diff --git a/src/vm/classnames.h b/src/vm/classnames.h
index 0c24914a56..fc087372c8 100644
--- a/src/vm/classnames.h
+++ b/src/vm/classnames.h
@@ -145,6 +145,7 @@
#define g_CompilerServicesFixedAddressValueTypeAttribute "System.Runtime.CompilerServices.FixedAddressValueTypeAttribute"
#define g_CompilerServicesUnsafeValueTypeAttribute "System.Runtime.CompilerServices.UnsafeValueTypeAttribute"
+#define g_CompilerServicesIntrinsicAttribute "System.Runtime.CompilerServices.IntrinsicAttribute"
#define g_UnmanagedFunctionPointerAttribute "System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute"
#define g_DefaultDllImportSearchPathsAttribute "System.Runtime.InteropServices.DefaultDllImportSearchPathsAttribute"
#define g_NativeCallableAttribute "System.Runtime.InteropServices.NativeCallableAttribute"
diff --git a/src/vm/clrex.cpp b/src/vm/clrex.cpp
index ba040b7e81..3b21d649d6 100644
--- a/src/vm/clrex.cpp
+++ b/src/vm/clrex.cpp
@@ -1224,7 +1224,7 @@ OBJECTREF EEException::CreateThrowable()
#endif
}
-RuntimeExceptionKind EEException::GetKindFromHR(HRESULT hr, bool fIsWinRtMode)
+RuntimeExceptionKind EEException::GetKindFromHR(HRESULT hr, bool fIsWinRtMode /*= false*/)
{
LIMITED_METHOD_CONTRACT;
diff --git a/src/vm/clrex.h b/src/vm/clrex.h
index ce55ebcefa..12eb702be1 100644
--- a/src/vm/clrex.h
+++ b/src/vm/clrex.h
@@ -1095,7 +1095,7 @@ inline EEMessageException::EEMessageException(HRESULT hr)
}
inline EEMessageException::EEMessageException(HRESULT hr, bool fUseCOMException)
- : EEException(GetKindFromHR(hr, fUseCOMException)),
+ : EEException(GetKindFromHR(hr, !fUseCOMException)),
m_hr(hr),
m_resID(0)
{
diff --git a/src/vm/clsload.cpp b/src/vm/clsload.cpp
index da48549e7b..22b030caa0 100644
--- a/src/vm/clsload.cpp
+++ b/src/vm/clsload.cpp
@@ -23,7 +23,6 @@
#include "comsynchronizable.h"
#include "threads.h"
#include "dllimport.h"
-#include "security.h"
#include "dbginterface.h"
#include "log.h"
#include "eeconfig.h"
@@ -4869,27 +4868,6 @@ StaticAccessCheckContext::StaticAccessCheckContext(MethodDesc* pCallerMethod, Me
m_pCallerAssembly = pCallerType->GetAssembly();
}
-// Critical callers do not need the extra access checks
-bool StaticAccessCheckContext::IsCallerCritical()
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if (m_pCallerMethod == NULL || !Security::IsMethodTransparent(m_pCallerMethod))
- {
- return true;
- }
-
- return false;
-}
-
-
-
//******************************************************************************
// static
@@ -4911,8 +4889,7 @@ void AccessCheckOptions::Startup()
//******************************************************************************
AccessCheckOptions::AccessCheckOptions(
const AccessCheckOptions & templateOptions,
- BOOL throwIfTargetIsInaccessible,
- BOOL skipCheckForCriticalCode /*=FALSE*/) :
+ BOOL throwIfTargetIsInaccessible) :
m_pAccessContext(templateOptions.m_pAccessContext)
{
WRAPPER_NO_CONTRACT;
@@ -4922,8 +4899,7 @@ AccessCheckOptions::AccessCheckOptions(
throwIfTargetIsInaccessible,
templateOptions.m_pTargetMT,
templateOptions.m_pTargetMethod,
- templateOptions.m_pTargetField,
- skipCheckForCriticalCode);
+ templateOptions.m_pTargetField);
}
//******************************************************************************
@@ -4978,36 +4954,15 @@ BOOL AccessCheckOptions::DemandMemberAccess(AccessCheckContext *pContext, Method
// classes/members in app code.
if (m_accessCheckType != kMemberAccess && pTargetMT)
{
- if (visibilityCheck && Security::IsTransparencyEnforcementEnabled())
- {
- // In CoreCLR RMA means visibility checks always succeed if the target is user code.
- if (m_accessCheckType == kRestrictedMemberAccess || m_accessCheckType == kRestrictedMemberAccessNoTransparency)
- return TRUE;
-
- // Accessing private types/members in platform code.
- fAccessingFrameworkCode = TRUE;
- }
- else
- {
- // We allow all transparency checks to succeed in LCG methods and reflection invocation.
- if (m_accessCheckType == kNormalAccessNoTransparency || m_accessCheckType == kRestrictedMemberAccessNoTransparency)
- return TRUE;
- }
+ // We allow all transparency checks to succeed in LCG methods and reflection invocation.
+ if (m_accessCheckType == kNormalAccessNoTransparency || m_accessCheckType == kRestrictedMemberAccessNoTransparency)
+ return TRUE;
}
// Always allow interop (NULL) callers full access.
if (pContext->IsCalledFromInterop())
return TRUE;
- MethodDesc* pCallerMD = pContext->GetCallerMethod();
-
- // critical code is exempted from all accessibility rules, regardless of the AccessCheckType.
- if (pCallerMD != NULL &&
- !Security::IsMethodTransparent(pCallerMD))
- {
- return TRUE;
- }
-
// No Access
if (m_fThrowIfTargetIsInaccessible)
{
@@ -5090,15 +5045,6 @@ BOOL AccessCheckOptions::DemandMemberAccessOrFail(AccessCheckContext *pContext,
}
CONTRACTL_END;
- // m_fSkipCheckForCriticalCode is only ever set to true for CanAccessMemberForExtraChecks.
- // For legacy compat we allow the access check to succeed for all AccessCheckType if the caller is critical.
- if (m_fSkipCheckForCriticalCode)
- {
- if (pContext->IsCalledFromInterop() ||
- !Security::IsMethodTransparent(pContext->GetCallerMethod()))
- return TRUE;
- }
-
if (DoNormalAccessibilityChecks())
{
if (pContext->GetCallerAssembly()->IgnoresAccessChecksTo(pTargetMT->GetAssembly()))
@@ -5131,15 +5077,6 @@ BOOL AccessCheckOptions::FailOrThrow(AccessCheckContext *pContext) const
}
CONTRACTL_END;
- // m_fSkipCheckForCriticalCode is only ever set to true for CanAccessMemberForExtraChecks.
- // For legacy compat we allow the access check to succeed for all AccessCheckType if the caller is critical.
- if (m_fSkipCheckForCriticalCode)
- {
- if (pContext->IsCalledFromInterop() ||
- !Security::IsMethodTransparent(pContext->GetCallerMethod()))
- return TRUE;
- }
-
if (m_fThrowIfTargetIsInaccessible)
{
ThrowAccessException(pContext);
@@ -5151,7 +5088,6 @@ BOOL AccessCheckOptions::FailOrThrow(AccessCheckContext *pContext) const
// Generate access exception context strings that are due to potential security misconfiguration
void GetAccessExceptionAdditionalContextForSecurity(Assembly *pAccessingAssembly,
Assembly *pTargetAssembly,
- BOOL isTransparencyError,
BOOL fAccessingFrameworkCode,
StringArrayList *pContextInformation)
{
@@ -5182,7 +5118,6 @@ void GetAccessExceptionAdditionalContextForSecurity(Assembly *pAccessingAssembly
// context is available, then this returns SString.Empty.
SString GetAdditionalAccessExceptionContext(Assembly *pAccessingAssembly,
Assembly *pTargetAssembly,
- BOOL isTransparencyError,
BOOL fAccessingFrameworkCode)
{
CONTRACTL
@@ -5200,7 +5135,6 @@ SString GetAdditionalAccessExceptionContext(Assembly *pAccessingAssembly,
// See if the exception may have been caused by security
GetAccessExceptionAdditionalContextForSecurity(pAccessingAssembly,
pTargetAssembly,
- isTransparencyError,
fAccessingFrameworkCode,
&contextComponents);
@@ -5236,15 +5170,10 @@ void DECLSPEC_NORETURN ThrowFieldAccessException(AccessCheckContext* pContext,
}
CONTRACTL_END;
- BOOL isTransparencyError = FALSE;
-
MethodDesc* pCallerMD = pContext->GetCallerMethod();
- if (pCallerMD != NULL)
- isTransparencyError = !Security::CheckCriticalAccess(pContext, NULL, pFD, NULL);
ThrowFieldAccessException(pCallerMD,
pFD,
- isTransparencyError,
messageID,
pInnerException,
fAccessingFrameworkCode);
@@ -5252,7 +5181,6 @@ void DECLSPEC_NORETURN ThrowFieldAccessException(AccessCheckContext* pContext,
void DECLSPEC_NORETURN ThrowFieldAccessException(MethodDesc* pCallerMD,
FieldDesc *pFD,
- BOOL isTransparencyError,
UINT messageID /* = 0 */,
Exception *pInnerException /* = NULL */,
BOOL fAccessingFrameworkCode /* = FALSE */)
@@ -5271,22 +5199,11 @@ void DECLSPEC_NORETURN ThrowFieldAccessException(MethodDesc* pCallerMD,
{
if (messageID == 0)
{
- // Figure out if we can give a specific reason why this field access was rejected - for instance, if
- // we see that the caller is transparent and accessing a critical field, then we can put that
- // information into the exception message.
- if (isTransparencyError)
- {
- messageID = IDS_E_CRITICAL_FIELD_ACCESS_DENIED;
- }
- else
- {
- messageID = IDS_E_FIELDACCESS;
- }
+ messageID = IDS_E_FIELDACCESS;
}
SString strAdditionalContext = GetAdditionalAccessExceptionContext(pCallerMD->GetAssembly(),
pFD->GetApproxEnclosingMethodTable()->GetAssembly(),
- isTransparencyError,
fAccessingFrameworkCode);
EX_THROW_WITH_INNER(EEFieldException, (pFD, pCallerMD, strAdditionalContext, messageID), pInnerException);
@@ -5313,15 +5230,10 @@ void DECLSPEC_NORETURN ThrowMethodAccessException(AccessCheckContext* pContext,
}
CONTRACTL_END;
- BOOL isTransparencyError = FALSE;
-
MethodDesc* pCallerMD = pContext->GetCallerMethod();
- if (pCallerMD != NULL)
- isTransparencyError = !Security::CheckCriticalAccess(pContext, pCalleeMD, NULL, NULL);
ThrowMethodAccessException(pCallerMD,
pCalleeMD,
- isTransparencyError,
messageID,
pInnerException,
fAccessingFrameworkCode);
@@ -5329,7 +5241,6 @@ void DECLSPEC_NORETURN ThrowMethodAccessException(AccessCheckContext* pContext,
void DECLSPEC_NORETURN ThrowMethodAccessException(MethodDesc* pCallerMD,
MethodDesc *pCalleeMD,
- BOOL isTransparencyError,
UINT messageID /* = 0 */,
Exception *pInnerException /* = NULL */,
BOOL fAccessingFrameworkCode /* = FALSE */)
@@ -5348,22 +5259,11 @@ void DECLSPEC_NORETURN ThrowMethodAccessException(MethodDesc* pCallerMD,
{
if (messageID == 0)
{
- // Figure out if we can give a specific reason why this method access was rejected - for instance, if
- // we see that the caller is transparent and the callee is critical, then we can put that
- // information into the exception message.
- if (isTransparencyError)
- {
- messageID = IDS_E_CRITICAL_METHOD_ACCESS_DENIED;
- }
- else
- {
- messageID = IDS_E_METHODACCESS;
- }
+ messageID = IDS_E_METHODACCESS;
}
SString strAdditionalContext = GetAdditionalAccessExceptionContext(pCallerMD->GetAssembly(),
pCalleeMD->GetAssembly(),
- isTransparencyError,
fAccessingFrameworkCode);
EX_THROW_WITH_INNER(EEMethodException, (pCalleeMD, pCallerMD, strAdditionalContext, messageID), pInnerException);
@@ -5390,15 +5290,10 @@ void DECLSPEC_NORETURN ThrowTypeAccessException(AccessCheckContext* pContext,
}
CONTRACTL_END;
- BOOL isTransparencyError = FALSE;
-
MethodDesc* pCallerMD = pContext->GetCallerMethod();
- if (pCallerMD != NULL)
- isTransparencyError = !Security::CheckCriticalAccess(pContext, NULL, NULL, pMT);
ThrowTypeAccessException(pCallerMD,
pMT,
- isTransparencyError,
messageID,
pInnerException,
fAccessingFrameworkCode);
@@ -5406,7 +5301,6 @@ void DECLSPEC_NORETURN ThrowTypeAccessException(AccessCheckContext* pContext,
void DECLSPEC_NORETURN ThrowTypeAccessException(MethodDesc* pCallerMD,
MethodTable *pMT,
- BOOL isTransparencyError,
UINT messageID /* = 0 */,
Exception *pInnerException /* = NULL */,
BOOL fAccessingFrameworkCode /* = FALSE */)
@@ -5425,22 +5319,11 @@ void DECLSPEC_NORETURN ThrowTypeAccessException(MethodDesc* pCallerMD,
{
if (messageID == 0)
{
- // Figure out if we can give a specific reason why this type access was rejected - for instance, if
- // we see that the caller is transparent and is accessing a critical type, then we can put that
- // information into the exception message.
- if (isTransparencyError)
- {
- messageID = IDS_E_CRITICAL_TYPE_ACCESS_DENIED;
- }
- else
- {
- messageID = IDS_E_TYPEACCESS;
- }
+ messageID = IDS_E_TYPEACCESS;
}
SString strAdditionalContext = GetAdditionalAccessExceptionContext(pCallerMD->GetAssembly(),
pMT->GetAssembly(),
- isTransparencyError,
fAccessingFrameworkCode);
EX_THROW_WITH_INNER(EETypeAccessException, (pMT, pCallerMD, strAdditionalContext, messageID), pInnerException);
@@ -5451,51 +5334,6 @@ void DECLSPEC_NORETURN ThrowTypeAccessException(MethodDesc* pCallerMD,
}
}
-//******************************************************************************
-// This function determines whether a method [if transparent]
-// can access a specified target (e.g. Type, Method, Field)
-static BOOL CheckTransparentAccessToCriticalCode(
- AccessCheckContext* pContext,
- DWORD dwMemberAccess,
- MethodTable* pTargetMT,
- MethodDesc* pOptionalTargetMethod,
- FieldDesc* pOptionalTargetField,
- MethodTable* pOptionalTargetType,
- const AccessCheckOptions & accessCheckOptions)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- PRECONDITION(CheckPointer(pContext));
- PRECONDITION(accessCheckOptions.TransparencyCheckNeeded());
- }
- CONTRACTL_END;
-
- if (!Security::IsTransparencyEnforcementEnabled())
- return TRUE;
-
- // At most one of these should be non-NULL
- _ASSERTE(1 >= ((pOptionalTargetMethod ? 1 : 0) +
- (pOptionalTargetField ? 1 : 0) +
- (pOptionalTargetType ? 1 : 0)));
-
-
- // if the caller [Method] is transparent, do special security checks
- // check if security disallows access to target member
- if (!Security::CheckCriticalAccess(
- pContext,
- pOptionalTargetMethod,
- pOptionalTargetField,
- pOptionalTargetType))
- {
- return accessCheckOptions.DemandMemberAccessOrFail(pContext, pTargetMT, FALSE /*visibilityCheck*/);
- }
-
- return TRUE;
-} // static BOOL CheckTransparentAccessToCriticalCode
-
//---------------------------------------------------------------------------------------
//
// Checks to see if access to a member with assembly visiblity is allowed.
@@ -5620,8 +5458,7 @@ BOOL ClassLoader::CanAccessClass( // True if access is legal,
AccessCheckContext* pContext, // The caller context
MethodTable* pTargetClass, // The desired target class.
Assembly* pTargetAssembly, // Assembly containing the target class.
- const AccessCheckOptions & accessCheckOptions,
- BOOL checkTargetTypeTransparency)// = TRUE
+ const AccessCheckOptions & accessCheckOptions)// = TRUE
{
CONTRACTL
{
@@ -5639,26 +5476,6 @@ BOOL ClassLoader::CanAccessClass( // True if access is legal,
//if (!pTargetClass)
// return TRUE;
- // check transparent/critical on type
- // Note that dwMemberAccess is of no use here since we don't have a target method yet. It really should be made an optional arg.
- // For now, we pass in mdPublic.
- if (checkTargetTypeTransparency && accessCheckOptions.TransparencyCheckNeeded())
- {
- if (!CheckTransparentAccessToCriticalCode(
- pContext,
- mdPublic,
- pTargetClass,
- NULL,
- NULL,
- pTargetClass,
- accessCheckOptions))
- {
- // no need to call accessCheckOptions.DemandMemberAccessOrFail here because
- // CheckTransparentAccessToCriticalCode does that already
- return FALSE;
- }
- }
-
// Step 2: Recursively call CanAccessClass on the generic type arguments
// Is the desired target a generic instantiation?
if (pTargetClass->HasInstantiation())
@@ -5679,8 +5496,7 @@ BOOL ClassLoader::CanAccessClass( // True if access is legal,
pContext,
pMT,
th.GetAssembly(),
- accessCheckOptions,
- checkTargetTypeTransparency))
+ accessCheckOptions))
{
// no need to call accessCheckOptions.DemandMemberAccessOrFail here because the base case in
// CanAccessClass does that already
@@ -5780,23 +5596,14 @@ BOOL ClassLoader::CanAccessClass( // True if access is legal,
dwProtection,
NULL,
NULL,
- accessCheckOptions,
- FALSE,
- FALSE);
+ accessCheckOptions);
} // BOOL ClassLoader::CanAccessClass()
//******************************************************************************
// This is a front-end to CheckAccessMember that handles the nested class scope. If can't access
// from the current point and are a nested class, then try from the enclosing class.
-// It does two things in addition to CanAccessMember:
-// 1. If the caller class doesn't have access to the caller, see if the enclosing class does.
-// 2. CanAccessMemberForExtraChecks which checks whether the caller class has access to
-// the signature of the target method or field.
+// In addition to CanAccessMember, if the caller class doesn't have access to the caller, see if the enclosing class does.
//
-// checkTargetMethodTransparency is set to FALSE only when the check is for JIT-compilation
-// because the JIT has a mechanism to insert a callout for the case where
-// we need to perform the currentMD <-> TargetMD check at runtime.
-
/* static */
BOOL ClassLoader::CanAccess( // TRUE if access is allowed, FALSE otherwise.
AccessCheckContext* pContext, // The caller context
@@ -5806,9 +5613,7 @@ BOOL ClassLoader::CanAccess( // TRUE if access is all
MethodDesc* pOptionalTargetMethod, // The target method; NULL if the target is a not a method or
// there is no need to check the method's instantiation.
FieldDesc* pOptionalTargetField, // or The desired field; if NULL, return TRUE
- const AccessCheckOptions & accessCheckOptions, // = s_NormalAccessChecks
- BOOL checkTargetMethodTransparency, // = TRUE
- BOOL checkTargetTypeTransparency) // = TRUE
+ const AccessCheckOptions & accessCheckOptions) // = s_NormalAccessChecks
{
CONTRACT(BOOL)
{
@@ -5833,9 +5638,7 @@ BOOL ClassLoader::CanAccess( // TRUE if access is all
pOptionalTargetField,
// Suppress exceptions for nested classes since this is not a hard-failure,
// and we can do additional checks
- accessCheckOptionsNoThrow,
- checkTargetMethodTransparency,
- checkTargetTypeTransparency))
+ accessCheckOptionsNoThrow))
{
// If we're here, CheckAccessMember didn't allow access.
BOOL canAccess = FALSE;
@@ -5869,9 +5672,7 @@ BOOL ClassLoader::CanAccess( // TRUE if access is all
dwMemberAccess,
pOptionalTargetMethod,
pOptionalTargetField,
- accessCheckOptionsNoThrow,
- checkTargetMethodTransparency,
- checkTargetTypeTransparency);
+ accessCheckOptionsNoThrow);
}
if (!canAccess)
@@ -5881,212 +5682,12 @@ BOOL ClassLoader::CanAccess( // TRUE if access is all
}
}
- // For member access, we do additional checks to ensure that the specific member can
- // be accessed
-
- if (!CanAccessMemberForExtraChecks(
- pContext,
- pTargetMT,
- pOptionalTargetMethod,
- pOptionalTargetField,
- accessCheckOptions,
- checkTargetMethodTransparency))
- {
- RETURN_FROM_INTERIOR_PROBE(FALSE);
- }
-
RETURN_FROM_INTERIOR_PROBE(TRUE);
END_INTERIOR_STACK_PROBE;
} // BOOL ClassLoader::CanAccess()
//******************************************************************************
-// Performs additional checks for member access
-
-BOOL ClassLoader::CanAccessMemberForExtraChecks(
- AccessCheckContext* pContext,
- MethodTable* pTargetExactMT,
- MethodDesc* pOptionalTargetMethod,
- FieldDesc* pOptionalTargetField,
- const AccessCheckOptions & accessCheckOptions,
- BOOL checkTargetMethodTransparency)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- PRECONDITION(CheckPointer(pContext));
- }
- CONTRACTL_END;
-
- // Critical callers do not need the extra checks
- // This early-out saves the cost of all the subsequent work
- if (pContext->IsCallerCritical())
- {
- return TRUE;
- }
-
- if (pOptionalTargetMethod == NULL && pOptionalTargetField == NULL)
- return TRUE;
-
- _ASSERTE((pOptionalTargetMethod == NULL) != (pOptionalTargetField == NULL));
-
- // We should always do checks on member signatures. But for backward compatibility we skip this check
- // for critical callers. And since we don't want to look for the caller here which might incur a stack walk,
- // we delay the check to DemandMemberAccessOrFail time.
- AccessCheckOptions legacyAccessCheckOptions(accessCheckOptions, accessCheckOptions.Throws(), TRUE);
-
- if (pOptionalTargetMethod)
- {
- // A method is accessible only if all the types in the signature
- // are also accessible.
- if (!CanAccessSigForExtraChecks(pContext,
- pOptionalTargetMethod,
- pTargetExactMT,
- legacyAccessCheckOptions,
- checkTargetMethodTransparency))
- {
- return FALSE;
- }
- }
- else
- {
- _ASSERTE(pOptionalTargetField != NULL);
-
- // A field is accessible only if the field type is also accessible
-
- TypeHandle fieldType = pOptionalTargetField->GetExactFieldType(TypeHandle(pTargetExactMT));
- CorElementType fieldCorType = fieldType.GetSignatureCorElementType();
-
- MethodTable * pFieldTypeMT = fieldType.GetMethodTableOfElementType();
-
- // No access check needed on a generic variable or a function pointer
- if (pFieldTypeMT != NULL)
- {
- if (!CanAccessClassForExtraChecks(pContext,
- pFieldTypeMT,
- pFieldTypeMT->GetAssembly(),
- legacyAccessCheckOptions,
- TRUE))
- {
- return FALSE;
- }
- }
- }
-
- return TRUE;
-}
-
-//******************************************************************************
-// Can all the types in the signature of the pTargetMethodSig be accessed?
-//
-// "ForExtraChecks" means that we only do extra checks (security and transparency)
-// instead of the usual loader visibility checks. Post V2, we can enable all checks.
-
-BOOL ClassLoader::CanAccessSigForExtraChecks( // TRUE if access is allowed, FALSE otherwise.
- AccessCheckContext* pContext,
- MethodDesc* pTargetMethodSig, // The target method. If this is a shared method, pTargetExactMT gives
- // additional information about the exact type
- MethodTable* pTargetExactMT, // or The desired field; if NULL, return TRUE
- const AccessCheckOptions & accessCheckOptions,
- BOOL checkTargetTransparency)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- PRECONDITION(CheckPointer(pContext));
- }
- CONTRACTL_END;
-
- MetaSig sig(pTargetMethodSig, TypeHandle(pTargetExactMT));
-
- // First, check the return type
-
- TypeHandle retType = sig.GetRetTypeHandleThrowing();
- MethodTable * pRetMT = retType.GetMethodTableOfElementType();
-
- // No access check needed on a generic variable or a function pointer
- if (pRetMT != NULL)
- {
- if (!CanAccessClassForExtraChecks(pContext,
- pRetMT,
- retType.GetAssembly(),
- accessCheckOptions,
- checkTargetTransparency))
- {
- return FALSE;
- }
- }
-
- //
- // Now walk all the arguments in the signature
- //
-
- for (CorElementType argType = sig.NextArg(); argType != ELEMENT_TYPE_END; argType = sig.NextArg())
- {
- TypeHandle thArg = sig.GetLastTypeHandleThrowing();
-
- MethodTable * pArgMT = thArg.GetMethodTableOfElementType();
-
- // Either a TypeVarTypeDesc or a FnPtrTypeDesc. No access check needed.
- if (pArgMT == NULL)
- continue;
-
- BOOL canAcesssElement = CanAccessClassForExtraChecks(
- pContext,
- pArgMT,
- thArg.GetAssembly(),
- accessCheckOptions,
- checkTargetTransparency);
- if (!canAcesssElement)
- {
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-//******************************************************************************
-// Can the type be accessed?
-//
-// "ForExtraChecks" means that we only do extra checks (security and transparency)
-// instead of the usual loader visibility checks. Post V2, we can enable all checks.
-
-BOOL ClassLoader::CanAccessClassForExtraChecks( // True if access is legal, false otherwise.
- AccessCheckContext* pContext,
- MethodTable* pTargetClass, // The desired target class.
- Assembly* pTargetAssembly, // Assembly containing that class.
- const AccessCheckOptions & accessCheckOptions,
- BOOL checkTargetTypeTransparency)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- PRECONDITION(CheckPointer(pContext));
- }
- CONTRACTL_END;
-
- // ------------- Old comments begins ------------
- // Critical callers do not need the extra checks
- // TODO: can we enable full access checks now?
- // ------------- Old comments ends ------------
-
- // We shouldn't bypass accessibility check on member signature for FT/Critical callers
-
- return CanAccessClass(pContext,
- pTargetClass,
- pTargetAssembly,
- accessCheckOptions,
- checkTargetTypeTransparency);
-}
-
-//******************************************************************************
// This is the helper function for the corresponding CanAccess()
// It does the following checks:
// 1. CanAccessClass on pTargetMT
@@ -6103,9 +5704,7 @@ BOOL ClassLoader::CheckAccessMember( // TRUE if access is allowed
MethodDesc* pOptionalTargetMethod, // The target method; NULL if the target is a not a method or
// there is no need to check the method's instantiation.
FieldDesc* pOptionalTargetField, // target field, NULL if there is no Target field
- const AccessCheckOptions & accessCheckOptions,
- BOOL checkTargetMethodTransparency,
- BOOL checkTargetTypeTransparency
+ const AccessCheckOptions & accessCheckOptions
)
{
CONTRACTL
@@ -6124,17 +5723,13 @@ BOOL ClassLoader::CheckAccessMember( // TRUE if access is allowed
if (!CanAccessClass(pContext,
pTargetMT,
pTargetAssembly,
- accessCheckOptions,
- checkTargetTypeTransparency))
+ accessCheckOptions))
{
return FALSE;
}
// If we are trying to access a generic method, we have to ensure its instantiation is accessible.
// Note that we need to perform transparency checks on the instantiation even if we have
- // checkTargetMethodTransparency set to false, since generic type parameters by design do not effect
- // the transparency of the generic method that is closing over them. This means standard transparency
- // checks between caller and closed callee may succeed even if the callee's closure includes a critical type.
if (!CanAccessMethodInstantiation(
pContext,
pOptionalTargetMethod,
@@ -6150,23 +5745,6 @@ BOOL ClassLoader::CheckAccessMember( // TRUE if access is allowed
// We don't need to do transparency check against pTargetMT here because
// it was already done in CanAccessClass above.
- if (accessCheckOptions.TransparencyCheckNeeded() &&
- ((checkTargetMethodTransparency && pOptionalTargetMethod) ||
- pOptionalTargetField))
- {
- if (!CheckTransparentAccessToCriticalCode(
- pContext,
- dwMemberAccess,
- pTargetMT,
- pOptionalTargetMethod,
- pOptionalTargetField,
- NULL,
- accessCheckOptions))
- {
- return FALSE;
- }
- }
-
if (IsMdPublic(dwMemberAccess))
{
return TRUE;
diff --git a/src/vm/clsload.hpp b/src/vm/clsload.hpp
index 656f260e01..5a9248e422 100644
--- a/src/vm/clsload.hpp
+++ b/src/vm/clsload.hpp
@@ -317,7 +317,6 @@ public:
virtual MethodTable* GetCallerMT() = 0; // The class that wants access; NULL if interop caller.
virtual Assembly* GetCallerAssembly() = 0; // Assembly containing that class.
virtual bool IsCalledFromInterop() = 0;
- virtual bool IsCallerCritical() = 0; // Can we do a quick check for caller's transparency status?
};
class StaticAccessCheckContext : public AccessCheckContext
@@ -367,8 +366,6 @@ public:
return false;
}
- virtual bool IsCallerCritical();
-
private:
MethodDesc* m_pCallerMethod;
MethodTable* m_pCallerMT;
@@ -429,8 +426,7 @@ public:
AccessCheckOptions(
const AccessCheckOptions & templateAccessCheckOptions,
- BOOL throwIfTargetIsInaccessible,
- BOOL skipCheckForCriticalCode = FALSE);
+ BOOL throwIfTargetIsInaccessible);
// Follow standard rules for doing accessability
BOOL DoNormalAccessibilityChecks() const
@@ -471,8 +467,7 @@ private:
BOOL throwIfTargetIsInaccessible,
MethodTable * pTargetMT,
MethodDesc * pTargetMD,
- FieldDesc * pTargetFD,
- BOOL skipCheckForCriticalCode = FALSE);
+ FieldDesc * pTargetFD);
BOOL DemandMemberAccess(AccessCheckContext *pContext, MethodTable * pTargetMT, BOOL visibilityCheck) const;
@@ -493,27 +488,22 @@ private:
DynamicResolver * m_pAccessContext;
// If the target is not accessible, should the API return FALSE, or should it throw an exception?
BOOL m_fThrowIfTargetIsInaccessible;
- // flag to enable legacy behavior in ClassLoader::CanAccessMemberForExtraChecks.
- BOOL m_fSkipCheckForCriticalCode;
};
void DECLSPEC_NORETURN ThrowFieldAccessException(MethodDesc *pCallerMD,
FieldDesc *pFD,
- BOOL isTransparencyError,
UINT messageID = 0,
Exception *pInnerException = NULL,
BOOL fAccessingFrameworkCode = FALSE);
void DECLSPEC_NORETURN ThrowMethodAccessException(MethodDesc *pCallerMD,
MethodDesc *pCalleeMD,
- BOOL isTransparencyError,
UINT messageID = 0,
Exception *pInnerException = NULL,
BOOL fAccessingFrameworkCode = FALSE);
void DECLSPEC_NORETURN ThrowTypeAccessException(MethodDesc *pCallerMD,
MethodTable *pMT,
- BOOL isTransparencyError,
UINT messageID = 0,
Exception *pInnerException = NULL,
BOOL fAccessingFrameworkCode = FALSE);
@@ -889,8 +879,7 @@ public:
AccessCheckContext* pContext,
MethodTable* pTargetClass,
Assembly* pTargetAssembly,
- const AccessCheckOptions & accessCheckOptions = *AccessCheckOptions::s_pNormalAccessChecks,
- BOOL checkTargetTypeTransparency = TRUE);
+ const AccessCheckOptions & accessCheckOptions = *AccessCheckOptions::s_pNormalAccessChecks);
static BOOL CanAccess(
AccessCheckContext* pContext,
@@ -899,16 +888,7 @@ public:
DWORD dwMemberAttrs,
MethodDesc* pOptionalTargetMethod,
FieldDesc* pOptionalTargetField,
- const AccessCheckOptions & accessCheckOptions = *AccessCheckOptions::s_pNormalAccessChecks,
- BOOL checkTargetMethodTransparency = TRUE,
- BOOL checkTargetTypeTransparency = TRUE);
-
- static BOOL CanAccessClassForExtraChecks(
- AccessCheckContext* pContext,
- MethodTable* pTargetClass,
- Assembly* pTargetAssembly,
- const AccessCheckOptions & accessCheckOptions,
- BOOL checkTargetTypeTransparency);
+ const AccessCheckOptions & accessCheckOptions = *AccessCheckOptions::s_pNormalAccessChecks);
static BOOL CanAccessFamilyVerification(
TypeHandle thCurrentClass,
@@ -921,21 +901,6 @@ private:
MethodDesc* pOptionalTargetMethod,
const AccessCheckOptions & accessCheckOptions);
- static BOOL CanAccessMemberForExtraChecks(
- AccessCheckContext* pContext,
- MethodTable* pTargetExactMT,
- MethodDesc* pOptionalTargetMethod,
- FieldDesc* pOptionalTargetField,
- const AccessCheckOptions & accessCheckOptions,
- BOOL checkTargetMethodTransparency);
-
- static BOOL CanAccessSigForExtraChecks(
- AccessCheckContext* pContext,
- MethodDesc* pTargetMethodSig,
- MethodTable* pTargetExactMT,
- const AccessCheckOptions & accessCheckOptions,
- BOOL checkTargetTransparency);
-
static BOOL CanAccessFamily(
MethodTable* pCurrentClass,
MethodTable* pTargetClass);
@@ -947,9 +912,7 @@ private:
DWORD dwMemberAttrs,
MethodDesc* pOptionalTargetMethod,
FieldDesc* pOptionalTargetField,
- const AccessCheckOptions & accessCheckOptions = *AccessCheckOptions::s_pNormalAccessChecks,
- BOOL checkTargetMethodTransparency = TRUE,
- BOOL checkTargetTypeTransparency = TRUE);
+ const AccessCheckOptions & accessCheckOptions = *AccessCheckOptions::s_pNormalAccessChecks);
public:
diff --git a/src/vm/clsload.inl b/src/vm/clsload.inl
index 991498ec9c..7dcd1a5d00 100644
--- a/src/vm/clsload.inl
+++ b/src/vm/clsload.inl
@@ -64,8 +64,7 @@ inline void AccessCheckOptions::Initialize(
BOOL throwIfTargetIsInaccessible,
MethodTable * pTargetMT,
MethodDesc * pTargetMethod,
- FieldDesc * pTargetField,
- BOOL skipCheckForCriticalCode /*=FALSE*/)
+ FieldDesc * pTargetField)
{
CONTRACTL
{
@@ -90,7 +89,6 @@ inline void AccessCheckOptions::Initialize(
m_pTargetMT = pTargetMT;
m_pTargetMethod = pTargetMethod;
m_pTargetField = pTargetField;
- m_fSkipCheckForCriticalCode = skipCheckForCriticalCode;
}
//******************************************************************************
diff --git a/src/vm/codeman.cpp b/src/vm/codeman.cpp
index d934b824f6..7d90ce9a5e 100644
--- a/src/vm/codeman.cpp
+++ b/src/vm/codeman.cpp
@@ -38,6 +38,10 @@
#include "../debug/daccess/fntableaccess.h"
#endif // _WIN64
+#ifdef FEATURE_PERFMAP
+#include "perfmap.h"
+#endif
+
#define MAX_M_ALLOCATED (16 * 1024)
// Default number of jump stubs in a jump stub block
@@ -2186,7 +2190,7 @@ HeapList* EEJitManager::NewCodeHeap(CodeHeapRequestInfo *pInfo, DomainCodeHeapLi
} CONTRACT_END;
size_t initialRequestSize = pInfo->getRequestSize();
- size_t minReserveSize = VIRTUAL_ALLOC_RESERVE_GRANULARITY; // ( 64 KB)
+ size_t minReserveSize = VIRTUAL_ALLOC_RESERVE_GRANULARITY; // ( 64 KB)
#ifdef _WIN64
if (pInfo->m_hiAddr == 0)
@@ -2390,12 +2394,12 @@ void* EEJitManager::allocCodeRaw(CodeHeapRequestInfo *pInfo,
{
// Let us create a new heap.
- DomainCodeHeapList *pList = GetCodeHeapList(pInfo->m_pMD, pInfo->m_pAllocator);
+ DomainCodeHeapList *pList = GetCodeHeapList(pInfo, pInfo->m_pAllocator);
if (pList == NULL)
{
// not found so need to create the first one
pList = CreateCodeHeapList(pInfo);
- _ASSERTE(pList == GetCodeHeapList(pInfo->m_pMD, pInfo->m_pAllocator));
+ _ASSERTE(pList == GetCodeHeapList(pInfo, pInfo->m_pAllocator));
}
_ASSERTE(pList);
@@ -2478,23 +2482,29 @@ CodeHeader* EEJitManager::allocCode(MethodDesc* pMD, size_t blockSize, CorJitAll
SIZE_T totalSize = blockSize;
+ CodeHeader * pCodeHdr = NULL;
+
+ CodeHeapRequestInfo requestInfo(pMD);
+#if defined(FEATURE_JIT_PITCHING)
+ if (pMD && pMD->IsPitchable() && CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchMethodSizeThreshold) < blockSize)
+ {
+ requestInfo.SetDynamicDomain();
+ }
+#endif
+
#if defined(USE_INDIRECT_CODEHEADER)
SIZE_T realHeaderSize = offsetof(RealCodeHeader, unwindInfos[0]) + (sizeof(T_RUNTIME_FUNCTION) * nUnwindInfos);
// if this is a LCG method then we will be allocating the RealCodeHeader
// following the code so that the code block can be removed easily by
// the LCG code heap.
- if (pMD->IsLCGMethod())
+ if (requestInfo.IsDynamicDomain())
{
totalSize = ALIGN_UP(totalSize, sizeof(void*)) + realHeaderSize;
static_assert_no_msg(CODE_SIZE_ALIGN >= sizeof(void*));
}
#endif // USE_INDIRECT_CODEHEADER
- CodeHeader * pCodeHdr = NULL;
-
- CodeHeapRequestInfo requestInfo(pMD);
-
// Scope the lock
{
CrstHolder ch(&m_CodeHeapCritSec);
@@ -2521,7 +2531,7 @@ CodeHeader* EEJitManager::allocCode(MethodDesc* pMD, size_t blockSize, CorJitAll
pCodeHdr = ((CodeHeader *)pCode) - 1;
#ifdef USE_INDIRECT_CODEHEADER
- if (pMD->IsLCGMethod())
+ if (requestInfo.IsDynamicDomain())
{
pCodeHdr->SetRealCodeHeader((BYTE*)pCode + ALIGN_UP(blockSize, sizeof(void*)));
}
@@ -2550,7 +2560,7 @@ CodeHeader* EEJitManager::allocCode(MethodDesc* pMD, size_t blockSize, CorJitAll
RETURN(pCodeHdr);
}
-EEJitManager::DomainCodeHeapList *EEJitManager::GetCodeHeapList(MethodDesc *pMD, LoaderAllocator *pAllocator, BOOL fDynamicOnly)
+EEJitManager::DomainCodeHeapList *EEJitManager::GetCodeHeapList(CodeHeapRequestInfo *pInfo, LoaderAllocator *pAllocator, BOOL fDynamicOnly)
{
CONTRACTL {
NOTHROW;
@@ -2564,7 +2574,7 @@ EEJitManager::DomainCodeHeapList *EEJitManager::GetCodeHeapList(MethodDesc *pMD,
// get the appropriate list of heaps
// pMD is NULL for NGen modules during Module::LoadTokenTables
- if (fDynamicOnly || (pMD != NULL && pMD->IsLCGMethod()))
+ if (fDynamicOnly || (pInfo != NULL && pInfo->IsDynamicDomain()))
{
ppList = m_DynamicDomainCodeHeaps.Table();
count = m_DynamicDomainCodeHeaps.Count();
@@ -2605,7 +2615,7 @@ HeapList* EEJitManager::GetCodeHeap(CodeHeapRequestInfo *pInfo)
// loop through the m_DomainCodeHeaps to find the AppDomain
// if not found, then create it
- DomainCodeHeapList *pList = GetCodeHeapList(pInfo->m_pMD, pInfo->m_pAllocator);
+ DomainCodeHeapList *pList = GetCodeHeapList(pInfo, pInfo->m_pAllocator);
if (pList)
{
// Set pResult to the largest non-full HeapList
@@ -2726,7 +2736,7 @@ bool EEJitManager::CanUseCodeHeap(CodeHeapRequestInfo *pInfo, HeapList *pCodeHea
}
}
- return retVal;
+ return retVal;
}
EEJitManager::DomainCodeHeapList * EEJitManager::CreateCodeHeapList(CodeHeapRequestInfo *pInfo)
@@ -5048,6 +5058,10 @@ DONE:
emitBackToBackJump(jumpStub, (void*) target);
+#ifdef FEATURE_PERFMAP
+ PerfMap::LogStubs(__FUNCTION__, "emitBackToBackJump", (PCODE)jumpStub, BACK_TO_BACK_JUMP_ALLOCATE_SIZE);
+#endif
+
// We always add the new jumpstub to the jumpStubCache
//
_ASSERTE(pJumpStubCache != NULL);
diff --git a/src/vm/codeman.h b/src/vm/codeman.h
index f85eeb59db..afef682e2a 100644
--- a/src/vm/codeman.h
+++ b/src/vm/codeman.h
@@ -369,6 +369,8 @@ struct CodeHeapRequestInfo
bool m_isCollectible;
bool IsDynamicDomain() { return m_isDynamicDomain; }
+ void SetDynamicDomain() { m_isDynamicDomain = true; }
+
bool IsCollectible() { return m_isCollectible; }
size_t getRequestSize() { return m_requestSize; }
@@ -1095,7 +1097,7 @@ private :
size_t header, size_t blockSize, unsigned align,
HeapList ** ppCodeHeap /* Writeback, Can be null */ );
- DomainCodeHeapList *GetCodeHeapList(MethodDesc *pMD, LoaderAllocator *pAllocator, BOOL fDynamicOnly = FALSE);
+ DomainCodeHeapList *GetCodeHeapList(CodeHeapRequestInfo *pInfo, LoaderAllocator *pAllocator, BOOL fDynamicOnly = FALSE);
DomainCodeHeapList *CreateCodeHeapList(CodeHeapRequestInfo *pInfo);
LoaderHeap* GetJitMetaHeap(MethodDesc *pMD);
#endif // !CROSSGEN_COMPILE
diff --git a/src/vm/codepitchingmanager.cpp b/src/vm/codepitchingmanager.cpp
new file mode 100644
index 0000000000..521c101b0f
--- /dev/null
+++ b/src/vm/codepitchingmanager.cpp
@@ -0,0 +1,522 @@
+// 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.
+// ===========================================================================
+// File: CodePitchingManager.cpp
+//
+
+// ===========================================================================
+// This file contains the implementation for code pitching.
+// Its distinctive features and algorithm are:
+//
+// 1. All its code is under #if defined(FEATURE_JIT_PITCHING) and doesn't mess up with other code
+// 2. This feature is working only if the options INTERNAL_JitPitchEnabled != 0 and INTERNAL_JitPitchMemThreshold > 0
+// 3. Jitted code can be pitched only for methods that are not Dynamic, FCall or Virtual
+// 4. If the size of the generated native code exceeds the value of INTERNAL_JitDPitchMethodSizeThreshold this code is
+// placed in the special heap code list. Each heap block in this list stores the code for only one method and has the
+// sufficient size for the code of a method aligned to 4K. The pointers to such methods are stored in the
+// "PitchingCandidateMethods" hash map.
+// 5. If the entrypoint of a method is backpatched this method is excluded from the "PitchingCandidateMethods" hash map
+// and stored in "NotForPitchingMethods" hashmap.
+// 6. When the total size of the generated native code exceeds the value of INTERNAL_JitPitchMemThreshold option, the
+// execution of the program is stopped and stack frames for all the threads are inspected and pointers to methods
+// being executed are stored in the "ExecutedMethods" hash map
+// 7. The code for all the methods from the "PitchingCandidateMethods" that are not in the "ExecutedMethods" is pitched.
+// (All heap blocks for these methods are set in the initial state and can be reused for newly compiled methods, pointers
+// to the code for non-executed methods are set to nullptr).
+// 8. If the code for the given method is pitched once, this method is stored in the "NotForPitchingMethods" hashmap. Thus,
+// if this method is compiled the second time, it is considered as called repeatedly, therefore, pitching for it is inexpedient,
+// and the newly compiled code stored in the usual heap.
+// 9. The coreclr code with this feature is built by the option
+// ./build.sh cmakeargs -DFEATURE_JIT_PITCHING=true
+// ===========================================================================
+
+#include "common.h"
+
+#ifndef DACCESS_COMPILE
+
+#if defined(FEATURE_JIT_PITCHING)
+
+#include "nibblemapmacros.h"
+#include "threadsuspend.h"
+
+static PtrHashMap* s_pPitchingCandidateMethods = nullptr;
+static PtrHashMap* s_pPitchingCandidateSizes = nullptr;
+static SimpleRWLock* s_pPitchingCandidateMethodsLock = nullptr;
+
+static PtrHashMap* s_pExecutedMethods = nullptr;
+static SimpleRWLock* s_pExecutedMethodsLock = nullptr;
+
+static PtrHashMap* s_pNotForPitchingMethods = nullptr;
+static SimpleRWLock* s_pNotForPitchingMethodsLock = nullptr;
+
+#ifdef _DEBUG
+static PtrHashMap* s_pPitchedMethods = nullptr;
+static SimpleRWLock* s_pPitchedMethodsLock = nullptr;
+#endif
+
+static ULONG s_totalNCSize = 0;
+static SimpleRWLock* s_totalNCSizeLock = nullptr;
+
+static ULONG s_jitPitchedBytes = 0;
+
+static INT64 s_JitPitchLastTick = 0;
+
+static bool s_JitPitchInitialized = false;
+
+
+static BOOL IsOwnerOfRWLock(LPVOID lock)
+{
+ // @TODO - SimpleRWLock does not have knowledge of which thread gets the writer
+ // lock, so no way to verify
+ return TRUE;
+}
+
+static void CreateRWLock(SimpleRWLock** lock)
+{
+ if (*lock == nullptr)
+ {
+ void *pLockSpace = SystemDomain::GetGlobalLoaderAllocator()->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(SimpleRWLock)));
+ SimpleRWLock *pLock = new (pLockSpace) SimpleRWLock(COOPERATIVE_OR_PREEMPTIVE, LOCK_TYPE_DEFAULT);
+
+ if (FastInterlockCompareExchangePointer(lock, pLock, NULL) != NULL)
+ SystemDomain::GetGlobalLoaderAllocator()->GetLowFrequencyHeap()->BackoutMem(pLockSpace, sizeof(SimpleRWLock));
+ }
+}
+
+static PtrHashMap* CreateHashMap(SimpleRWLock* rwLock)
+{
+ PtrHashMap *pMap = new (SystemDomain::GetGlobalLoaderAllocator()->GetLowFrequencyHeap()) PtrHashMap();
+ LockOwner lock = {rwLock, IsOwnerOfRWLock};
+ pMap->Init(32, nullptr, FALSE, &lock);
+ return pMap;
+}
+
+static void InitializeJitPitching()
+{
+ if (!s_JitPitchInitialized)
+ {
+ CreateRWLock(&s_pNotForPitchingMethodsLock);
+ CreateRWLock(&s_pPitchingCandidateMethodsLock);
+ CreateRWLock(&s_totalNCSizeLock);
+
+ {
+ SimpleReadLockHolder srlh(s_pNotForPitchingMethodsLock);
+ if (s_pNotForPitchingMethods == nullptr)
+ {
+ s_pNotForPitchingMethods = CreateHashMap(s_pNotForPitchingMethodsLock);
+ }
+ }
+
+ {
+ SimpleReadLockHolder srlh(s_pPitchingCandidateMethodsLock);
+ if (s_pPitchingCandidateMethods == nullptr)
+ {
+ s_pPitchingCandidateMethods = CreateHashMap(s_pPitchingCandidateMethodsLock);
+ s_pPitchingCandidateSizes = CreateHashMap(s_pPitchingCandidateMethodsLock);
+ }
+ }
+
+ s_JitPitchInitialized = true;
+ }
+}
+
+static COUNT_T GetFullHash(MethodDesc* pMD)
+{
+ const char *moduleName = pMD->GetModule()->GetSimpleName();
+
+ COUNT_T hash = HashStringA(moduleName); // Start the hash with the Module name
+
+ SString className, methodName, methodSig;
+
+ pMD->GetMethodInfo(className, methodName, methodSig);
+
+ hash = HashCOUNT_T(hash, className.Hash()); // Hash in the name of the Class name
+ hash = HashCOUNT_T(hash, methodName.Hash()); // Hash in the name of the Method name
+ hash = HashCOUNT_T(hash, 0xffffffff & (ULONGLONG)pMD);
+
+ return hash;
+}
+
+bool MethodDesc::IsPitchable()
+{
+ if ((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchEnabled) == 0) ||
+ (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchMemThreshold) == 0))
+ return FALSE;
+
+ InitializeJitPitching();
+
+ if (IsLCGMethod() || IsVtableMethod() || IsInterface() || IsVirtual())
+ return FALSE;
+
+ _ASSERTE(s_pNotForPitchingMethodsLock != nullptr && s_pNotForPitchingMethods != nullptr);
+
+ {
+ SimpleReadLockHolder srlh(s_pNotForPitchingMethodsLock);
+ UPTR key = (UPTR)GetFullHash(this);
+ MethodDesc *pFound = (MethodDesc *)s_pNotForPitchingMethods->LookupValue(key, (LPVOID)this);
+ if (pFound != (MethodDesc *)INVALIDENTRY)
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+EXTERN_C bool LookupOrCreateInNotForPitching(MethodDesc* pMD)
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ if (pMD != nullptr && pMD->IsPitchable())
+ {
+ UPTR key = (UPTR)GetFullHash(pMD);
+
+ _ASSERTE(s_pNotForPitchingMethodsLock != nullptr && s_pNotForPitchingMethods != nullptr);
+
+ {
+ SimpleReadLockHolder srlh(s_pNotForPitchingMethodsLock);
+ MethodDesc *pFound = (MethodDesc *)s_pNotForPitchingMethods->LookupValue(key, (LPVOID)pMD);
+ if (pFound != (MethodDesc *)INVALIDENTRY)
+ return TRUE;
+ }
+
+ {
+ SimpleWriteLockHolder swlh(s_pNotForPitchingMethodsLock);
+ s_pNotForPitchingMethods->InsertValue(key, (LPVOID)pMD);
+ }
+ }
+ return FALSE;
+}
+
+static void LookupOrCreateInPitchingCandidate(MethodDesc* pMD, ULONG sizeOfCode)
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ if (pMD == nullptr || !pMD->IsPitchable())
+ return;
+
+ PCODE prCode = pMD->GetPreImplementedCode();
+ if (prCode)
+ return;
+
+ if (!pMD->HasPrecode())
+ return;
+
+ UPTR key = (UPTR)GetFullHash(pMD);
+
+ _ASSERTE(s_pPitchingCandidateMethodsLock != nullptr && s_pPitchingCandidateMethods != nullptr);
+ _ASSERTE(s_pPitchingCandidateSizes);
+
+ {
+ // Try getting an existing value first.
+ SimpleReadLockHolder srlh(s_pPitchingCandidateMethodsLock);
+ MethodDesc *pFound = (MethodDesc *)s_pPitchingCandidateMethods->LookupValue(key, (LPVOID)pMD);
+ if (pFound != (MethodDesc *)INVALIDENTRY)
+ return;
+ }
+
+ {
+ SimpleWriteLockHolder swlh(s_pPitchingCandidateMethodsLock);
+ s_pPitchingCandidateMethods->InsertValue(key, (LPVOID)pMD);
+ s_pPitchingCandidateSizes->InsertValue(key, (LPVOID)((ULONGLONG)(sizeOfCode << 1)));
+#ifdef _DEBUG
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchPrintStat) != 0)
+ {
+ SString className, methodName, methodSig;
+ pMD->GetMethodInfo(className, methodName, methodSig);
+
+ StackScratchBuffer scratch;
+ const char* szClassName = className.GetUTF8(scratch);
+ const char* szMethodSig = methodSig.GetUTF8(scratch);
+
+ printf("Candidate %lld %s :: %s %s\n",
+ sizeOfCode, szClassName, pMD->GetName(), szMethodSig);
+ }
+#endif
+ }
+}
+
+EXTERN_C void DeleteFromPitchingCandidate(MethodDesc* pMD)
+{
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ if (pMD != nullptr && pMD->IsPitchable())
+ {
+ PCODE pCode = pMD->GetPreImplementedCode();
+
+ if (pCode)
+ return;
+
+ _ASSERTE(s_pPitchingCandidateMethodsLock != nullptr && s_pPitchingCandidateMethods != nullptr);
+ _ASSERTE(s_pPitchingCandidateSizes != nullptr);
+
+ UPTR key = (UPTR)GetFullHash(pMD);
+ {
+ SimpleReadLockHolder srlh(s_pPitchingCandidateMethodsLock);
+ MethodDesc *pFound = (MethodDesc *)s_pPitchingCandidateMethods->LookupValue(key, (LPVOID)pMD);
+ if (pFound == (MethodDesc *)INVALIDENTRY)
+ return;
+ }
+
+ {
+ SimpleWriteLockHolder swlh(s_pPitchingCandidateMethodsLock);
+ s_pPitchingCandidateMethods->DeleteValue(key, (LPVOID)pMD);
+ }
+
+ LPVOID pitchedBytes;
+ {
+ SimpleReadLockHolder srlh(s_pPitchingCandidateMethodsLock);
+ pitchedBytes = s_pPitchingCandidateSizes->LookupValue(key, nullptr);
+ _ASSERTE(pitchedBytes != (LPVOID)INVALIDENTRY);
+ }
+ {
+ SimpleWriteLockHolder swlh(s_pPitchingCandidateMethodsLock);
+ s_pPitchingCandidateSizes->DeleteValue(key, pitchedBytes);
+ }
+ }
+}
+
+EXTERN_C void MarkMethodNotPitchingCandidate(MethodDesc* pMD)
+{
+
+ DeleteFromPitchingCandidate(pMD);
+ (void)LookupOrCreateInNotForPitching(pMD);
+}
+
+StackWalkAction CrawlFrameVisitor(CrawlFrame* pCf, Thread* pMdThread)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ MethodDesc* pMD = pCf->GetFunction();
+
+ // Filter out methods we don't care about
+ if (pMD == nullptr || !pMD->IsPitchable())
+ {
+ return SWA_CONTINUE;
+ }
+
+ if (s_pExecutedMethods == nullptr)
+ {
+ PtrHashMap *pMap = new (SystemDomain::GetGlobalLoaderAllocator()->GetLowFrequencyHeap()) PtrHashMap();
+ pMap->Init(TRUE, nullptr);
+ s_pExecutedMethods = pMap;
+ }
+
+ UPTR key = (UPTR)GetFullHash(pMD);
+ MethodDesc *pFound = (MethodDesc *)s_pExecutedMethods->LookupValue(key, (LPVOID)pMD);
+ if (pFound == (MethodDesc *)INVALIDENTRY)
+ {
+ s_pExecutedMethods->InsertValue(key, (LPVOID)pMD);
+ }
+
+ return SWA_CONTINUE;
+}
+
+// Visitor for stack walk callback.
+StackWalkAction StackWalkCallback(CrawlFrame* pCf, VOID* data)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // WalkInfo* info = (WalkInfo*) data;
+ return CrawlFrameVisitor(pCf, (Thread *)data);
+}
+
+static ULONGLONG s_PitchedMethodCounter = 0;
+void MethodDesc::PitchNativeCode()
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ g_IBCLogger.LogMethodDescAccess(this);
+
+ if (!IsPitchable())
+ return;
+
+ PCODE pCode = GetNativeCode();
+
+ if (!pCode)
+ return;
+
+ _ASSERTE(HasPrecode());
+
+ _ASSERTE(HasNativeCode());
+
+ ++s_PitchedMethodCounter;
+
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchMinVal) > s_PitchedMethodCounter)
+ {
+ return;
+ }
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchMaxVal) < s_PitchedMethodCounter)
+ {
+ return;
+ }
+
+ if (LookupOrCreateInNotForPitching(this))
+ return;
+
+ MethodTable * pMT = GetMethodTable();
+ _ASSERTE(pMT != nullptr);
+
+ CodeHeader* pCH = ((CodeHeader*)(pCode & ~1)) - 1;
+ _ASSERTE(pCH->GetMethodDesc() == this);
+
+ HostCodeHeap* pHeap = HostCodeHeap::GetCodeHeap((TADDR)pCode);
+ pHeap->GetJitManager()->FreeCodeMemory(pHeap, (void*)pCode);
+
+ ClearFlagsOnUpdate();
+
+ _ASSERTE(HasPrecode());
+ GetPrecode()->Reset();
+
+ if (HasNativeCodeSlot())
+ {
+ RelativePointer<TADDR> *pRelPtr = (RelativePointer<TADDR> *)GetAddrOfNativeCodeSlot();
+ pRelPtr->SetValueMaybeNull(NULL);
+ }
+ else
+ {
+#ifdef FEATURE_INTERPRETER
+ SetNativeCodeInterlocked(NULL, NULL, FALSE);
+#else
+ SetNativeCodeInterlocked(NULL, NULL);
+#endif
+ }
+
+ _ASSERTE(!HasNativeCode());
+
+ UPTR key = (UPTR)GetFullHash(this);
+ ULONGLONG pitchedBytes;
+ {
+ SimpleReadLockHolder srlh(s_pPitchingCandidateMethodsLock);
+ pitchedBytes = (ULONGLONG)s_pPitchingCandidateSizes->LookupValue(key, nullptr);
+ _ASSERTE(pitchedBytes != (ULONGLONG)INVALIDENTRY);
+ if (pitchedBytes == (ULONGLONG)INVALIDENTRY)
+ pitchedBytes = 0;
+ s_jitPitchedBytes += (pitchedBytes >> 1);
+ }
+ {
+ SimpleWriteLockHolder swlh(s_pPitchingCandidateMethodsLock);
+ s_pPitchingCandidateMethods->DeleteValue(key, (LPVOID)this);
+ if (pitchedBytes != 0)
+ s_pPitchingCandidateSizes->DeleteValue(key, (LPVOID)pitchedBytes);
+ }
+
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchPrintStat) != 0)
+ {
+ SString className, methodName, methodSig;
+ GetMethodInfo(className, methodName, methodSig);
+
+ StackScratchBuffer scratch;
+ const char* szClassName = className.GetUTF8(scratch);
+ const char* szMethodSig = methodSig.GetUTF8(scratch);
+
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchPrintStat) != 0)
+ {
+ printf("Pitched %lld %lld %s :: %s %s\n",
+ s_PitchedMethodCounter, pitchedBytes, szClassName, GetName(), szMethodSig);
+ }
+ }
+
+ DACNotify::DoJITPitchingNotification(this);
+}
+
+EXTERN_C void CheckStacksAndPitch()
+{
+ if ((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchEnabled) != 0) &&
+ (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchMemThreshold) != 0) &&
+ (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchTimeInterval) == 0 ||
+ ((::GetTickCount64() - s_JitPitchLastTick) > CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchTimeInterval))))
+ {
+ SimpleReadLockHolder srlh(s_totalNCSizeLock);
+
+ if ((s_totalNCSize - s_jitPitchedBytes) > CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchMemThreshold) &&
+ s_pPitchingCandidateMethods != nullptr)
+ {
+ EX_TRY
+ {
+ // Suspend the runtime.
+ ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_OTHER);
+
+ // Walk all other threads.
+ Thread* pThread = nullptr;
+ while ((pThread = ThreadStore::GetThreadList(pThread)) != nullptr)
+ {
+ pThread->StackWalkFrames(StackWalkCallback, (VOID *)pThread, ALLOW_ASYNC_STACK_WALK);
+ }
+
+ if (s_pExecutedMethods)
+ {
+ PtrHashMap::PtrIterator i = s_pPitchingCandidateMethods->begin();
+ while (!i.end())
+ {
+ MethodDesc *pMD = (MethodDesc *) i.GetValue();
+ UPTR key = (UPTR)GetFullHash(pMD);
+ MethodDesc *pFound = (MethodDesc *)s_pExecutedMethods->LookupValue(key, (LPVOID)pMD);
+ ++i;
+ if (pFound == (MethodDesc *)INVALIDENTRY)
+ {
+ pMD->PitchNativeCode();
+ }
+ }
+ s_pExecutedMethods->Clear();
+ delete s_pExecutedMethods;
+ s_pExecutedMethods = nullptr;
+ s_pPitchingCandidateMethods->Compact();
+ s_pPitchingCandidateSizes->Compact();
+ }
+
+ s_JitPitchLastTick = ::GetTickCount64();
+
+ ThreadSuspend::RestartEE(FALSE, TRUE);
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+ }
+ }
+}
+
+EXTERN_C void SavePitchingCandidate(MethodDesc* pMD, ULONG sizeOfCode)
+{
+ if (pMD && pMD->IsPitchable() && CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchMethodSizeThreshold) < sizeOfCode)
+ {
+ LookupOrCreateInPitchingCandidate(pMD, sizeOfCode);
+ }
+ if (sizeOfCode > 0)
+ {
+ SimpleWriteLockHolder swlh(s_totalNCSizeLock);
+ s_totalNCSize += sizeOfCode;
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchPrintStat) != 0)
+ printf("jitted %lld (bytes) pitched %lld (bytes)\n", s_totalNCSize, s_jitPitchedBytes);
+ }
+}
+#endif
+
+#endif
diff --git a/src/vm/codeversion.cpp b/src/vm/codeversion.cpp
new file mode 100644
index 0000000000..10d3013f35
--- /dev/null
+++ b/src/vm/codeversion.cpp
@@ -0,0 +1,2862 @@
+// 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.
+// ===========================================================================
+// File: CodeVersion.cpp
+//
+// ===========================================================================
+
+#include "common.h"
+#include "codeversion.h"
+
+#ifdef FEATURE_CODE_VERSIONING
+#include "threadsuspend.h"
+#include "methoditer.h"
+#include "../debug/ee/debugger.h"
+#include "../debug/ee/walker.h"
+#include "../debug/ee/controller.h"
+#endif // FEATURE_CODE_VERSIONING
+
+#ifndef FEATURE_CODE_VERSIONING
+
+//
+// When not using code versioning we've got a minimal implementation of
+// NativeCodeVersion that simply wraps a MethodDesc* with no additional
+// versioning information
+//
+
+NativeCodeVersion::NativeCodeVersion(const NativeCodeVersion & rhs) : m_pMethod(rhs.m_pMethod) {}
+NativeCodeVersion::NativeCodeVersion(PTR_MethodDesc pMethod) : m_pMethod(pMethod) {}
+BOOL NativeCodeVersion::IsNull() const { return m_pMethod == NULL; }
+PTR_MethodDesc NativeCodeVersion::GetMethodDesc() const { return m_pMethod; }
+PCODE NativeCodeVersion::GetNativeCode() const { return m_pMethod->GetNativeCode(); }
+NativeCodeVersionId NativeCodeVersion::GetVersionId() const { return 0; }
+ReJITID NativeCodeVersion::GetILCodeVersionId() const; { return 0; }
+ILCodeVersion NativeCodeVersion::GetILCodeVersion() const { return ILCodeVersion(m_pMethod); }
+#ifndef DACCESS_COMPILE
+BOOL NativeCodeVersion::SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected) { return m_pMethod->SetNativeCodeInterlocked(pCode, pExpected); }
+#endif
+bool NativeCodeVersion::operator==(const NativeCodeVersion & rhs) const { return m_pMethod == rhs.m_pMethod; }
+bool NativeCodeVersion::operator!=(const NativeCodeVersion & rhs) const { return !operator==(rhs); }
+
+
+#else // FEATURE_CODE_VERSIONING
+
+
+// This HRESULT is only used as a private implementation detail. If it escapes through public APIS
+// it is a bug. Corerror.xml has a comment in it reserving this value for our use but it doesn't
+// appear in the public headers.
+
+#define CORPROF_E_RUNTIME_SUSPEND_REQUIRED 0x80131381
+
+#ifndef DACCESS_COMPILE
+NativeCodeVersionNode::NativeCodeVersionNode(NativeCodeVersionId id, MethodDesc* pMethodDesc, ReJITID parentId) :
+ m_pNativeCode(NULL),
+ m_pMethodDesc(pMethodDesc),
+ m_parentId(parentId),
+ m_pNextMethodDescSibling(NULL),
+ m_id(id),
+ m_optTier(NativeCodeVersion::OptimizationTier0),
+ m_flags(0)
+{}
+#endif
+
+#ifdef DEBUG
+BOOL NativeCodeVersionNode::LockOwnedByCurrentThread() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread();
+}
+#endif //DEBUG
+
+PTR_MethodDesc NativeCodeVersionNode::GetMethodDesc() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_pMethodDesc;
+}
+
+PCODE NativeCodeVersionNode::GetNativeCode() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_pNativeCode;
+}
+
+ReJITID NativeCodeVersionNode::GetILVersionId() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_parentId;
+}
+
+ILCodeVersion NativeCodeVersionNode::GetILCodeVersion() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+#ifdef DEBUG
+ if (GetILVersionId() != 0)
+ {
+ _ASSERTE(LockOwnedByCurrentThread());
+ }
+#endif
+ PTR_MethodDesc pMD = GetMethodDesc();
+ return pMD->GetCodeVersionManager()->GetILCodeVersion(pMD, GetILVersionId());
+}
+
+NativeCodeVersionId NativeCodeVersionNode::GetVersionId() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_id;
+}
+
+#ifndef DACCESS_COMPILE
+BOOL NativeCodeVersionNode::SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected)
+{
+ LIMITED_METHOD_CONTRACT;
+ return FastInterlockCompareExchangePointer(&m_pNativeCode,
+ (TADDR&)pCode, (TADDR&)pExpected) == (TADDR&)pExpected;
+}
+#endif
+
+BOOL NativeCodeVersionNode::IsActiveChildVersion() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ return (m_flags & IsActiveChildFlag) != 0;
+}
+
+#ifndef DACCESS_COMPILE
+void NativeCodeVersionNode::SetActiveChildFlag(BOOL isActive)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ if (isActive)
+ {
+ m_flags |= IsActiveChildFlag;
+ }
+ else
+ {
+ m_flags &= ~IsActiveChildFlag;
+ }
+}
+#endif
+
+
+#ifdef FEATURE_TIERED_COMPILATION
+NativeCodeVersion::OptimizationTier NativeCodeVersionNode::GetOptimizationTier() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_optTier.Load();
+}
+#ifndef DACCESS_COMPILE
+void NativeCodeVersionNode::SetOptimizationTier(NativeCodeVersion::OptimizationTier tier)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ m_optTier.Store(tier);
+}
+#endif
+#endif // FEATURE_TIERED_COMPILATION
+
+NativeCodeVersion::NativeCodeVersion() :
+ m_storageKind(StorageKind::Unknown)
+{}
+
+NativeCodeVersion::NativeCodeVersion(const NativeCodeVersion & rhs) :
+ m_storageKind(rhs.m_storageKind)
+{
+ if(m_storageKind == StorageKind::Explicit)
+ {
+ m_pVersionNode = rhs.m_pVersionNode;
+ }
+ else if(m_storageKind == StorageKind::Synthetic)
+ {
+ m_synthetic = rhs.m_synthetic;
+ }
+}
+
+NativeCodeVersion::NativeCodeVersion(PTR_NativeCodeVersionNode pVersionNode) :
+ m_storageKind(pVersionNode != NULL ? StorageKind::Explicit : StorageKind::Unknown),
+ m_pVersionNode(pVersionNode)
+{}
+
+NativeCodeVersion::NativeCodeVersion(PTR_MethodDesc pMethod) :
+ m_storageKind(pMethod != NULL ? StorageKind::Synthetic : StorageKind::Unknown)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ m_synthetic.m_pMethodDesc = pMethod;
+}
+
+BOOL NativeCodeVersion::IsNull() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_storageKind == StorageKind::Unknown;
+}
+
+BOOL NativeCodeVersion::IsDefaultVersion() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_storageKind == StorageKind::Synthetic;
+}
+
+PTR_MethodDesc NativeCodeVersion::GetMethodDesc() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetMethodDesc();
+ }
+ else
+ {
+ return m_synthetic.m_pMethodDesc;
+ }
+}
+
+PCODE NativeCodeVersion::GetNativeCode() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetNativeCode();
+ }
+ else
+ {
+ return GetMethodDesc()->GetNativeCode();
+ }
+}
+
+ReJITID NativeCodeVersion::GetILCodeVersionId() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetILVersionId();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+ILCodeVersion NativeCodeVersion::GetILCodeVersion() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetILCodeVersion();
+ }
+ else
+ {
+ PTR_MethodDesc pMethod = GetMethodDesc();
+ return ILCodeVersion(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef());
+ }
+}
+
+NativeCodeVersionId NativeCodeVersion::GetVersionId() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetVersionId();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+#ifndef DACCESS_COMPILE
+BOOL NativeCodeVersion::SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->SetNativeCodeInterlocked(pCode, pExpected);
+ }
+ else
+ {
+ return GetMethodDesc()->SetNativeCodeInterlocked(pCode, pExpected);
+ }
+}
+#endif
+
+BOOL NativeCodeVersion::IsActiveChildVersion() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->IsActiveChildVersion();
+ }
+ else
+ {
+ MethodDescVersioningState* pMethodVersioningState = GetMethodDescVersioningState();
+ if (pMethodVersioningState == NULL)
+ {
+ return TRUE;
+ }
+ return pMethodVersioningState->IsDefaultVersionActiveChild();
+ }
+}
+
+PTR_MethodDescVersioningState NativeCodeVersion::GetMethodDescVersioningState() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ PTR_MethodDesc pMethodDesc = GetMethodDesc();
+ CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
+ return pCodeVersionManager->GetMethodDescVersioningState(pMethodDesc);
+}
+
+#ifndef DACCESS_COMPILE
+void NativeCodeVersion::SetActiveChildFlag(BOOL isActive)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ AsNode()->SetActiveChildFlag(isActive);
+ }
+ else
+ {
+ MethodDescVersioningState* pMethodVersioningState = GetMethodDescVersioningState();
+ pMethodVersioningState->SetDefaultVersionActiveChildFlag(isActive);
+ }
+}
+
+MethodDescVersioningState* NativeCodeVersion::GetMethodDescVersioningState()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ MethodDesc* pMethodDesc = GetMethodDesc();
+ CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
+ return pCodeVersionManager->GetMethodDescVersioningState(pMethodDesc);
+}
+#endif
+
+#ifdef FEATURE_TIERED_COMPILATION
+NativeCodeVersion::OptimizationTier NativeCodeVersion::GetOptimizationTier() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetOptimizationTier();
+ }
+ else
+ {
+ return NativeCodeVersion::OptimizationTier0;
+ }
+}
+
+#ifndef DACCESS_COMPILE
+void NativeCodeVersion::SetOptimizationTier(NativeCodeVersion::OptimizationTier tier)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ AsNode()->SetOptimizationTier(tier);
+ }
+ else
+ {
+ _ASSERTE(!"Do not call SetOptimizationTier on default code versions - these versions are immutable");
+ }
+}
+#endif
+#endif
+
+PTR_NativeCodeVersionNode NativeCodeVersion::AsNode() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return m_pVersionNode;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+#ifndef DACCESS_COMPILE
+PTR_NativeCodeVersionNode NativeCodeVersion::AsNode()
+{
+ LIMITED_METHOD_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return m_pVersionNode;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+#endif
+
+bool NativeCodeVersion::operator==(const NativeCodeVersion & rhs) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return (rhs.m_storageKind == StorageKind::Explicit) &&
+ (rhs.AsNode() == AsNode());
+ }
+ else if (m_storageKind == StorageKind::Synthetic)
+ {
+ return (rhs.m_storageKind == StorageKind::Synthetic) &&
+ (m_synthetic.m_pMethodDesc == rhs.m_synthetic.m_pMethodDesc);
+ }
+ else
+ {
+ return rhs.m_storageKind == StorageKind::Unknown;
+ }
+}
+bool NativeCodeVersion::operator!=(const NativeCodeVersion & rhs) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return !operator==(rhs);
+}
+
+NativeCodeVersionCollection::NativeCodeVersionCollection(PTR_MethodDesc pMethodDescFilter, ILCodeVersion ilCodeFilter) :
+ m_pMethodDescFilter(pMethodDescFilter),
+ m_ilCodeFilter(ilCodeFilter)
+{
+}
+
+NativeCodeVersionIterator NativeCodeVersionCollection::Begin()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return NativeCodeVersionIterator(this);
+}
+NativeCodeVersionIterator NativeCodeVersionCollection::End()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return NativeCodeVersionIterator(NULL);
+}
+
+NativeCodeVersionIterator::NativeCodeVersionIterator(NativeCodeVersionCollection* pNativeCodeVersionCollection) :
+ m_stage(IterationStage::Initial),
+ m_pCollection(pNativeCodeVersionCollection),
+ m_pLinkedListCur(dac_cast<PTR_NativeCodeVersionNode>(nullptr))
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ First();
+}
+void NativeCodeVersionIterator::First()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_pCollection == NULL)
+ {
+ m_stage = IterationStage::End;
+ }
+ Next();
+}
+void NativeCodeVersionIterator::Next()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_stage == IterationStage::Initial)
+ {
+ ILCodeVersion ilCodeFilter = m_pCollection->m_ilCodeFilter;
+ m_stage = IterationStage::ImplicitCodeVersion;
+ if (ilCodeFilter.IsNull() || ilCodeFilter.IsDefaultVersion())
+ {
+ m_cur = NativeCodeVersion(m_pCollection->m_pMethodDescFilter);
+ return;
+ }
+ }
+ if (m_stage == IterationStage::ImplicitCodeVersion)
+ {
+ m_stage = IterationStage::LinkedList;
+ CodeVersionManager* pCodeVersionManager = m_pCollection->m_pMethodDescFilter->GetCodeVersionManager();
+ MethodDescVersioningState* pMethodDescVersioningState = pCodeVersionManager->GetMethodDescVersioningState(m_pCollection->m_pMethodDescFilter);
+ if (pMethodDescVersioningState == NULL)
+ {
+ m_pLinkedListCur = NULL;
+ }
+ else
+ {
+ ILCodeVersion ilCodeFilter = m_pCollection->m_ilCodeFilter;
+ m_pLinkedListCur = pMethodDescVersioningState->GetFirstVersionNode();
+ while (m_pLinkedListCur != NULL && !ilCodeFilter.IsNull() && ilCodeFilter.GetVersionId() != m_pLinkedListCur->GetILVersionId())
+ {
+ m_pLinkedListCur = m_pLinkedListCur->m_pNextMethodDescSibling;
+ }
+ }
+ if (m_pLinkedListCur != NULL)
+ {
+ m_cur = NativeCodeVersion(m_pLinkedListCur);
+ return;
+ }
+ }
+ if (m_stage == IterationStage::LinkedList)
+ {
+ if (m_pLinkedListCur != NULL)
+ {
+ ILCodeVersion ilCodeFilter = m_pCollection->m_ilCodeFilter;
+ do
+ {
+ m_pLinkedListCur = m_pLinkedListCur->m_pNextMethodDescSibling;
+ } while (m_pLinkedListCur != NULL && !ilCodeFilter.IsNull() && ilCodeFilter.GetVersionId() != m_pLinkedListCur->GetILVersionId());
+ }
+ if (m_pLinkedListCur != NULL)
+ {
+ m_cur = NativeCodeVersion(m_pLinkedListCur);
+ return;
+ }
+ else
+ {
+ m_stage = IterationStage::End;
+ m_cur = NativeCodeVersion();
+ }
+ }
+}
+const NativeCodeVersion & NativeCodeVersionIterator::Get() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_cur;
+}
+bool NativeCodeVersionIterator::Equal(const NativeCodeVersionIterator &i) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_cur == i.m_cur;
+}
+
+ILCodeVersionNode::ILCodeVersionNode() :
+ m_pModule(dac_cast<PTR_Module>(nullptr)),
+ m_methodDef(0),
+ m_rejitId(0),
+ m_pNextILVersionNode(dac_cast<PTR_ILCodeVersionNode>(nullptr)),
+ m_rejitState(ILCodeVersion::kStateRequested),
+ m_pIL(dac_cast<PTR_COR_ILMETHOD>(nullptr)),
+ m_jitFlags(0)
+{}
+
+#ifndef DACCESS_COMPILE
+ILCodeVersionNode::ILCodeVersionNode(Module* pModule, mdMethodDef methodDef, ReJITID id) :
+ m_pModule(pModule),
+ m_methodDef(methodDef),
+ m_rejitId(id),
+ m_pNextILVersionNode(dac_cast<PTR_ILCodeVersionNode>(nullptr)),
+ m_rejitState(ILCodeVersion::kStateRequested),
+ m_pIL(nullptr),
+ m_jitFlags(0)
+{}
+#endif
+
+#ifdef DEBUG
+BOOL ILCodeVersionNode::LockOwnedByCurrentThread() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return GetModule()->GetCodeVersionManager()->LockOwnedByCurrentThread();
+}
+#endif //DEBUG
+
+PTR_Module ILCodeVersionNode::GetModule() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_pModule;
+}
+
+mdMethodDef ILCodeVersionNode::GetMethodDef() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_methodDef;
+}
+
+ReJITID ILCodeVersionNode::GetVersionId() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_rejitId;
+}
+
+ILCodeVersion::RejitFlags ILCodeVersionNode::GetRejitState() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_rejitState.Load();
+}
+
+PTR_COR_ILMETHOD ILCodeVersionNode::GetIL() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return dac_cast<PTR_COR_ILMETHOD>(m_pIL.Load());
+}
+
+DWORD ILCodeVersionNode::GetJitFlags() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_jitFlags.Load();
+}
+
+const InstrumentedILOffsetMapping* ILCodeVersionNode::GetInstrumentedILMap() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ return &m_instrumentedILMap;
+}
+
+PTR_ILCodeVersionNode ILCodeVersionNode::GetNextILVersionNode() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ return m_pNextILVersionNode;
+}
+
+#ifndef DACCESS_COMPILE
+void ILCodeVersionNode::SetRejitState(ILCodeVersion::RejitFlags newState)
+{
+ LIMITED_METHOD_CONTRACT;
+ m_rejitState.Store(newState);
+}
+
+void ILCodeVersionNode::SetIL(COR_ILMETHOD* pIL)
+{
+ LIMITED_METHOD_CONTRACT;
+ m_pIL.Store(pIL);
+}
+
+void ILCodeVersionNode::SetJitFlags(DWORD flags)
+{
+ LIMITED_METHOD_CONTRACT;
+ m_jitFlags.Store(flags);
+}
+
+void ILCodeVersionNode::SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ m_instrumentedILMap.SetMappingInfo(cMap, rgMap);
+}
+
+void ILCodeVersionNode::SetNextILVersionNode(ILCodeVersionNode* pNextILVersionNode)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ m_pNextILVersionNode = pNextILVersionNode;
+}
+#endif
+
+ILCodeVersion::ILCodeVersion() :
+ m_storageKind(StorageKind::Unknown)
+{}
+
+ILCodeVersion::ILCodeVersion(const ILCodeVersion & ilCodeVersion) :
+ m_storageKind(ilCodeVersion.m_storageKind)
+{
+ if(m_storageKind == StorageKind::Explicit)
+ {
+ m_pVersionNode = ilCodeVersion.m_pVersionNode;
+ }
+ else if(m_storageKind == StorageKind::Synthetic)
+ {
+ m_synthetic = ilCodeVersion.m_synthetic;
+ }
+}
+
+ILCodeVersion::ILCodeVersion(PTR_ILCodeVersionNode pILCodeVersionNode) :
+ m_storageKind(pILCodeVersionNode != NULL ? StorageKind::Explicit : StorageKind::Unknown),
+ m_pVersionNode(pILCodeVersionNode)
+{}
+
+ILCodeVersion::ILCodeVersion(PTR_Module pModule, mdMethodDef methodDef) :
+ m_storageKind(pModule != NULL ? StorageKind::Synthetic : StorageKind::Unknown)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ m_synthetic.m_pModule = pModule;
+ m_synthetic.m_methodDef = methodDef;
+}
+
+bool ILCodeVersion::operator==(const ILCodeVersion & rhs) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return (rhs.m_storageKind == StorageKind::Explicit) &&
+ (AsNode() == rhs.AsNode());
+ }
+ else if (m_storageKind == StorageKind::Synthetic)
+ {
+ return (rhs.m_storageKind == StorageKind::Synthetic) &&
+ (m_synthetic.m_pModule == rhs.m_synthetic.m_pModule) &&
+ (m_synthetic.m_methodDef == rhs.m_synthetic.m_methodDef);
+ }
+ else
+ {
+ return rhs.m_storageKind == StorageKind::Unknown;
+ }
+}
+
+BOOL ILCodeVersion::IsNull() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_storageKind == StorageKind::Unknown;
+}
+
+BOOL ILCodeVersion::IsDefaultVersion() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_storageKind == StorageKind::Synthetic;
+}
+
+PTR_Module ILCodeVersion::GetModule() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetModule();
+ }
+ else
+ {
+ return m_synthetic.m_pModule;
+ }
+}
+
+mdMethodDef ILCodeVersion::GetMethodDef() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetMethodDef();
+ }
+ else
+ {
+ return m_synthetic.m_methodDef;
+ }
+}
+
+ReJITID ILCodeVersion::GetVersionId() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetVersionId();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+NativeCodeVersionCollection ILCodeVersion::GetNativeCodeVersions(PTR_MethodDesc pClosedMethodDesc) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return NativeCodeVersionCollection(pClosedMethodDesc, *this);
+}
+
+NativeCodeVersion ILCodeVersion::GetActiveNativeCodeVersion(PTR_MethodDesc pClosedMethodDesc) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ NativeCodeVersionCollection versions = GetNativeCodeVersions(pClosedMethodDesc);
+ for (NativeCodeVersionIterator cur = versions.Begin(), end = versions.End(); cur != end; cur++)
+ {
+ if (cur->IsActiveChildVersion())
+ {
+ return *cur;
+ }
+ }
+ return NativeCodeVersion();
+}
+
+ILCodeVersion::RejitFlags ILCodeVersion::GetRejitState() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetRejitState();
+ }
+ else
+ {
+ return ILCodeVersion::kStateActive;
+ }
+}
+
+PTR_COR_ILMETHOD ILCodeVersion::GetIL() const
+{
+ CONTRACTL
+ {
+ THROWS; //GetILHeader throws
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ MODE_ANY;
+ }
+ CONTRACTL_END
+
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetIL();
+ }
+ else
+ {
+ PTR_Module pModule = GetModule();
+ PTR_MethodDesc pMethodDesc = dac_cast<PTR_MethodDesc>(pModule->LookupMethodDef(GetMethodDef()));
+ if (pMethodDesc == NULL)
+ {
+ return NULL;
+ }
+ else
+ {
+ return dac_cast<PTR_COR_ILMETHOD>(pMethodDesc->GetILHeader(TRUE));
+ }
+ }
+}
+
+PTR_COR_ILMETHOD ILCodeVersion::GetILNoThrow() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ PTR_COR_ILMETHOD ret;
+ EX_TRY
+ {
+ ret = GetIL();
+ }
+ EX_CATCH
+ {
+ ret = NULL;
+ }
+ EX_END_CATCH(RethrowTerminalExceptions);
+ return ret;
+}
+
+DWORD ILCodeVersion::GetJitFlags() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetJitFlags();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+const InstrumentedILOffsetMapping* ILCodeVersion::GetInstrumentedILMap() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_storageKind == StorageKind::Explicit)
+ {
+ return AsNode()->GetInstrumentedILMap();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+#ifndef DACCESS_COMPILE
+void ILCodeVersion::SetRejitState(RejitFlags newState)
+{
+ LIMITED_METHOD_CONTRACT;
+ AsNode()->SetRejitState(newState);
+}
+
+void ILCodeVersion::SetIL(COR_ILMETHOD* pIL)
+{
+ LIMITED_METHOD_CONTRACT;
+ AsNode()->SetIL(pIL);
+}
+
+void ILCodeVersion::SetJitFlags(DWORD flags)
+{
+ LIMITED_METHOD_CONTRACT;
+ AsNode()->SetJitFlags(flags);
+}
+
+void ILCodeVersion::SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap)
+{
+ LIMITED_METHOD_CONTRACT;
+ AsNode()->SetInstrumentedILMap(cMap, rgMap);
+}
+
+HRESULT ILCodeVersion::AddNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion)
+{
+ LIMITED_METHOD_CONTRACT;
+ CodeVersionManager* pManager = GetModule()->GetCodeVersionManager();
+ HRESULT hr = pManager->AddNativeCodeVersion(*this, pClosedMethodDesc, pNativeCodeVersion);
+ if (FAILED(hr))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+ return S_OK;
+}
+
+HRESULT ILCodeVersion::GetOrCreateActiveNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion* pActiveNativeCodeVersion)
+{
+ LIMITED_METHOD_CONTRACT;
+ HRESULT hr = S_OK;
+ NativeCodeVersion activeNativeChild = GetActiveNativeCodeVersion(pClosedMethodDesc);
+ if (activeNativeChild.IsNull())
+ {
+ if (FAILED(hr = AddNativeCodeVersion(pClosedMethodDesc, &activeNativeChild)))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+ }
+ // The first added child should automatically become active
+ _ASSERTE(GetActiveNativeCodeVersion(pClosedMethodDesc) == activeNativeChild);
+ *pActiveNativeCodeVersion = activeNativeChild;
+ return S_OK;
+}
+
+HRESULT ILCodeVersion::SetActiveNativeCodeVersion(NativeCodeVersion activeNativeCodeVersion, BOOL fEESuspended)
+{
+ LIMITED_METHOD_CONTRACT;
+ HRESULT hr = S_OK;
+ MethodDesc* pMethodDesc = activeNativeCodeVersion.GetMethodDesc();
+ NativeCodeVersion prevActiveVersion = GetActiveNativeCodeVersion(pMethodDesc);
+ if (prevActiveVersion == activeNativeCodeVersion)
+ {
+ //nothing to do, this version is already active
+ return S_OK;
+ }
+
+ if (!prevActiveVersion.IsNull())
+ {
+ prevActiveVersion.SetActiveChildFlag(FALSE);
+ }
+ activeNativeCodeVersion.SetActiveChildFlag(TRUE);
+
+ // If needed update the published code body for this method
+ CodeVersionManager* pCodeVersionManager = GetModule()->GetCodeVersionManager();
+ if (pCodeVersionManager->GetActiveILCodeVersion(GetModule(), GetMethodDef()) == *this)
+ {
+ if (FAILED(hr = pCodeVersionManager->PublishNativeCodeVersion(pMethodDesc, activeNativeCodeVersion, fEESuspended)))
+ {
+ return hr;
+ }
+ }
+
+ return S_OK;
+}
+
+ILCodeVersionNode* ILCodeVersion::AsNode()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_pVersionNode;
+}
+#endif //DACCESS_COMPILE
+
+PTR_ILCodeVersionNode ILCodeVersion::AsNode() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_pVersionNode;
+}
+
+ILCodeVersionCollection::ILCodeVersionCollection(PTR_Module pModule, mdMethodDef methodDef) :
+ m_pModule(pModule),
+ m_methodDef(methodDef)
+{}
+
+ILCodeVersionIterator ILCodeVersionCollection::Begin()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return ILCodeVersionIterator(this);
+}
+
+ILCodeVersionIterator ILCodeVersionCollection::End()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return ILCodeVersionIterator(NULL);
+}
+
+ILCodeVersionIterator::ILCodeVersionIterator(const ILCodeVersionIterator & iter) :
+ m_stage(iter.m_stage),
+ m_cur(iter.m_cur),
+ m_pLinkedListCur(iter.m_pLinkedListCur),
+ m_pCollection(iter.m_pCollection)
+{}
+
+ILCodeVersionIterator::ILCodeVersionIterator(ILCodeVersionCollection* pCollection) :
+ m_stage(pCollection != NULL ? IterationStage::Initial : IterationStage::End),
+ m_pLinkedListCur(dac_cast<PTR_ILCodeVersionNode>(nullptr)),
+ m_pCollection(pCollection)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ First();
+}
+
+const ILCodeVersion & ILCodeVersionIterator::Get() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_cur;
+}
+
+void ILCodeVersionIterator::First()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ Next();
+}
+
+void ILCodeVersionIterator::Next()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ if (m_stage == IterationStage::Initial)
+ {
+ m_stage = IterationStage::ImplicitCodeVersion;
+ m_cur = ILCodeVersion(m_pCollection->m_pModule, m_pCollection->m_methodDef);
+ return;
+ }
+ if (m_stage == IterationStage::ImplicitCodeVersion)
+ {
+ CodeVersionManager* pCodeVersionManager = m_pCollection->m_pModule->GetCodeVersionManager();
+ _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread());
+ PTR_ILCodeVersioningState pILCodeVersioningState = pCodeVersionManager->GetILCodeVersioningState(m_pCollection->m_pModule, m_pCollection->m_methodDef);
+ if (pILCodeVersioningState != NULL)
+ {
+ m_pLinkedListCur = pILCodeVersioningState->GetFirstVersionNode();
+ }
+ m_stage = IterationStage::LinkedList;
+ if (m_pLinkedListCur != NULL)
+ {
+ m_cur = ILCodeVersion(m_pLinkedListCur);
+ return;
+ }
+ }
+ if (m_stage == IterationStage::LinkedList)
+ {
+ if (m_pLinkedListCur != NULL)
+ {
+ m_pLinkedListCur = m_pLinkedListCur->GetNextILVersionNode();
+ }
+ if (m_pLinkedListCur != NULL)
+ {
+ m_cur = ILCodeVersion(m_pLinkedListCur);
+ return;
+ }
+ else
+ {
+ m_stage = IterationStage::End;
+ m_cur = ILCodeVersion();
+ return;
+ }
+ }
+}
+
+bool ILCodeVersionIterator::Equal(const ILCodeVersionIterator &i) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_cur == i.m_cur;
+}
+
+MethodDescVersioningState::MethodDescVersioningState(PTR_MethodDesc pMethodDesc) :
+ m_pMethodDesc(pMethodDesc),
+ m_flags(IsDefaultVersionActiveChildFlag),
+ m_nextId(1),
+ m_pFirstVersionNode(dac_cast<PTR_NativeCodeVersionNode>(nullptr))
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+#ifdef FEATURE_JUMPSTAMP
+ ZeroMemory(m_rgSavedCode, JumpStubSize);
+#endif
+}
+
+PTR_MethodDesc MethodDescVersioningState::GetMethodDesc() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_pMethodDesc;
+}
+
+#ifndef DACCESS_COMPILE
+NativeCodeVersionId MethodDescVersioningState::AllocateVersionId()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_nextId++;
+}
+#endif
+
+PTR_NativeCodeVersionNode MethodDescVersioningState::GetFirstVersionNode() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_pFirstVersionNode;
+}
+
+#ifdef FEATURE_JUMPSTAMP
+MethodDescVersioningState::JumpStampFlags MethodDescVersioningState::GetJumpStampState()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (JumpStampFlags)(m_flags & JumpStampMask);
+}
+
+#ifndef DACCESS_COMPILE
+void MethodDescVersioningState::SetJumpStampState(JumpStampFlags newState)
+{
+ LIMITED_METHOD_CONTRACT;
+ m_flags = (m_flags & ~JumpStampMask) | (BYTE)newState;
+}
+#endif // DACCESS_COMPILE
+
+#ifndef DACCESS_COMPILE
+HRESULT MethodDescVersioningState::SyncJumpStamp(NativeCodeVersion nativeCodeVersion, BOOL fEESuspended)
+ {
+ LIMITED_METHOD_CONTRACT;
+ HRESULT hr = S_OK;
+ PCODE pCode = nativeCodeVersion.IsNull() ? NULL : nativeCodeVersion.GetNativeCode();
+ MethodDesc* pMethod = GetMethodDesc();
+ _ASSERTE(pMethod->IsVersionable() && pMethod->IsVersionableWithJumpStamp());
+
+ if (!pMethod->HasNativeCode())
+ {
+ //we'll set up the jump-stamp when the default native code is created
+ return S_OK;
+ }
+
+ if (!nativeCodeVersion.IsNull() && nativeCodeVersion.IsDefaultVersion())
+ {
+ return UndoJumpStampNativeCode(fEESuspended);
+ }
+ else
+ {
+ // We don't have new code ready yet, jumpstamp back to the prestub to let us generate it the next time
+ // the method is called
+ if (pCode == NULL)
+ {
+ if (!fEESuspended)
+ {
+ return CORPROF_E_RUNTIME_SUSPEND_REQUIRED;
+ }
+ return JumpStampNativeCode();
+ }
+ // We do know the new code body, install the jump stamp now
+ else
+ {
+ return UpdateJumpTarget(fEESuspended, pCode);
+ }
+ }
+}
+#endif // DACCESS_COMPILE
+
+//---------------------------------------------------------------------------------------
+//
+// Simple, thin abstraction of debugger breakpoint patching. Given an address and a
+// previously procured DebuggerControllerPatch governing the code address, this decides
+// whether the code address is patched. If so, it returns a pointer to the debugger's
+// buffer (of what's "underneath" the int 3 patch); otherwise, it returns the code
+// address itself.
+//
+// Arguments:
+// * pbCode - Code address to return if unpatched
+// * dbgpatch - DebuggerControllerPatch to test
+//
+// Return Value:
+// Either pbCode or the debugger's patch buffer, as per description above.
+//
+// Assumptions:
+// Caller must manually grab (and hold) the ControllerLockHolder and get the
+// DebuggerControllerPatch before calling this helper.
+//
+// Notes:
+// pbCode need not equal the code address governed by dbgpatch, but is always
+// "related" (and sometimes really is equal). For example, this helper may be used
+// when writing a code byte to an internal rejit buffer (e.g., in preparation for an
+// eventual 64-bit interlocked write into the code stream), and thus pbCode would
+// point into the internal rejit buffer whereas dbgpatch governs the corresponding
+// code byte in the live code stream. This function would then be used to determine
+// whether a byte should be written into the internal rejit buffer OR into the
+// debugger controller's breakpoint buffer.
+//
+
+LPBYTE FirstCodeByteAddr(LPBYTE pbCode, DebuggerControllerPatch * dbgpatch)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (dbgpatch != NULL && dbgpatch->IsActivated())
+ {
+ // Debugger has patched the code, so return the address of the buffer
+ return LPBYTE(&(dbgpatch->opcode));
+ }
+
+ // no active patch, just return the direct code address
+ return pbCode;
+}
+
+
+#ifdef _DEBUG
+#ifndef DACCESS_COMPILE
+BOOL MethodDescVersioningState::CodeIsSaved()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ for (size_t i = 0; i < sizeof(m_rgSavedCode); i++)
+ {
+ if (m_rgSavedCode[i] != 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif //DACCESS_COMPILE
+#endif //_DEBUG
+
+//---------------------------------------------------------------------------------------
+//
+// Do the actual work of stamping the top of originally-jitted-code with a jmp that goes
+// to the prestub. This can be called in one of three ways:
+// * Case 1: By RequestReJIT against an already-jitted function, in which case the
+// PCODE may be inferred by the MethodDesc, and our caller will have suspended
+// the EE for us, OR
+// * Case 2: By the prestub worker after jitting the original code of a function
+// (i.e., the "pre-rejit" scenario). In this case, the EE is not suspended. But
+// that's ok, because the PCODE has not yet been published to the MethodDesc, and
+// no thread can be executing inside the originally JITted function yet.
+// * Case 3: At type/method restore time for an NGEN'ed assembly. This is also the pre-rejit
+// scenario because we are guaranteed to do this before the code in the module
+// is executable. EE suspend is not required.
+//
+// Arguments:
+// * pCode - Case 1 (above): will be NULL, and we can infer the PCODE from the
+// MethodDesc; Case 2+3 (above, pre-rejit): will be non-NULL, and we'll need to use
+// this to find the code to stamp on top of.
+//
+// Return Value:
+// * S_OK: Either we successfully did the jmp-stamp, or a racing thread took care of
+// it for us.
+// * Else, HRESULT indicating failure.
+//
+// Assumptions:
+// The caller will have suspended the EE if necessary (case 1), before this is
+// called.
+//
+#ifndef DACCESS_COMPILE
+HRESULT MethodDescVersioningState::JumpStampNativeCode(PCODE pCode /* = NULL */)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ // It may seem dangerous to be stamping jumps over code while a GC is going on,
+ // but we're actually safe. As we assert below, either we're holding the thread
+ // store lock (and thus preventing a GC) OR we're stamping code that has not yet
+ // been published (and will thus not be executed by managed therads or examined
+ // by the GC).
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ PCODE pCodePublished = GetMethodDesc()->GetNativeCode();
+
+ _ASSERTE((pCode != NULL) || (pCodePublished != NULL));
+ _ASSERTE(GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread());
+
+ HRESULT hr = S_OK;
+
+ // We'll jump-stamp over pCode, or if pCode is NULL, jump-stamp over the published
+ // code for this's MethodDesc.
+ LPBYTE pbCode = (LPBYTE)pCode;
+ if (pbCode == NULL)
+ {
+ // If caller didn't specify a pCode, just use the one that was published after
+ // the original JIT. (A specific pCode would be passed in the pre-rejit case,
+ // to jump-stamp the original code BEFORE the PCODE gets published.)
+ pbCode = (LPBYTE)pCodePublished;
+ }
+ _ASSERTE(pbCode != NULL);
+
+ // The debugging API may also try to write to the very top of this function (though
+ // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know
+ // whether we can safely patch the actual code, or instead write to the debugger's
+ // buffer.
+ DebuggerController::ControllerLockHolder lockController;
+
+ if (GetJumpStampState() == JumpStampToPrestub)
+ {
+ // The method has already been jump stamped so nothing left to do
+ _ASSERTE(CodeIsSaved());
+ return S_OK;
+ }
+
+ // Remember what we're stamping our jump on top of, so we can replace it during a
+ // revert.
+ if (GetJumpStampState() == JumpStampNone)
+ {
+ for (int i = 0; i < sizeof(m_rgSavedCode); i++)
+ {
+ m_rgSavedCode[i] = *FirstCodeByteAddr(pbCode + i, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)(pbCode + i)));
+ }
+ }
+
+ EX_TRY
+ {
+ AllocMemTracker amt;
+
+ // This guy might throw on out-of-memory, so rely on the tracker to clean-up
+ Precode * pPrecode = Precode::Allocate(PRECODE_STUB, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator(), &amt);
+ PCODE target = pPrecode->GetEntryPoint();
+
+#if defined(_X86_) || defined(_AMD64_)
+
+ // Normal unpatched code never starts with a jump
+ _ASSERTE(GetJumpStampState() == JumpStampToActiveVersion ||
+ *FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) != X86_INSTR_JMP_REL32);
+
+ INT64 i64OldCode = *(INT64*)pbCode;
+ INT64 i64NewCode = i64OldCode;
+ LPBYTE pbNewValue = (LPBYTE)&i64NewCode;
+ *pbNewValue = X86_INSTR_JMP_REL32;
+ INT32 UNALIGNED * pOffset = reinterpret_cast<INT32 UNALIGNED *>(&pbNewValue[1]);
+ // This will throw for out-of-memory, so don't write anything until
+ // after he succeeds
+ // This guy will leak/cache/reuse the jumpstub
+ *pOffset = rel32UsingJumpStub(reinterpret_cast<INT32 UNALIGNED *>(pbCode + 1), target, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator());
+
+ // If we have the EE suspended or the code is unpublished there won't be contention on this code
+ hr = UpdateJumpStampHelper(pbCode, i64OldCode, i64NewCode, FALSE);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+
+ //
+ // No failure point after this!
+ //
+ amt.SuppressRelease();
+
+#else // _X86_ || _AMD64_
+#error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
+#endif // _X86_ || _AMD64_
+
+ SetJumpStampState(JumpStampToPrestub);
+ }
+ EX_CATCH_HRESULT(hr);
+ _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY);
+
+ if (SUCCEEDED(hr))
+ {
+ _ASSERTE(GetJumpStampState() == JumpStampToPrestub);
+ _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0
+ }
+
+ return hr;
+}
+
+
+//---------------------------------------------------------------------------------------
+//
+// After code has been rejitted, this is called to update the jump-stamp to go from
+// pointing to the prestub, to pointing to the newly rejitted code.
+//
+// Arguments:
+// fEESuspended - TRUE if the caller keeps the EE suspended during this call
+// pRejittedCode - jitted code for the updated IL this method should execute
+//
+// Assumptions:
+// This rejit manager's table crst should be held by the caller
+//
+// Returns - S_OK if the jump target is updated
+// CORPROF_E_RUNTIME_SUSPEND_REQUIRED if the ee isn't suspended and it
+// will need to be in order to do the update safely
+HRESULT MethodDescVersioningState::UpdateJumpTarget(BOOL fEESuspended, PCODE pRejittedCode)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ MethodDesc * pMD = GetMethodDesc();
+ _ASSERTE(pMD->GetCodeVersionManager()->LockOwnedByCurrentThread());
+
+ // It isn't safe to overwrite the original method prolog with a jmp because threads might
+ // be at an IP in the middle of the jump stamp already. However converting between different
+ // jump stamps is OK (when done atomically) because this only changes the jmp target, not
+ // instruction boundaries.
+ if (GetJumpStampState() == JumpStampNone && !fEESuspended)
+ {
+ return CORPROF_E_RUNTIME_SUSPEND_REQUIRED;
+ }
+
+ // Beginning of originally JITted code containing the jmp that we will redirect.
+ BYTE * pbCode = (BYTE*)pMD->GetNativeCode();
+
+ // Remember what we're stamping our jump on top of, so we can replace it during a
+ // revert.
+ if (GetJumpStampState() == JumpStampNone)
+ {
+ for (int i = 0; i < sizeof(m_rgSavedCode); i++)
+ {
+ m_rgSavedCode[i] = *FirstCodeByteAddr(pbCode + i, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)(pbCode + i)));
+ }
+ }
+
+#if defined(_X86_) || defined(_AMD64_)
+
+ HRESULT hr = S_OK;
+ {
+ DebuggerController::ControllerLockHolder lockController;
+
+ // This will throw for out-of-memory, so don't write anything until
+ // after he succeeds
+ // This guy will leak/cache/reuse the jumpstub
+ INT32 offset = 0;
+ EX_TRY
+ {
+ offset = rel32UsingJumpStub(
+ reinterpret_cast<INT32 UNALIGNED *>(&pbCode[1]), // base of offset
+ pRejittedCode, // target of jump
+ pMD,
+ pMD->GetLoaderAllocator());
+ }
+ EX_CATCH_HRESULT(hr);
+ _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ // For validation later, remember what pbCode is right now
+ INT64 i64OldValue = *(INT64 *)pbCode;
+
+ // Assemble the INT64 of the new code bytes to write. Start with what's there now
+ INT64 i64NewValue = i64OldValue;
+ LPBYTE pbNewValue = (LPBYTE)&i64NewValue;
+
+ // First byte becomes a rel32 jmp instruction (if it wasn't already)
+ *pbNewValue = X86_INSTR_JMP_REL32;
+ // Next 4 bytes are the jmp target (offset to jmp stub)
+ INT32 UNALIGNED * pnOffset = reinterpret_cast<INT32 UNALIGNED *>(&pbNewValue[1]);
+ *pnOffset = offset;
+
+ hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended);
+ _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended));
+ }
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+#else // _X86_ || _AMD64_
+#error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
+#endif // _X86_ || _AMD64_
+
+ // State transition
+ SetJumpStampState(JumpStampToActiveVersion);
+ return S_OK;
+}
+
+
+//---------------------------------------------------------------------------------------
+//
+// Poke the JITted code to satsify a revert request (or to perform an implicit revert as
+// part of a second, third, etc. rejit request). Reinstates the originally JITted code
+// that had been jump-stamped over to perform a prior rejit.
+//
+// Arguments
+// fEESuspended - TRUE if the caller keeps the EE suspended during this call
+//
+//
+// Return Value:
+// S_OK to indicate the revert succeeded,
+// CORPROF_E_RUNTIME_SUSPEND_REQUIRED to indicate the jumpstamp hasn't been reverted
+// and EE suspension will be needed for success
+// other failure HRESULT indicating what went wrong.
+//
+// Assumptions:
+// Caller must be holding the owning ReJitManager's table crst.
+//
+HRESULT MethodDescVersioningState::UndoJumpStampNativeCode(BOOL fEESuspended)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread());
+ if (GetJumpStampState() == JumpStampNone)
+ {
+ return S_OK;
+ }
+
+ _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0
+
+ BYTE * pbCode = (BYTE*)GetMethodDesc()->GetNativeCode();
+ DebuggerController::ControllerLockHolder lockController;
+
+#if defined(_X86_) || defined(_AMD64_)
+ _ASSERTE(m_rgSavedCode[0] != X86_INSTR_JMP_REL32);
+ _ASSERTE(*FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) == X86_INSTR_JMP_REL32);
+#else
+#error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
+#endif // _X86_ || _AMD64_
+
+ // For the interlocked compare, remember what pbCode is right now
+ INT64 i64OldValue = *(INT64 *)pbCode;
+ // Assemble the INT64 of the new code bytes to write. Start with what's there now
+ INT64 i64NewValue = i64OldValue;
+ memcpy(LPBYTE(&i64NewValue), m_rgSavedCode, sizeof(m_rgSavedCode));
+ HRESULT hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended);
+ _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended));
+ if (hr != S_OK)
+ return hr;
+
+ // Transition state of this ReJitInfo to indicate the MD no longer has any jump stamp
+ SetJumpStampState(JumpStampNone);
+ return S_OK;
+}
+#endif
+
+//---------------------------------------------------------------------------------------
+//
+// This is called to modify the jump-stamp area, the first ReJitInfo::JumpStubSize bytes
+// in the method's code.
+//
+// Notes:
+// Callers use this method in a variety of circumstances:
+// a) when the code is unpublished (fContentionPossible == FALSE)
+// b) when the caller has taken the ThreadStoreLock and suspended the EE
+// (fContentionPossible == FALSE)
+// c) when the code is published, the EE isn't suspended, and the jumpstamp
+// area consists of a single 5 byte long jump instruction
+// (fContentionPossible == TRUE)
+// This method will attempt to alter the jump-stamp even if the caller has not prevented
+// contention, but there is no guarantee it will be succesful. When the caller has prevented
+// contention, then success is assured. Callers may oportunistically try without
+// EE suspension, and then upgrade to EE suspension if the first attempt fails.
+//
+// Assumptions:
+// This rejit manager's table crst should be held by the caller or fContentionPossible==FALSE
+// The debugger patch table lock should be held by the caller
+//
+// Arguments:
+// pbCode - pointer to the code where the jump stamp is placed
+// i64OldValue - the bytes which should currently be at the start of the method code
+// i64NewValue - the new bytes which should be written at the start of the method code
+// fContentionPossible - See the Notes section above.
+//
+// Returns:
+// S_OK => the jumpstamp has been succesfully updated.
+// CORPROF_E_RUNTIME_SUSPEND_REQUIRED => the jumpstamp remains unchanged (preventing contention will be necessary)
+// other failing HR => VirtualProtect failed, the jumpstamp remains unchanged
+//
+#ifndef DACCESS_COMPILE
+HRESULT MethodDescVersioningState::UpdateJumpStampHelper(BYTE* pbCode, INT64 i64OldValue, INT64 i64NewValue, BOOL fContentionPossible)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ MethodDesc * pMD = GetMethodDesc();
+ _ASSERTE(pMD->GetCodeVersionManager()->LockOwnedByCurrentThread() || !fContentionPossible);
+
+ // When ReJIT is enabled, method entrypoints are always at least 8-byte aligned (see
+ // code:EEJitManager::allocCode), so we can do a single 64-bit interlocked operation
+ // to update the jump target. However, some code may have gotten compiled before
+ // the profiler had a chance to enable ReJIT (e.g., NGENd code, or code JITted
+ // before a profiler attaches). In such cases, we cannot rely on a simple
+ // interlocked operation, and instead must suspend the runtime to ensure we can
+ // safely update the jmp instruction.
+ //
+ // This method doesn't verify that the method is actually safe to rejit, we expect
+ // callers to do that. At the moment NGEN'ed code is safe to rejit even if
+ // it is unaligned, but code generated before the profiler attaches is not.
+ if (fContentionPossible && !(IS_ALIGNED(pbCode, sizeof(INT64))))
+ {
+ return CORPROF_E_RUNTIME_SUSPEND_REQUIRED;
+ }
+
+ // The debugging API may also try to write to this function (though
+ // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know
+ // whether we can safely patch the actual code, or instead write to the debugger's
+ // buffer.
+ if (fContentionPossible)
+ {
+ for (CORDB_ADDRESS_TYPE* pbProbeAddr = pbCode; pbProbeAddr < pbCode + MethodDescVersioningState::JumpStubSize; pbProbeAddr++)
+ {
+ if (NULL != DebuggerController::GetPatchTable()->GetPatch(pbProbeAddr))
+ {
+ return CORPROF_E_RUNTIME_SUSPEND_REQUIRED;
+ }
+ }
+ }
+
+#if defined(_X86_) || defined(_AMD64_)
+
+ DWORD oldProt;
+ if (!ClrVirtualProtect((LPVOID)pbCode, 8, PAGE_EXECUTE_READWRITE, &oldProt))
+ {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ if (fContentionPossible)
+ {
+ INT64 i64InterlockReportedOldValue = FastInterlockCompareExchangeLong((INT64 *)pbCode, i64NewValue, i64OldValue);
+ // Since changes to these bytes are protected by this rejitmgr's m_crstTable, we
+ // shouldn't have two writers conflicting.
+ _ASSERTE(i64InterlockReportedOldValue == i64OldValue);
+ }
+ else
+ {
+ // In this path the caller ensures:
+ // a) no thread will execute through the prologue area we are modifying
+ // b) no thread is stopped in a prologue such that it resumes in the middle of code we are modifying
+ // c) no thread is doing a debugger patch skip operation in which an unmodified copy of the method's
+ // code could be executed from a patch skip buffer.
+
+ // PERF: we might still want a faster path through here if we aren't debugging that doesn't do
+ // all the patch checks
+ for (int i = 0; i < MethodDescVersioningState::JumpStubSize; i++)
+ {
+ *FirstCodeByteAddr(pbCode + i, DebuggerController::GetPatchTable()->GetPatch(pbCode + i)) = ((BYTE*)&i64NewValue)[i];
+ }
+ }
+
+ if (oldProt != PAGE_EXECUTE_READWRITE)
+ {
+ // The CLR codebase in many locations simply ignores failures to restore the page protections
+ // Its true that it isn't a problem functionally, but it seems a bit sketchy?
+ // I am following the convention for now.
+ ClrVirtualProtect((LPVOID)pbCode, 8, oldProt, &oldProt);
+ }
+
+ FlushInstructionCache(GetCurrentProcess(), pbCode, MethodDescVersioningState::JumpStubSize);
+ return S_OK;
+
+#else // _X86_ || _AMD64_
+#error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
+#endif // _X86_ || _AMD64_
+}
+#endif
+#endif // FEATURE_JUMPSTAMP
+
+BOOL MethodDescVersioningState::IsDefaultVersionActiveChild() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (m_flags & IsDefaultVersionActiveChildFlag) != 0;
+}
+#ifndef DACCESS_COMPILE
+void MethodDescVersioningState::SetDefaultVersionActiveChildFlag(BOOL isActive)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (isActive)
+ {
+ m_flags |= IsDefaultVersionActiveChildFlag;
+ }
+ else
+ {
+ m_flags &= ~IsDefaultVersionActiveChildFlag;
+ }
+}
+
+void MethodDescVersioningState::LinkNativeCodeVersionNode(NativeCodeVersionNode* pNativeCodeVersionNode)
+{
+ LIMITED_METHOD_CONTRACT;
+ pNativeCodeVersionNode->m_pNextMethodDescSibling = m_pFirstVersionNode;
+ m_pFirstVersionNode = pNativeCodeVersionNode;
+}
+#endif
+
+ILCodeVersioningState::ILCodeVersioningState(PTR_Module pModule, mdMethodDef methodDef) :
+ m_activeVersion(ILCodeVersion(pModule,methodDef)),
+ m_pFirstVersionNode(dac_cast<PTR_ILCodeVersionNode>(nullptr)),
+ m_pModule(pModule),
+ m_methodDef(methodDef)
+{}
+
+
+ILCodeVersioningState::Key::Key() :
+ m_pModule(dac_cast<PTR_Module>(nullptr)),
+ m_methodDef(0)
+{}
+
+ILCodeVersioningState::Key::Key(PTR_Module pModule, mdMethodDef methodDef) :
+ m_pModule(pModule),
+ m_methodDef(methodDef)
+{}
+
+size_t ILCodeVersioningState::Key::Hash() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (size_t)(dac_cast<TADDR>(m_pModule) ^ m_methodDef);
+}
+
+bool ILCodeVersioningState::Key::operator==(const Key & rhs) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (m_pModule == rhs.m_pModule) && (m_methodDef == rhs.m_methodDef);
+}
+
+ILCodeVersioningState::Key ILCodeVersioningState::GetKey() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return Key(m_pModule, m_methodDef);
+}
+
+ILCodeVersion ILCodeVersioningState::GetActiveVersion() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_activeVersion;
+}
+
+PTR_ILCodeVersionNode ILCodeVersioningState::GetFirstVersionNode() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_pFirstVersionNode;
+}
+
+#ifndef DACCESS_COMPILE
+void ILCodeVersioningState::SetActiveVersion(ILCodeVersion ilActiveCodeVersion)
+{
+ LIMITED_METHOD_CONTRACT;
+ m_activeVersion = ilActiveCodeVersion;
+}
+
+void ILCodeVersioningState::LinkILCodeVersionNode(ILCodeVersionNode* pILCodeVersionNode)
+{
+ LIMITED_METHOD_CONTRACT;
+ pILCodeVersionNode->SetNextILVersionNode(m_pFirstVersionNode);
+ m_pFirstVersionNode = pILCodeVersionNode;
+}
+#endif
+
+CodeVersionManager::CodeVersionManager()
+{}
+
+//---------------------------------------------------------------------------------------
+//
+// Called from BaseDomain::BaseDomain to do any constructor-time initialization.
+// Presently, this takes care of initializing the Crst, choosing the type based on
+// whether this ReJitManager belongs to the SharedDomain.
+//
+// Arguments:
+// * fSharedDomain - nonzero iff this ReJitManager belongs to the SharedDomain.
+//
+
+void CodeVersionManager::PreInit(BOOL fSharedDomain)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ CAN_TAKE_LOCK;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+#ifndef DACCESS_COMPILE
+ m_crstTable.Init(
+ fSharedDomain ? CrstReJITSharedDomainTable : CrstReJITDomainTable,
+ CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN));
+#endif // DACCESS_COMPILE
+}
+
+CodeVersionManager::TableLockHolder::TableLockHolder(CodeVersionManager* pCodeVersionManager) :
+ CrstHolder(&pCodeVersionManager->m_crstTable)
+{
+}
+#ifndef DACCESS_COMPILE
+void CodeVersionManager::EnterLock()
+{
+ m_crstTable.Enter();
+}
+void CodeVersionManager::LeaveLock()
+{
+ m_crstTable.Leave();
+}
+#endif
+
+#ifdef DEBUG
+BOOL CodeVersionManager::LockOwnedByCurrentThread() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+#ifdef DACCESS_COMPILE
+ return TRUE;
+#else
+ return const_cast<CrstExplicitInit &>(m_crstTable).OwnedByCurrentThread();
+#endif
+}
+#endif
+
+PTR_ILCodeVersioningState CodeVersionManager::GetILCodeVersioningState(PTR_Module pModule, mdMethodDef methodDef) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ ILCodeVersioningState::Key key = ILCodeVersioningState::Key(pModule, methodDef);
+ return m_ilCodeVersioningStateMap.Lookup(key);
+}
+
+PTR_MethodDescVersioningState CodeVersionManager::GetMethodDescVersioningState(PTR_MethodDesc pClosedMethodDesc) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_methodDescVersioningStateMap.Lookup(pClosedMethodDesc);
+}
+
+#ifndef DACCESS_COMPILE
+HRESULT CodeVersionManager::GetOrCreateILCodeVersioningState(Module* pModule, mdMethodDef methodDef, ILCodeVersioningState** ppILCodeVersioningState)
+{
+ LIMITED_METHOD_CONTRACT;
+ HRESULT hr = S_OK;
+ ILCodeVersioningState* pILCodeVersioningState = GetILCodeVersioningState(pModule, methodDef);
+ if (pILCodeVersioningState == NULL)
+ {
+ pILCodeVersioningState = new (nothrow) ILCodeVersioningState(pModule, methodDef);
+ if (pILCodeVersioningState == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ EX_TRY
+ {
+ // This throws when out of memory, but remains internally
+ // consistent (without adding the new element)
+ m_ilCodeVersioningStateMap.Add(pILCodeVersioningState);
+ }
+ EX_CATCH_HRESULT(hr);
+ if (FAILED(hr))
+ {
+ delete pILCodeVersioningState;
+ return hr;
+ }
+ }
+ *ppILCodeVersioningState = pILCodeVersioningState;
+ return S_OK;
+}
+
+HRESULT CodeVersionManager::GetOrCreateMethodDescVersioningState(MethodDesc* pMethod, MethodDescVersioningState** ppMethodVersioningState)
+{
+ LIMITED_METHOD_CONTRACT;
+ HRESULT hr = S_OK;
+ MethodDescVersioningState* pMethodVersioningState = m_methodDescVersioningStateMap.Lookup(pMethod);
+ if (pMethodVersioningState == NULL)
+ {
+ pMethodVersioningState = new (nothrow) MethodDescVersioningState(pMethod);
+ if (pMethodVersioningState == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ EX_TRY
+ {
+ // This throws when out of memory, but remains internally
+ // consistent (without adding the new element)
+ m_methodDescVersioningStateMap.Add(pMethodVersioningState);
+ }
+ EX_CATCH_HRESULT(hr);
+ if (FAILED(hr))
+ {
+ delete pMethodVersioningState;
+ return hr;
+ }
+ }
+ *ppMethodVersioningState = pMethodVersioningState;
+ return S_OK;
+}
+#endif // DACCESS_COMPILE
+
+DWORD CodeVersionManager::GetNonDefaultILVersionCount()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ //This function is legal to call WITHOUT taking the lock
+ //It is used to do a quick check if work might be needed without paying the overhead
+ //of acquiring the lock and doing dictionary lookups
+ return m_ilCodeVersioningStateMap.GetCount();
+}
+
+ILCodeVersionCollection CodeVersionManager::GetILCodeVersions(PTR_MethodDesc pMethod)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ return GetILCodeVersions(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef());
+}
+
+ILCodeVersionCollection CodeVersionManager::GetILCodeVersions(PTR_Module pModule, mdMethodDef methodDef)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ return ILCodeVersionCollection(pModule, methodDef);
+}
+
+ILCodeVersion CodeVersionManager::GetActiveILCodeVersion(PTR_MethodDesc pMethod)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ return GetActiveILCodeVersion(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef());
+}
+
+ILCodeVersion CodeVersionManager::GetActiveILCodeVersion(PTR_Module pModule, mdMethodDef methodDef)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ ILCodeVersioningState* pILCodeVersioningState = GetILCodeVersioningState(pModule, methodDef);
+ if (pILCodeVersioningState == NULL)
+ {
+ return ILCodeVersion(pModule, methodDef);
+ }
+ else
+ {
+ return pILCodeVersioningState->GetActiveVersion();
+ }
+}
+
+ILCodeVersion CodeVersionManager::GetILCodeVersion(PTR_MethodDesc pMethod, ReJITID rejitId)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+
+#ifdef FEATURE_REJIT
+ ILCodeVersionCollection collection = GetILCodeVersions(pMethod);
+ for (ILCodeVersionIterator cur = collection.Begin(), end = collection.End(); cur != end; cur++)
+ {
+ if (cur->GetVersionId() == rejitId)
+ {
+ return *cur;
+ }
+ }
+ return ILCodeVersion();
+#else // FEATURE_REJIT
+ _ASSERTE(rejitId == 0);
+ return ILCodeVersion(dac_cast<PTR_Module>(pMethod->GetModule()), pMethod->GetMemberDef());
+#endif // FEATURE_REJIT
+}
+
+NativeCodeVersionCollection CodeVersionManager::GetNativeCodeVersions(PTR_MethodDesc pMethod) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ return NativeCodeVersionCollection(pMethod, ILCodeVersion());
+}
+
+NativeCodeVersion CodeVersionManager::GetNativeCodeVersion(PTR_MethodDesc pMethod, PCODE codeStartAddress) const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+
+ NativeCodeVersionCollection nativeCodeVersions = GetNativeCodeVersions(pMethod);
+ for (NativeCodeVersionIterator cur = nativeCodeVersions.Begin(), end = nativeCodeVersions.End(); cur != end; cur++)
+ {
+ if (cur->GetNativeCode() == codeStartAddress)
+ {
+ return *cur;
+ }
+ }
+ return NativeCodeVersion();
+}
+
+#ifndef DACCESS_COMPILE
+HRESULT CodeVersionManager::AddILCodeVersion(Module* pModule, mdMethodDef methodDef, ReJITID rejitId, ILCodeVersion* pILCodeVersion)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+
+ ILCodeVersioningState* pILCodeVersioningState;
+ HRESULT hr = GetOrCreateILCodeVersioningState(pModule, methodDef, &pILCodeVersioningState);
+ if (FAILED(hr))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+
+ ILCodeVersionNode* pILCodeVersionNode = new (nothrow) ILCodeVersionNode(pModule, methodDef, rejitId);
+ if (pILCodeVersionNode == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ pILCodeVersioningState->LinkILCodeVersionNode(pILCodeVersionNode);
+ *pILCodeVersion = ILCodeVersion(pILCodeVersionNode);
+ return S_OK;
+}
+
+HRESULT CodeVersionManager::SetActiveILCodeVersions(ILCodeVersion* pActiveVersions, DWORD cActiveVersions, BOOL fEESuspended, CDynArray<CodePublishError> * pErrors)
+{
+ // If the IL version is in the shared domain we need to iterate all domains
+ // looking for instantiations. The domain iterator lock is bigger than
+ // the code version manager lock so we can't do this atomically. In one atomic
+ // update the bookkeeping for IL versioning will happen and then in a second
+ // update the active native code versions will change/code jumpstamps+precodes
+ // will update.
+ //
+ // Note: For all domains other than the shared AppDomain we could do this
+ // atomically, but for now we use the lowest common denominator for all
+ // domains.
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ CAN_TAKE_LOCK;
+ PRECONDITION(CheckPointer(pActiveVersions));
+ PRECONDITION(CheckPointer(pErrors, NULL_OK));
+ }
+ CONTRACTL_END;
+ _ASSERTE(!LockOwnedByCurrentThread());
+ HRESULT hr = S_OK;
+
+#if DEBUG
+ for (DWORD i = 0; i < cActiveVersions; i++)
+ {
+ ILCodeVersion activeVersion = pActiveVersions[i];
+ if (activeVersion.IsNull())
+ {
+ _ASSERTE(!"The active IL version can't be NULL");
+ }
+ }
+#endif
+
+ // step 1 - mark the IL versions as being active, this ensures that
+ // any new method instantiations added after this point will bind to
+ // the correct version
+ {
+ TableLockHolder(this);
+ for (DWORD i = 0; i < cActiveVersions; i++)
+ {
+ ILCodeVersion activeVersion = pActiveVersions[i];
+ ILCodeVersioningState* pILCodeVersioningState = NULL;
+ if (FAILED(hr = GetOrCreateILCodeVersioningState(activeVersion.GetModule(), activeVersion.GetMethodDef(), &pILCodeVersioningState)))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+ pILCodeVersioningState->SetActiveVersion(activeVersion);
+ }
+ }
+
+ // step 2 - determine the set of pre-existing method instantiations
+
+ // a parallel array to activeVersions
+ // for each ILCodeVersion in activeVersions, this lists the set
+ // MethodDescs that will need to be updated
+ CDynArray<CDynArray<MethodDesc*>> methodDescsToUpdate;
+ CDynArray<CodePublishError> errorRecords;
+ for (DWORD i = 0; i < cActiveVersions; i++)
+ {
+ CDynArray<MethodDesc*>* pMethodDescs = methodDescsToUpdate.Append();
+ if (pMethodDescs == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ *pMethodDescs = CDynArray<MethodDesc*>();
+
+ MethodDesc* pLoadedMethodDesc = pActiveVersions[i].GetModule()->LookupMethodDef(pActiveVersions[i].GetMethodDef());
+ if (FAILED(hr = CodeVersionManager::EnumerateClosedMethodDescs(pLoadedMethodDesc, pMethodDescs, &errorRecords)))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+ }
+
+ // step 3 - update each pre-existing method instantiation
+ {
+ TableLockHolder lock(this);
+ for (DWORD i = 0; i < cActiveVersions; i++)
+ {
+ // Its possible the active IL version has changed if
+ // another caller made an update while this method wasn't
+ // holding the lock. We will ensure that we synchronize
+ // publishing to whatever version is currently active, even
+ // if that isn't the IL version we set above.
+ //
+ // Note: Although we attempt to handle this case gracefully
+ // it isn't recommended for callers to do this. Racing two calls
+ // that set the IL version to different results means it will be
+ // completely arbitrary which version wins.
+ ILCodeVersion requestedActiveILVersion = pActiveVersions[i];
+ ILCodeVersion activeILVersion = GetActiveILCodeVersion(requestedActiveILVersion.GetModule(), requestedActiveILVersion.GetMethodDef());
+
+ CDynArray<MethodDesc*> methodDescs = methodDescsToUpdate[i];
+ for (int j = 0; j < methodDescs.Count(); j++)
+ {
+ // Get an the active child code version for this method instantiation (it might be NULL, that is OK)
+ NativeCodeVersion activeNativeChild = activeILVersion.GetActiveNativeCodeVersion(methodDescs[j]);
+
+ // Publish that child version, because it is the active native child of the active IL version
+ // Failing to publish is non-fatal, but we do record it so the caller is aware
+ if (FAILED(hr = PublishNativeCodeVersion(methodDescs[j], activeNativeChild, fEESuspended)))
+ {
+ if (FAILED(hr = AddCodePublishError(activeILVersion.GetModule(), activeILVersion.GetMethodDef(), methodDescs[j], hr, &errorRecords)))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+ }
+ }
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT CodeVersionManager::AddNativeCodeVersion(ILCodeVersion ilCodeVersion, MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+
+ MethodDescVersioningState* pMethodVersioningState;
+ HRESULT hr = GetOrCreateMethodDescVersioningState(pClosedMethodDesc, &pMethodVersioningState);
+ if (FAILED(hr))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+
+ NativeCodeVersionId newId = pMethodVersioningState->AllocateVersionId();
+ NativeCodeVersionNode* pNativeCodeVersionNode = new (nothrow) NativeCodeVersionNode(newId, pClosedMethodDesc, ilCodeVersion.GetVersionId());
+ if (pNativeCodeVersionNode == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ pMethodVersioningState->LinkNativeCodeVersionNode(pNativeCodeVersionNode);
+
+ // the first child added is automatically considered the active one.
+ if (ilCodeVersion.GetActiveNativeCodeVersion(pClosedMethodDesc).IsNull())
+ {
+ pNativeCodeVersionNode->SetActiveChildFlag(TRUE);
+ _ASSERTE(!ilCodeVersion.GetActiveNativeCodeVersion(pClosedMethodDesc).IsNull());
+
+ // the new child shouldn't have any native code. If it did we might need to
+ // publish that code as part of adding the node which would require callers
+ // to pay attention to GC suspension and we'd need to report publishing errors
+ // back to them.
+ _ASSERTE(pNativeCodeVersionNode->GetNativeCode() == NULL);
+ }
+ *pNativeCodeVersion = NativeCodeVersion(pNativeCodeVersionNode);
+ return S_OK;
+}
+
+PCODE CodeVersionManager::PublishVersionableCodeIfNecessary(MethodDesc* pMethodDesc, BOOL fCanBackpatchPrestub)
+{
+ STANDARD_VM_CONTRACT;
+ _ASSERTE(!LockOwnedByCurrentThread());
+ _ASSERTE(pMethodDesc->IsVersionable());
+ _ASSERTE(!pMethodDesc->IsPointingToPrestub() || !pMethodDesc->IsVersionableWithJumpStamp());
+
+ HRESULT hr = S_OK;
+ PCODE pCode = NULL;
+ BOOL fIsJumpStampMethod = pMethodDesc->IsVersionableWithJumpStamp();
+
+ NativeCodeVersion activeVersion;
+ {
+ TableLockHolder lock(this);
+ if (FAILED(hr = GetActiveILCodeVersion(pMethodDesc).GetOrCreateActiveNativeCodeVersion(pMethodDesc, &activeVersion)))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ ReportCodePublishError(pMethodDesc->GetModule(), pMethodDesc->GetMemberDef(), pMethodDesc, hr);
+ return NULL;
+ }
+ }
+
+ BOOL fEESuspend = FALSE;
+ while (true)
+ {
+ // compile the code if needed
+ pCode = activeVersion.GetNativeCode();
+ if (pCode == NULL)
+ {
+ pCode = pMethodDesc->PrepareCode(activeVersion);
+ }
+
+ // suspend in preparation for publishing if needed
+ if (fEESuspend)
+ {
+ ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT);
+ }
+
+ {
+ TableLockHolder lock(this);
+ // The common case is that newActiveCode == activeCode, however we did leave the lock so there is
+ // possibility that the active version has changed. If it has we need to restart the compilation
+ // and publishing process with the new active version instead.
+ //
+ // In theory it should be legitimate to break out of this loop and run the less recent active version,
+ // because ultimately this is a race between one thread that is updating the version and another thread
+ // trying to run the current version. However for back-compat with ReJIT we need to guarantee that
+ // a versioning update at least as late as the profiler JitCompilationFinished callback wins the race.
+ NativeCodeVersion newActiveVersion;
+ if (FAILED(hr = GetActiveILCodeVersion(pMethodDesc).GetOrCreateActiveNativeCodeVersion(pMethodDesc, &newActiveVersion)))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ ReportCodePublishError(pMethodDesc->GetModule(), pMethodDesc->GetMemberDef(), pMethodDesc, hr);
+ pCode = NULL;
+ break;
+ }
+ if (newActiveVersion != activeVersion)
+ {
+ activeVersion = newActiveVersion;
+ }
+ else
+ {
+ // if we aren't allowed to backpatch we are done
+ if (!fCanBackpatchPrestub)
+ {
+ break;
+ }
+
+ // attempt to publish the active version still under the lock
+ if (FAILED(hr = PublishNativeCodeVersion(pMethodDesc, activeVersion, fEESuspend)))
+ {
+ // if we need an EESuspend to publish then start over. We have to leave the lock in order to suspend,
+ // and when we leave the lock the active version might change again. However now we know that suspend
+ if (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED)
+ {
+ _ASSERTE(!fEESuspend);
+ fEESuspend = true;
+ }
+ else
+ {
+ ReportCodePublishError(pMethodDesc->GetModule(), pMethodDesc->GetMemberDef(), pMethodDesc, hr);
+ pCode = NULL;
+ break;
+ }
+ }
+ else
+ {
+ //success
+ break;
+ }
+ }
+ } // exit lock
+
+ if (fEESuspend)
+ {
+ ThreadSuspend::RestartEE(FALSE, TRUE);
+ }
+ }
+
+ // if the EE is still suspended from breaking in the middle of the loop, resume it
+ if (fEESuspend)
+ {
+ ThreadSuspend::RestartEE(FALSE, TRUE);
+ }
+ return pCode;
+}
+
+HRESULT CodeVersionManager::PublishNativeCodeVersion(MethodDesc* pMethod, NativeCodeVersion nativeCodeVersion, BOOL fEESuspended)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(LockOwnedByCurrentThread());
+ _ASSERTE(pMethod->IsVersionable());
+ HRESULT hr = S_OK;
+ PCODE pCode = nativeCodeVersion.IsNull() ? NULL : nativeCodeVersion.GetNativeCode();
+ if (pMethod->IsVersionableWithPrecode())
+ {
+ Precode* pPrecode = pMethod->GetOrCreatePrecode();
+ if (pCode == NULL)
+ {
+ EX_TRY
+ {
+ pPrecode->Reset();
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+ }
+ else
+ {
+ EX_TRY
+ {
+ hr = pPrecode->SetTargetInterlocked(pCode, FALSE) ? S_OK : E_FAIL;
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+ }
+ }
+ else
+ {
+#ifndef FEATURE_JUMPSTAMP
+ _ASSERTE(!"This platform doesn't support JumpStamp but this method doesn't version with Precode,"
+ " this method can't be updated");
+ return E_FAIL;
+#else
+ MethodDescVersioningState* pVersioningState;
+ if (FAILED(hr = GetOrCreateMethodDescVersioningState(pMethod, &pVersioningState)))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+ return pVersioningState->SyncJumpStamp(nativeCodeVersion, fEESuspended);
+#endif
+ }
+}
+
+// static
+HRESULT CodeVersionManager::EnumerateClosedMethodDescs(
+ MethodDesc* pMD,
+ CDynArray<MethodDesc*> * pClosedMethodDescs,
+ CDynArray<CodePublishError> * pUnsupportedMethodErrors)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ CAN_TAKE_LOCK;
+ PRECONDITION(CheckPointer(pMD, NULL_OK));
+ PRECONDITION(CheckPointer(pClosedMethodDescs));
+ PRECONDITION(CheckPointer(pUnsupportedMethodErrors));
+ }
+ CONTRACTL_END;
+ HRESULT hr = S_OK;
+ if (pMD == NULL)
+ {
+ // nothing is loaded yet so we're done for this method.
+ return S_OK;
+ }
+
+ if (!pMD->HasClassOrMethodInstantiation())
+ {
+ // We have a JITted non-generic.
+ MethodDesc ** ppMD = pClosedMethodDescs->Append();
+ if (ppMD == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ *ppMD = pMD;
+ }
+
+ if (!pMD->HasClassOrMethodInstantiation())
+ {
+ // not generic, we're done for this method
+ return S_OK;
+ }
+
+ // Ok, now the case of a generic function (or function on generic class), which
+ // is loaded, and may thus have compiled instantiations.
+ // It's impossible to get to any other kind of domain from the profiling API
+ Module* pModule = pMD->GetModule();
+ mdMethodDef methodDef = pMD->GetMemberDef();
+ BaseDomain * pBaseDomainFromModule = pModule->GetDomain();
+ _ASSERTE(pBaseDomainFromModule->IsAppDomain() ||
+ pBaseDomainFromModule->IsSharedDomain());
+
+ if (pBaseDomainFromModule->IsSharedDomain())
+ {
+ // Iterate through all modules loaded into the shared domain, to
+ // find all instantiations living in the shared domain. This will
+ // include orphaned code (i.e., shared code used by ADs that have
+ // all unloaded), which is good, because orphaned code could get
+ // re-adopted if a new AD is created that can use that shared code
+ hr = EnumerateDomainClosedMethodDescs(
+ NULL, // NULL means to search SharedDomain instead of an AD
+ pModule,
+ methodDef,
+ pClosedMethodDescs,
+ pUnsupportedMethodErrors);
+ }
+ else
+ {
+ // Module is unshared, so just use the module's domain to find instantiations.
+ hr = EnumerateDomainClosedMethodDescs(
+ pBaseDomainFromModule->AsAppDomain(),
+ pModule,
+ methodDef,
+ pClosedMethodDescs,
+ pUnsupportedMethodErrors);
+ }
+ if (FAILED(hr))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+
+ // We want to iterate through all compilations of existing instantiations to
+ // ensure they get marked for rejit. Note: There may be zero instantiations,
+ // but we won't know until we try.
+ if (pBaseDomainFromModule->IsSharedDomain())
+ {
+ // Iterate through all real domains, to find shared instantiations.
+ AppDomainIterator appDomainIterator(TRUE);
+ while (appDomainIterator.Next())
+ {
+ AppDomain * pAppDomain = appDomainIterator.GetDomain();
+ if (pAppDomain->IsUnloading())
+ {
+ continue;
+ }
+ hr = EnumerateDomainClosedMethodDescs(
+ pAppDomain,
+ pModule,
+ methodDef,
+ pClosedMethodDescs,
+ pUnsupportedMethodErrors);
+ if (FAILED(hr))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+ }
+ }
+ return S_OK;
+}
+
+// static
+HRESULT CodeVersionManager::EnumerateDomainClosedMethodDescs(
+ AppDomain * pAppDomainToSearch,
+ Module* pModuleContainingMethodDef,
+ mdMethodDef methodDef,
+ CDynArray<MethodDesc*> * pClosedMethodDescs,
+ CDynArray<CodePublishError> * pUnsupportedMethodErrors)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ CAN_TAKE_LOCK;
+ PRECONDITION(CheckPointer(pAppDomainToSearch, NULL_OK));
+ PRECONDITION(CheckPointer(pModuleContainingMethodDef));
+ PRECONDITION(CheckPointer(pClosedMethodDescs));
+ PRECONDITION(CheckPointer(pUnsupportedMethodErrors));
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(methodDef != mdTokenNil);
+
+ HRESULT hr;
+
+ BaseDomain * pDomainContainingGenericDefinition = pModuleContainingMethodDef->GetDomain();
+
+#ifdef _DEBUG
+ // If the generic definition is not loaded domain-neutral, then all its
+ // instantiations will also be non-domain-neutral and loaded into the same
+ // domain as the generic definition. So the caller may only pass the
+ // domain containing the generic definition as pAppDomainToSearch
+ if (!pDomainContainingGenericDefinition->IsSharedDomain())
+ {
+ _ASSERTE(pDomainContainingGenericDefinition == pAppDomainToSearch);
+ }
+#endif //_DEBUG
+
+ // If pAppDomainToSearch is NULL, iterate through all existing
+ // instantiations loaded into the SharedDomain. If pAppDomainToSearch is non-NULL,
+ // iterate through all existing instantiations in pAppDomainToSearch, and only consider
+ // instantiations in non-domain-neutral assemblies (as we already covered domain
+ // neutral assemblies when we searched the SharedDomain).
+ LoadedMethodDescIterator::AssemblyIterationMode mode = LoadedMethodDescIterator::kModeSharedDomainAssemblies;
+ // these are the default flags which won't actually be used in shared mode other than
+ // asserting they were specified with their default values
+ AssemblyIterationFlags assemFlags = (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution);
+ ModuleIterationOption moduleFlags = (ModuleIterationOption)kModIterIncludeLoaded;
+ if (pAppDomainToSearch != NULL)
+ {
+ mode = LoadedMethodDescIterator::kModeUnsharedADAssemblies;
+ assemFlags = (AssemblyIterationFlags)(kIncludeAvailableToProfilers | kIncludeExecution);
+ moduleFlags = (ModuleIterationOption)kModIterIncludeAvailableToProfilers;
+ }
+ LoadedMethodDescIterator it(
+ pAppDomainToSearch,
+ pModuleContainingMethodDef,
+ methodDef,
+ mode,
+ assemFlags,
+ moduleFlags);
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+ while (it.Next(pDomainAssembly.This()))
+ {
+ MethodDesc * pLoadedMD = it.Current();
+
+ if (!pLoadedMD->IsVersionable())
+ {
+ // For compatibility with the rejit APIs we ensure certain errors are detected and reported using their
+ // original HRESULTS
+ HRESULT errorHR = GetNonVersionableError(pLoadedMD);
+ if (FAILED(errorHR))
+ {
+ if (FAILED(hr = CodeVersionManager::AddCodePublishError(pModuleContainingMethodDef, methodDef, pLoadedMD, CORPROF_E_FUNCTION_IS_COLLECTIBLE, pUnsupportedMethodErrors)))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+ }
+ continue;
+ }
+
+#ifdef _DEBUG
+ if (!pDomainContainingGenericDefinition->IsSharedDomain())
+ {
+ // Method is defined outside of the shared domain, so its instantiation must
+ // be defined in the AD we're iterating over (pAppDomainToSearch, which, as
+ // asserted above, must be the same domain as the generic's definition)
+ _ASSERTE(pLoadedMD->GetDomain() == pAppDomainToSearch);
+ }
+#endif // _DEBUG
+
+ MethodDesc ** ppMD = pClosedMethodDescs->Append();
+ if (ppMD == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ *ppMD = pLoadedMD;
+ }
+ return S_OK;
+}
+#endif // DACCESS_COMPILE
+
+
+//---------------------------------------------------------------------------------------
+//
+// Given the default version code for a MethodDesc that is about to published, add
+// a jumpstamp pointing back to the prestub if the currently active version isn't
+// the default one. This called from the PublishMethodHolder.
+//
+// Arguments:
+// * pMD - MethodDesc to jmp-stamp
+// * pCode - Top of the code that was just jitted (using original IL).
+//
+//
+// Return value:
+// * S_OK: Either we successfully did the jmp-stamp, or we didn't have to
+// * Else, HRESULT indicating failure.
+
+// Assumptions:
+// The caller has not yet published pCode to the MethodDesc, so no threads can be
+// executing inside pMD's code yet. Thus, we don't need to suspend the runtime while
+// applying the jump-stamp like we usually do for rejit requests that are made after
+// a function has been JITted.
+//
+#ifndef DACCESS_COMPILE
+HRESULT CodeVersionManager::DoJumpStampIfNecessary(MethodDesc* pMD, PCODE pCode)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ CAN_TAKE_LOCK;
+ PRECONDITION(CheckPointer(pMD));
+ PRECONDITION(pCode != NULL);
+ }
+ CONTRACTL_END;
+
+ HRESULT hr;
+
+ _ASSERTE(LockOwnedByCurrentThread());
+
+ NativeCodeVersion activeCodeVersion = GetActiveILCodeVersion(pMD).GetActiveNativeCodeVersion(pMD);
+ if (activeCodeVersion.IsDefaultVersion())
+ {
+ //Method not requested to be rejitted, nothing to do
+ return S_OK;
+ }
+
+ if (!(pMD->IsVersionable() && pMD->IsVersionableWithJumpStamp()))
+ {
+ return GetNonVersionableError(pMD);
+ }
+
+#ifndef FEATURE_JUMPSTAMP
+ _ASSERTE(!"How did we get here? IsVersionableWithJumpStamp() should have been FALSE above");
+ return S_OK;
+#else
+ MethodDescVersioningState* pVersioningState;
+ if (FAILED(hr = GetOrCreateMethodDescVersioningState(pMD, &pVersioningState)))
+ {
+ _ASSERTE(hr == E_OUTOFMEMORY);
+ return hr;
+ }
+ if (pVersioningState->GetJumpStampState() != MethodDescVersioningState::JumpStampNone)
+ {
+ //JumpStamp already in place
+ return S_OK;
+ }
+ return pVersioningState->JumpStampNativeCode(pCode);
+#endif // FEATURE_JUMPSTAMP
+
+}
+#endif // DACCESS_COMPILE
+
+#ifndef DACCESS_COMPILE
+//static
+void CodeVersionManager::OnAppDomainExit(AppDomain * pAppDomain)
+{
+ LIMITED_METHOD_CONTRACT;
+ // This would clean up all the allocations we have done and synchronize with any threads that might
+ // still be using the data
+ _ASSERTE(!".Net Core shouldn't be doing app domain shutdown - if we start doing so this needs to be implemented");
+}
+#endif
+
+//---------------------------------------------------------------------------------------
+//
+// Small helper to determine whether a given (possibly instantiated generic) MethodDesc
+// is safe to rejit.
+//
+// Arguments:
+// pMD - MethodDesc to test
+// Return Value:
+// S_OK iff pMD is safe to rejit
+// CORPROF_E_FUNCTION_IS_COLLECTIBLE - function can't be rejitted because it is collectible
+//
+
+// static
+#ifndef DACCESS_COMPILE
+HRESULT CodeVersionManager::GetNonVersionableError(MethodDesc* pMD)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CAN_TAKE_LOCK;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(pMD != NULL);
+
+ // Weird, non-user functions were already weeded out in RequestReJIT(), and will
+ // also never be passed to us by the prestub worker (for the pre-rejit case).
+ _ASSERTE(pMD->IsIL());
+
+ // Any MethodDescs that could be collected are not currently supported. Although we
+ // rule out all Ref.Emit modules in RequestReJIT(), there can still exist types defined
+ // in a non-reflection module and instantiated into a collectible assembly
+ // (e.g., List<MyCollectibleStruct>). In the future we may lift this
+ // restriction by updating the ReJitManager when the collectible assemblies
+ // owning the instantiations get collected.
+ if (pMD->GetLoaderAllocator()->IsCollectible())
+ {
+ return CORPROF_E_FUNCTION_IS_COLLECTIBLE;
+ }
+
+ return S_OK;
+}
+#endif
+
+//---------------------------------------------------------------------------------------
+//
+// Helper that inits a new CodePublishError and adds it to the pErrors array
+//
+// Arguments:
+// * pModule - The module in the module/MethodDef identifier pair for the method which
+// had an error during rejit
+// * methodDef - The MethodDef in the module/MethodDef identifier pair for the method which
+// had an error during rejit
+// * pMD - If available, the specific method instance which had an error during rejit
+// * hrStatus - HRESULT for the rejit error that occurred
+// * pErrors - the list of error records that this method will append to
+//
+// Return Value:
+// * S_OK: error was appended
+// * E_OUTOFMEMORY: Not enough memory to create the new error item. The array is unchanged.
+//
+
+//static
+#ifndef DACCESS_COMPILE
+HRESULT CodeVersionManager::AddCodePublishError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus, CDynArray<CodePublishError> * pErrors)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (pErrors == NULL)
+ {
+ return S_OK;
+ }
+
+ CodePublishError* pError = pErrors->Append();
+ if (pError == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ pError->pModule = pModule;
+ pError->methodDef = methodDef;
+ pError->pMethodDesc = pMD;
+ pError->hrStatus = hrStatus;
+ return S_OK;
+}
+#endif
+
+#ifndef DACCESS_COMPILE
+void CodeVersionManager::ReportCodePublishError(CodePublishError* pErrorRecord)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ CAN_TAKE_LOCK;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ ReportCodePublishError(pErrorRecord->pModule, pErrorRecord->methodDef, pErrorRecord->pMethodDesc, pErrorRecord->hrStatus);
+}
+
+void CodeVersionManager::ReportCodePublishError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ CAN_TAKE_LOCK;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_REJIT
+ BOOL isRejitted = FALSE;
+ {
+ TableLockHolder(this);
+ isRejitted = !GetActiveILCodeVersion(pModule, methodDef).IsDefaultVersion();
+ }
+
+ // this isn't perfect, we might be activating a tiered jitting variation of a rejitted
+ // method for example. If it proves to be an issue we can revisit.
+ if (isRejitted)
+ {
+ ReJitManager::ReportReJITError(pModule, methodDef, pMD, hrStatus);
+ }
+#endif
+}
+#endif // DACCESS_COMPILE
+
+//---------------------------------------------------------------------------------------
+//
+// PrepareCodeConfig::SetNativeCode() calls this to determine if there's a non-default code
+// version requested for a MethodDesc that has just been jitted for the first time.
+// This is also called when methods are being restored in NGEN images. The sequence looks like:
+// *Enter holder
+// Enter code version manager lock
+// DoJumpStampIfNecessary
+// *Runtime code publishes/restores method
+// *Exit holder
+// Leave code version manager lock
+// Send rejit error callbacks if needed
+//
+//
+// #PublishCode:
+// Note that the runtime needs to publish/restore the PCODE while this holder is
+// on the stack, so it can happen under the code version manager's lock.
+// This prevents a race with a profiler that calls
+// RequestReJIT just as the method finishes compiling. In particular, the locking ensures
+// atomicity between this set of steps (performed in DoJumpStampIfNecessary):
+// * (1) Checking whether there is a non-default version for this MD
+// * (2) If not, skip doing the jmp-stamp
+// * (3) Publishing the PCODE
+//
+// with respect to these steps performed in RequestReJIT:
+// * (a) Is PCODE published yet?
+// * (b) Create non-default ILCodeVersion which the prestub will
+// consult when it JITs the original IL
+//
+// Without this atomicity, we could get the ordering (1), (2), (a), (b), (3), resulting
+// in the rejit request getting completely ignored (i.e., we file away the new ILCodeVersion
+// AFTER the prestub checks for it).
+//
+// A similar race is possible for code being restored. In that case the restoring thread
+// does:
+// * (1) Check if there is a non-default ILCodeVersion for this MD
+// * (2) If not, no need to jmp-stamp
+// * (3) Restore the MD
+
+// And RequestRejit does:
+// * (a) [In LoadedMethodDescIterator] Is a potential MD restored yet?
+// * (b) [In EnumerateDomainClosedMethodDescs] If not, don't queue it for jump-stamping
+//
+// Same ordering (1), (2), (a), (b), (3) results in missing both opportunities to jump
+// stamp.
+
+#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+PublishMethodHolder::PublishMethodHolder(MethodDesc* pMethodDesc, PCODE pCode) :
+ m_pMD(NULL), m_hr(S_OK)
+{
+ // This method can't have a contract because entering the table lock
+ // below increments GCNoTrigger count. Contracts always revert these changes
+ // at the end of the method but we need the incremented count to flow out of the
+ // method. The balancing decrement occurs in the destructor.
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CAN_TAKE_LOCK;
+ STATIC_CONTRACT_MODE_ANY;
+
+ // We come here from the PreStub and from MethodDesc::CheckRestore
+ // The method should be effectively restored, but we haven't yet
+ // cleared the unrestored bit so we can't assert pMethodDesc->IsRestored()
+ // We can assert:
+ _ASSERTE(pMethodDesc->GetMethodTable()->IsRestored());
+
+ if (pCode != NULL)
+ {
+ m_pMD = pMethodDesc;
+ CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
+ pCodeVersionManager->EnterLock();
+ m_hr = pCodeVersionManager->DoJumpStampIfNecessary(pMethodDesc, pCode);
+ }
+}
+
+
+PublishMethodHolder::~PublishMethodHolder()
+{
+ // This method can't have a contract because leaving the table lock
+ // below decrements GCNoTrigger count. Contracts always revert these changes
+ // at the end of the method but we need the decremented count to flow out of the
+ // method. The balancing increment occurred in the constructor.
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS; // NOTRIGGER until we leave the lock
+ STATIC_CONTRACT_CAN_TAKE_LOCK;
+ STATIC_CONTRACT_MODE_ANY;
+
+ if (m_pMD)
+ {
+ CodeVersionManager* pCodeVersionManager = m_pMD->GetCodeVersionManager();
+ pCodeVersionManager->LeaveLock();
+ if (FAILED(m_hr))
+ {
+ pCodeVersionManager->ReportCodePublishError(m_pMD->GetModule(), m_pMD->GetMemberDef(), m_pMD, m_hr);
+ }
+ }
+}
+
+PublishMethodTableHolder::PublishMethodTableHolder(MethodTable* pMethodTable) :
+ m_pMethodTable(NULL)
+{
+ // This method can't have a contract because entering the table lock
+ // below increments GCNoTrigger count. Contracts always revert these changes
+ // at the end of the method but we need the incremented count to flow out of the
+ // method. The balancing decrement occurs in the destructor.
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CAN_TAKE_LOCK;
+ STATIC_CONTRACT_MODE_ANY;
+
+ // We come here from MethodTable::SetIsRestored
+ // The method table should be effectively restored, but we haven't yet
+ // cleared the unrestored bit so we can't assert pMethodTable->IsRestored()
+
+ m_pMethodTable = pMethodTable;
+ CodeVersionManager* pCodeVersionManager = pMethodTable->GetModule()->GetCodeVersionManager();
+ pCodeVersionManager->EnterLock();
+ MethodTable::IntroducedMethodIterator itMethods(pMethodTable, FALSE);
+ for (; itMethods.IsValid(); itMethods.Next())
+ {
+ // Although the MethodTable is restored, the methods might not be.
+ // We need to be careful to only query portions of the MethodDesc
+ // that work in a partially restored state. The only methods that need
+ // further restoration are IL stubs (which aren't rejittable) and
+ // generic methods. The only generic methods directly accesible from
+ // the MethodTable are definitions. GetNativeCode() on generic defs
+ // will run succesfully and return NULL which short circuits the
+ // rest of the logic.
+ MethodDesc * pMD = itMethods.GetMethodDesc();
+ PCODE pCode = pMD->GetNativeCode();
+ if (pCode != NULL)
+ {
+ HRESULT hr = pCodeVersionManager->DoJumpStampIfNecessary(pMD, pCode);
+ if (FAILED(hr))
+ {
+ CodeVersionManager::AddCodePublishError(pMD->GetModule(), pMD->GetMemberDef(), pMD, hr, &m_errors);
+ }
+ }
+ }
+}
+
+
+PublishMethodTableHolder::~PublishMethodTableHolder()
+{
+ // This method can't have a contract because leaving the table lock
+ // below decrements GCNoTrigger count. Contracts always revert these changes
+ // at the end of the method but we need the decremented count to flow out of the
+ // method. The balancing increment occurred in the constructor.
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS; // NOTRIGGER until we leave the lock
+ STATIC_CONTRACT_CAN_TAKE_LOCK;
+ STATIC_CONTRACT_MODE_ANY;
+
+ if (m_pMethodTable)
+ {
+ CodeVersionManager* pCodeVersionManager = m_pMethodTable->GetModule()->GetCodeVersionManager();
+ pCodeVersionManager->LeaveLock();
+ for (int i = 0; i < m_errors.Count(); i++)
+ {
+ pCodeVersionManager->ReportCodePublishError(&(m_errors[i]));
+ }
+ }
+}
+#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+
+#endif // FEATURE_CODE_VERSIONING
+
diff --git a/src/vm/codeversion.h b/src/vm/codeversion.h
new file mode 100644
index 0000000000..768c9cdb55
--- /dev/null
+++ b/src/vm/codeversion.h
@@ -0,0 +1,689 @@
+// 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.
+// ===========================================================================
+// File: CodeVersion.h
+//
+// ===========================================================================
+
+
+#ifndef CODE_VERSION_H
+#define CODE_VERSION_H
+
+class NativeCodeVersion;
+class ILCodeVersion;
+typedef DWORD NativeCodeVersionId;
+
+#ifdef FEATURE_CODE_VERSIONING
+class NativeCodeVersionNode;
+typedef DPTR(class NativeCodeVersionNode) PTR_NativeCodeVersionNode;
+class NativeCodeVersionCollection;
+class NativeCodeVersionIterator;
+class ILCodeVersionNode;
+typedef DPTR(class ILCodeVersionNode) PTR_ILCodeVersionNode;
+class ILCodeVersionCollection;
+class ILCodeVersionIterator;
+class MethodDescVersioningState;
+typedef DPTR(class MethodDescVersioningState) PTR_MethodDescVersioningState;
+
+class ILCodeVersioningState;
+typedef DPTR(class ILCodeVersioningState) PTR_ILCodeVersioningState;
+class CodeVersionManager;
+typedef DPTR(class CodeVersionManager) PTR_CodeVersionManager;
+
+// This HRESULT is only used as a private implementation detail. Corerror.xml has a comment in it
+// reserving this value for our use but it doesn't appear in the public headers.
+#define CORPROF_E_RUNTIME_SUSPEND_REQUIRED 0x80131381
+
+#endif
+
+
+
+
+class NativeCodeVersion
+{
+#ifdef FEATURE_CODE_VERSIONING
+ friend class MethodDescVersioningState;
+ friend class ILCodeVersion;
+#endif
+
+public:
+ NativeCodeVersion();
+ NativeCodeVersion(const NativeCodeVersion & rhs);
+#ifdef FEATURE_CODE_VERSIONING
+ NativeCodeVersion(PTR_NativeCodeVersionNode pVersionNode);
+#endif
+ NativeCodeVersion(PTR_MethodDesc pMethod);
+ BOOL IsNull() const;
+ PTR_MethodDesc GetMethodDesc() const;
+ NativeCodeVersionId GetVersionId() const;
+ BOOL IsDefaultVersion() const;
+ PCODE GetNativeCode() const;
+ ILCodeVersion GetILCodeVersion() const;
+ ReJITID GetILCodeVersionId() const;
+#ifndef DACCESS_COMPILE
+ BOOL SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected = NULL);
+#endif
+#ifdef FEATURE_TIERED_COMPILATION
+ enum OptimizationTier
+ {
+ OptimizationTier0,
+ OptimizationTier1
+ };
+ OptimizationTier GetOptimizationTier() const;
+#ifndef DACCESS_COMPILE
+ void SetOptimizationTier(OptimizationTier tier);
+#endif
+#endif // FEATURE_TIERED_COMPILATION
+ bool operator==(const NativeCodeVersion & rhs) const;
+ bool operator!=(const NativeCodeVersion & rhs) const;
+#if defined(DACCESS_COMPILE) && defined(FEATURE_CODE_VERSIONING)
+ // The DAC is privy to the backing node abstraction
+ PTR_NativeCodeVersionNode AsNode() const;
+#endif
+
+private:
+
+#ifndef FEATURE_CODE_VERSIONING
+ MethodDesc* m_pMethodDesc;
+#else // FEATURE_CODE_VERSIONING
+
+#ifndef DACCESS_COMPILE
+ NativeCodeVersionNode* AsNode() const;
+ NativeCodeVersionNode* AsNode();
+ void SetActiveChildFlag(BOOL isActive);
+ MethodDescVersioningState* GetMethodDescVersioningState();
+#endif
+
+ BOOL IsActiveChildVersion() const;
+ PTR_MethodDescVersioningState GetMethodDescVersioningState() const;
+
+ enum StorageKind
+ {
+ Unknown,
+ Explicit,
+ Synthetic
+ };
+
+ StorageKind m_storageKind;
+ union
+ {
+ PTR_NativeCodeVersionNode m_pVersionNode;
+ struct SyntheticStorage
+ {
+ PTR_MethodDesc m_pMethodDesc;
+ } m_synthetic;
+ };
+#endif // FEATURE_CODE_VERSIONING
+};
+
+
+
+#ifdef FEATURE_CODE_VERSIONING
+
+
+
+class ILCodeVersion
+{
+ friend class NativeCodeVersionIterator;
+
+public:
+ ILCodeVersion();
+ ILCodeVersion(const ILCodeVersion & ilCodeVersion);
+ ILCodeVersion(PTR_ILCodeVersionNode pILCodeVersionNode);
+ ILCodeVersion(PTR_Module pModule, mdMethodDef methodDef);
+
+ bool operator==(const ILCodeVersion & rhs) const;
+ bool operator!=(const ILCodeVersion & rhs) const;
+ BOOL IsNull() const;
+ BOOL IsDefaultVersion() const;
+ PTR_Module GetModule() const;
+ mdMethodDef GetMethodDef() const;
+ ReJITID GetVersionId() const;
+ NativeCodeVersionCollection GetNativeCodeVersions(PTR_MethodDesc pClosedMethodDesc) const;
+ NativeCodeVersion GetActiveNativeCodeVersion(PTR_MethodDesc pClosedMethodDesc) const;
+ PTR_COR_ILMETHOD GetIL() const;
+ PTR_COR_ILMETHOD GetILNoThrow() const;
+ DWORD GetJitFlags() const;
+ const InstrumentedILOffsetMapping* GetInstrumentedILMap() const;
+
+#ifndef DACCESS_COMPILE
+ void SetIL(COR_ILMETHOD* pIL);
+ void SetJitFlags(DWORD flags);
+ void SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap);
+ HRESULT AddNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion);
+ HRESULT GetOrCreateActiveNativeCodeVersion(MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion);
+ HRESULT SetActiveNativeCodeVersion(NativeCodeVersion activeNativeCodeVersion, BOOL fEESuspended);
+#endif //DACCESS_COMPILE
+
+ enum RejitFlags
+ {
+ // The profiler has requested a ReJit, so we've allocated stuff, but we haven't
+ // called back to the profiler to get any info or indicate that the ReJit has
+ // started. (This Info can be 'reused' for a new ReJit if the
+ // profiler calls RequestRejit again before we transition to the next state.)
+ kStateRequested = 0x00000000,
+
+ // The CLR has initiated the call to the profiler's GetReJITParameters() callback
+ // but it hasn't completed yet. At this point we have to assume the profiler has
+ // commited to a specific IL body, even if the CLR doesn't know what it is yet.
+ // If the profiler calls RequestRejit we need to allocate a new ILCodeVersion
+ // and call GetReJITParameters() again.
+ kStateGettingReJITParameters = 0x00000001,
+
+ // We have asked the profiler about this method via ICorProfilerFunctionControl,
+ // and have thus stored the IL and codegen flags the profiler specified.
+ kStateActive = 0x00000002,
+
+ kStateMask = 0x0000000F,
+ };
+
+ RejitFlags GetRejitState() const;
+#ifndef DACCESS_COMPILE
+ void SetRejitState(RejitFlags newState);
+#endif
+
+#ifdef DACCESS_COMPILE
+ // The DAC is privy to the backing node abstraction
+ PTR_ILCodeVersionNode AsNode() const;
+#endif
+
+private:
+
+#ifndef DACCESS_COMPILE
+ PTR_ILCodeVersionNode AsNode();
+ PTR_ILCodeVersionNode AsNode() const;
+#endif
+
+ enum StorageKind
+ {
+ Unknown,
+ Explicit,
+ Synthetic
+ };
+
+ StorageKind m_storageKind;
+ union
+ {
+ PTR_ILCodeVersionNode m_pVersionNode;
+ struct SyntheticStorage
+ {
+ PTR_Module m_pModule;
+ mdMethodDef m_methodDef;
+ } m_synthetic;
+ };
+};
+
+
+class NativeCodeVersionNode
+{
+ friend NativeCodeVersionIterator;
+ friend MethodDescVersioningState;
+ friend ILCodeVersionNode;
+public:
+#ifndef DACCESS_COMPILE
+ NativeCodeVersionNode(NativeCodeVersionId id, MethodDesc* pMethod, ReJITID parentId);
+#endif
+#ifdef DEBUG
+ BOOL LockOwnedByCurrentThread() const;
+#endif
+ PTR_MethodDesc GetMethodDesc() const;
+ NativeCodeVersionId GetVersionId() const;
+ PCODE GetNativeCode() const;
+ ReJITID GetILVersionId() const;
+ ILCodeVersion GetILCodeVersion() const;
+ BOOL IsActiveChildVersion() const;
+#ifndef DACCESS_COMPILE
+ BOOL SetNativeCodeInterlocked(PCODE pCode, PCODE pExpected);
+ void SetActiveChildFlag(BOOL isActive);
+#endif
+#ifdef FEATURE_TIERED_COMPILATION
+ NativeCodeVersion::OptimizationTier GetOptimizationTier() const;
+#ifndef DACCESS_COMPILE
+ void SetOptimizationTier(NativeCodeVersion::OptimizationTier tier);
+#endif
+#endif
+
+private:
+ //union - could save a little memory?
+ //{
+ PCODE m_pNativeCode;
+ PTR_MethodDesc m_pMethodDesc;
+ //};
+
+ ReJITID m_parentId;
+ PTR_NativeCodeVersionNode m_pNextMethodDescSibling;
+ NativeCodeVersionId m_id;
+#ifdef FEATURE_TIERED_COMPILATION
+ Volatile<NativeCodeVersion::OptimizationTier> m_optTier;
+#endif
+
+ enum NativeCodeVersionNodeFlags
+ {
+ IsActiveChildFlag = 1
+ };
+ DWORD m_flags;
+};
+
+class NativeCodeVersionCollection
+{
+ friend class NativeCodeVersionIterator;
+public:
+ NativeCodeVersionCollection(PTR_MethodDesc pMethodDescFilter, ILCodeVersion ilCodeFilter);
+ NativeCodeVersionIterator Begin();
+ NativeCodeVersionIterator End();
+
+private:
+ PTR_MethodDesc m_pMethodDescFilter;
+ ILCodeVersion m_ilCodeFilter;
+};
+
+class NativeCodeVersionIterator : public Enumerator<const NativeCodeVersion, NativeCodeVersionIterator>
+{
+ friend class Enumerator<const NativeCodeVersion, NativeCodeVersionIterator>;
+
+public:
+ NativeCodeVersionIterator(NativeCodeVersionCollection* pCollection);
+ CHECK Check() const { CHECK_OK; }
+
+protected:
+ const NativeCodeVersion & Get() const;
+ void First();
+ void Next();
+ bool Equal(const NativeCodeVersionIterator &i) const;
+
+ CHECK DoCheck() const { CHECK_OK; }
+
+private:
+ enum IterationStage
+ {
+ Initial,
+ ImplicitCodeVersion,
+ LinkedList,
+ End
+ };
+ IterationStage m_stage;
+ NativeCodeVersionCollection* m_pCollection;
+ PTR_NativeCodeVersionNode m_pLinkedListCur;
+ NativeCodeVersion m_cur;
+};
+
+class ILCodeVersionNode
+{
+public:
+ ILCodeVersionNode();
+#ifndef DACCESS_COMPILE
+ ILCodeVersionNode(Module* pModule, mdMethodDef methodDef, ReJITID id);
+#endif
+#ifdef DEBUG
+ BOOL LockOwnedByCurrentThread() const;
+#endif //DEBUG
+ PTR_Module GetModule() const;
+ mdMethodDef GetMethodDef() const;
+ ReJITID GetVersionId() const;
+ PTR_COR_ILMETHOD GetIL() const;
+ DWORD GetJitFlags() const;
+ const InstrumentedILOffsetMapping* GetInstrumentedILMap() const;
+ ILCodeVersion::RejitFlags GetRejitState() const;
+ PTR_ILCodeVersionNode GetNextILVersionNode() const;
+#ifndef DACCESS_COMPILE
+ void SetIL(COR_ILMETHOD* pIL);
+ void SetJitFlags(DWORD flags);
+ void SetInstrumentedILMap(SIZE_T cMap, COR_IL_MAP * rgMap);
+ void SetRejitState(ILCodeVersion::RejitFlags newState);
+ void SetNextILVersionNode(ILCodeVersionNode* pNextVersionNode);
+#endif
+
+private:
+ PTR_Module m_pModule;
+ mdMethodDef m_methodDef;
+ ReJITID m_rejitId;
+ PTR_ILCodeVersionNode m_pNextILVersionNode;
+ Volatile<ILCodeVersion::RejitFlags> m_rejitState;
+ VolatilePtr<COR_ILMETHOD> m_pIL;
+ Volatile<DWORD> m_jitFlags;
+ InstrumentedILOffsetMapping m_instrumentedILMap;
+};
+
+class ILCodeVersionCollection
+{
+ friend class ILCodeVersionIterator;
+
+public:
+ ILCodeVersionCollection(PTR_Module pModule, mdMethodDef methodDef);
+ ILCodeVersionIterator Begin();
+ ILCodeVersionIterator End();
+
+private:
+ PTR_Module m_pModule;
+ mdMethodDef m_methodDef;
+};
+
+class ILCodeVersionIterator : public Enumerator<const ILCodeVersion, ILCodeVersionIterator>
+{
+ friend class Enumerator<const ILCodeVersion, ILCodeVersionIterator>;
+
+public:
+ ILCodeVersionIterator();
+ ILCodeVersionIterator(const ILCodeVersionIterator & iter);
+ ILCodeVersionIterator(ILCodeVersionCollection* pCollection);
+ CHECK Check() const { CHECK_OK; }
+
+protected:
+ const ILCodeVersion & Get() const;
+ void First();
+ void Next();
+ bool Equal(const ILCodeVersionIterator &i) const;
+
+ CHECK DoCheck() const { CHECK_OK; }
+
+private:
+ enum IterationStage
+ {
+ Initial,
+ ImplicitCodeVersion,
+ LinkedList,
+ End
+ };
+ IterationStage m_stage;
+ ILCodeVersion m_cur;
+ PTR_ILCodeVersionNode m_pLinkedListCur;
+ ILCodeVersionCollection* m_pCollection;
+};
+
+class MethodDescVersioningState
+{
+public:
+ // The size of the code used to jump stamp the prolog
+#ifdef FEATURE_JUMPSTAMP
+ static const size_t JumpStubSize =
+#if defined(_X86_) || defined(_AMD64_)
+ 5;
+#else
+#error "Need to define size of jump-stamp for this platform"
+#endif
+#endif // FEATURE_JUMPSTAMP
+
+ MethodDescVersioningState(PTR_MethodDesc pMethodDesc);
+ PTR_MethodDesc GetMethodDesc() const;
+ NativeCodeVersionId AllocateVersionId();
+ PTR_NativeCodeVersionNode GetFirstVersionNode() const;
+
+#ifndef DACCESS_COMPILE
+#ifdef FEATURE_JUMPSTAMP
+ HRESULT SyncJumpStamp(NativeCodeVersion nativeCodeVersion, BOOL fEESuspended);
+ HRESULT UpdateJumpTarget(BOOL fEESuspended, PCODE pRejittedCode);
+ HRESULT UndoJumpStampNativeCode(BOOL fEESuspended);
+ HRESULT JumpStampNativeCode(PCODE pCode = NULL);
+#endif // FEATURE_JUMPSTAMP
+ void LinkNativeCodeVersionNode(NativeCodeVersionNode* pNativeCodeVersionNode);
+#endif // DACCESS_COMPILE
+
+#ifdef FEATURE_JUMPSTAMP
+ enum JumpStampFlags
+ {
+ // There is no jump stamp in place on this method (Either because
+ // there is no code at all, or there is code that hasn't been
+ // overwritten with a jump)
+ JumpStampNone = 0x0,
+
+ // The method code has the jump stamp written in, and it points to the Prestub
+ JumpStampToPrestub = 0x1,
+
+ // The method code has the jump stamp written in, and it points to the currently
+ // active code version
+ JumpStampToActiveVersion = 0x2,
+ };
+
+ JumpStampFlags GetJumpStampState();
+ void SetJumpStampState(JumpStampFlags newState);
+#endif // FEATURE_JUMPSTAMP
+
+ //read-write data for the default native code version
+ BOOL IsDefaultVersionActiveChild() const;
+#ifndef DACCESS_COMPILE
+ void SetDefaultVersionActiveChildFlag(BOOL isActive);
+#endif
+
+private:
+#if !defined(DACCESS_COMPILE) && defined(FEATURE_JUMPSTAMP)
+ INDEBUG(BOOL CodeIsSaved();)
+ HRESULT UpdateJumpStampHelper(BYTE* pbCode, INT64 i64OldValue, INT64 i64NewValue, BOOL fContentionPossible);
+#endif
+ PTR_MethodDesc m_pMethodDesc;
+
+ enum MethodDescVersioningStateFlags
+ {
+ JumpStampMask = 0x3,
+ IsDefaultVersionActiveChildFlag = 0x4
+ };
+ BYTE m_flags;
+ NativeCodeVersionId m_nextId;
+ PTR_NativeCodeVersionNode m_pFirstVersionNode;
+
+
+ // The originally JITted code that was overwritten with the jmp stamp.
+#ifdef FEATURE_JUMPSTAMP
+ BYTE m_rgSavedCode[JumpStubSize];
+#endif
+};
+
+class MethodDescVersioningStateHashTraits : public NoRemoveSHashTraits<DefaultSHashTraits<PTR_MethodDescVersioningState>>
+{
+public:
+ typedef typename DefaultSHashTraits<PTR_MethodDescVersioningState>::element_t element_t;
+ typedef typename DefaultSHashTraits<PTR_MethodDescVersioningState>::count_t count_t;
+
+ typedef const PTR_MethodDesc key_t;
+
+ static key_t GetKey(element_t e)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return e->GetMethodDesc();
+ }
+ static BOOL Equals(key_t k1, key_t k2)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return k1 == k2;
+ }
+ static count_t Hash(key_t k)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (count_t)(size_t)dac_cast<TADDR>(k);
+ }
+
+ static const element_t Null() { LIMITED_METHOD_CONTRACT; return dac_cast<PTR_MethodDescVersioningState>(nullptr); }
+ static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e == NULL; }
+};
+
+typedef SHash<MethodDescVersioningStateHashTraits> MethodDescVersioningStateHash;
+
+class ILCodeVersioningState
+{
+public:
+ ILCodeVersioningState(PTR_Module pModule, mdMethodDef methodDef);
+ ILCodeVersion GetActiveVersion() const;
+ PTR_ILCodeVersionNode GetFirstVersionNode() const;
+#ifndef DACCESS_COMPILE
+ void SetActiveVersion(ILCodeVersion ilActiveCodeVersion);
+ void LinkILCodeVersionNode(ILCodeVersionNode* pILCodeVersionNode);
+#endif
+
+ struct Key
+ {
+ public:
+ Key();
+ Key(PTR_Module pModule, mdMethodDef methodDef);
+ size_t Hash() const;
+ bool operator==(const Key & rhs) const;
+ private:
+ PTR_Module m_pModule;
+ mdMethodDef m_methodDef;
+ };
+
+ Key GetKey() const;
+
+private:
+ ILCodeVersion m_activeVersion;
+ PTR_ILCodeVersionNode m_pFirstVersionNode;
+ PTR_Module m_pModule;
+ mdMethodDef m_methodDef;
+};
+
+class ILCodeVersioningStateHashTraits : public NoRemoveSHashTraits<DefaultSHashTraits<PTR_ILCodeVersioningState>>
+{
+public:
+ typedef typename DefaultSHashTraits<PTR_ILCodeVersioningState>::element_t element_t;
+ typedef typename DefaultSHashTraits<PTR_ILCodeVersioningState>::count_t count_t;
+
+ typedef const ILCodeVersioningState::Key key_t;
+
+ static key_t GetKey(element_t e)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return e->GetKey();
+ }
+ static BOOL Equals(key_t k1, key_t k2)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return k1 == k2;
+ }
+ static count_t Hash(key_t k)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (count_t)k.Hash();
+ }
+
+ static const element_t Null() { LIMITED_METHOD_CONTRACT; return dac_cast<PTR_ILCodeVersioningState>(nullptr); }
+ static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e == NULL; }
+};
+
+typedef SHash<ILCodeVersioningStateHashTraits> ILCodeVersioningStateHash;
+
+
+class CodeVersionManager
+{
+ friend class ILCodeVersion;
+ friend class PublishMethodHolder;
+ friend class PublishMethodTableHolder;
+
+public:
+ CodeVersionManager();
+
+ void PreInit(BOOL fSharedDomain);
+
+ class TableLockHolder : public CrstHolder
+ {
+ public:
+ TableLockHolder(CodeVersionManager * pCodeVersionManager);
+ };
+ //Using the holder is preferable, but in some cases the holder can't be used
+#ifndef DACCESS_COMPILE
+ void EnterLock();
+ void LeaveLock();
+#endif
+
+#ifdef DEBUG
+ BOOL LockOwnedByCurrentThread() const;
+#endif
+
+ DWORD GetNonDefaultILVersionCount();
+ ILCodeVersionCollection GetILCodeVersions(PTR_MethodDesc pMethod);
+ ILCodeVersionCollection GetILCodeVersions(PTR_Module pModule, mdMethodDef methodDef);
+ ILCodeVersion GetActiveILCodeVersion(PTR_MethodDesc pMethod);
+ ILCodeVersion GetActiveILCodeVersion(PTR_Module pModule, mdMethodDef methodDef);
+ ILCodeVersion GetILCodeVersion(PTR_MethodDesc pMethod, ReJITID rejitId);
+ NativeCodeVersionCollection GetNativeCodeVersions(PTR_MethodDesc pMethod) const;
+ NativeCodeVersion GetNativeCodeVersion(PTR_MethodDesc pMethod, PCODE codeStartAddress) const;
+ PTR_ILCodeVersioningState GetILCodeVersioningState(PTR_Module pModule, mdMethodDef methodDef) const;
+ PTR_MethodDescVersioningState GetMethodDescVersioningState(PTR_MethodDesc pMethod) const;
+
+#ifndef DACCESS_COMPILE
+ struct CodePublishError
+ {
+ Module* pModule;
+ mdMethodDef methodDef;
+ MethodDesc* pMethodDesc;
+ HRESULT hrStatus;
+ };
+
+ HRESULT AddILCodeVersion(Module* pModule, mdMethodDef methodDef, ReJITID rejitId, ILCodeVersion* pILCodeVersion);
+ HRESULT AddNativeCodeVersion(ILCodeVersion ilCodeVersion, MethodDesc* pClosedMethodDesc, NativeCodeVersion* pNativeCodeVersion);
+ HRESULT DoJumpStampIfNecessary(MethodDesc* pMD, PCODE pCode);
+ PCODE PublishVersionableCodeIfNecessary(MethodDesc* pMethodDesc, BOOL fCanBackpatchPrestub);
+ HRESULT PublishNativeCodeVersion(MethodDesc* pMethodDesc, NativeCodeVersion nativeCodeVersion, BOOL fEESuspended);
+ HRESULT GetOrCreateMethodDescVersioningState(MethodDesc* pMethod, MethodDescVersioningState** ppMethodDescVersioningState);
+ HRESULT GetOrCreateILCodeVersioningState(Module* pModule, mdMethodDef methodDef, ILCodeVersioningState** ppILCodeVersioningState);
+ HRESULT SetActiveILCodeVersions(ILCodeVersion* pActiveVersions, DWORD cActiveVersions, BOOL fEESuspended, CDynArray<CodePublishError> * pPublishErrors);
+ static HRESULT AddCodePublishError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus, CDynArray<CodePublishError> * pErrors);
+ static HRESULT AddCodePublishError(NativeCodeVersion nativeCodeVersion, HRESULT hrStatus, CDynArray<CodePublishError> * pErrors);
+ static void OnAppDomainExit(AppDomain* pAppDomain);
+#endif
+
+private:
+
+#ifndef DACCESS_COMPILE
+ static HRESULT EnumerateClosedMethodDescs(MethodDesc* pMD, CDynArray<MethodDesc*> * pClosedMethodDescs, CDynArray<CodePublishError> * pUnsupportedMethodErrors);
+ static HRESULT EnumerateDomainClosedMethodDescs(
+ AppDomain * pAppDomainToSearch,
+ Module* pModuleContainingMethodDef,
+ mdMethodDef methodDef,
+ CDynArray<MethodDesc*> * pClosedMethodDescs,
+ CDynArray<CodePublishError> * pUnsupportedMethodErrors);
+ static HRESULT GetNonVersionableError(MethodDesc* pMD);
+ void ReportCodePublishError(CodePublishError* pErrorRecord);
+ void ReportCodePublishError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus);
+#endif
+
+ //Module,MethodDef -> ILCodeVersioningState
+ ILCodeVersioningStateHash m_ilCodeVersioningStateMap;
+
+ //closed MethodDesc -> MethodDescVersioningState
+ MethodDescVersioningStateHash m_methodDescVersioningStateMap;
+
+ CrstExplicitInit m_crstTable;
+};
+
+#endif // FEATURE_CODE_VERSIONING
+
+//
+// These holders are used by runtime code that is making new code
+// available for execution, either by publishing jitted code
+// or restoring NGEN code. It ensures the publishing is synchronized
+// with rejit requests
+//
+class PublishMethodHolder
+{
+public:
+#if !defined(FEATURE_CODE_VERSIONING) || defined(DACCESS_COMPILE) || defined(CROSSGEN_COMPILE)
+ PublishMethodHolder(MethodDesc* pMethod, PCODE pCode) { }
+#else
+ PublishMethodHolder(MethodDesc* pMethod, PCODE pCode);
+ ~PublishMethodHolder();
+#endif
+
+private:
+#if defined(FEATURE_CODE_VERSIONING)
+ MethodDesc * m_pMD;
+ HRESULT m_hr;
+#endif
+};
+
+class PublishMethodTableHolder
+{
+public:
+#if !defined(FEATURE_CODE_VERSIONING) || defined(DACCESS_COMPILE) || defined(CROSSGEN_COMPILE)
+ PublishMethodTableHolder(MethodTable* pMethodTable) { }
+#else
+ PublishMethodTableHolder(MethodTable* pMethodTable);
+ ~PublishMethodTableHolder();
+#endif
+
+private:
+#if defined(FEATURE_CODE_VERSIONING) && !defined(DACCESS_COMPILE)
+ MethodTable* m_pMethodTable;
+ CDynArray<CodeVersionManager::CodePublishError> m_errors;
+#endif
+};
+
+#endif // CODE_VERSION_H
diff --git a/src/vm/comcallablewrapper.cpp b/src/vm/comcallablewrapper.cpp
index fdb0e54a45..322bcdc268 100644
--- a/src/vm/comcallablewrapper.cpp
+++ b/src/vm/comcallablewrapper.cpp
@@ -37,7 +37,6 @@
#include "dispex.h"
#include "perfcounters.h"
#include "guidfromname.h"
-#include "security.h"
#include "comconnectionpoints.h"
#include <objsafe.h> // IID_IObjctSafe
#include "virtualcallstub.h"
@@ -3274,9 +3273,7 @@ inline IUnknown * ComCallWrapper::GetComIPFromCCW_VisibilityCheck(
}
CONTRACT_END;
- // Ensure that the interface we are passing out was defined in trusted code.
- if ((!(flags & GetComIPFromCCW::SuppressSecurityCheck) && pIntfComMT->IsDefinedInUntrustedCode()) ||
- // Do a visibility check if needed.
+ if (// Do a visibility check if needed.
((flags & GetComIPFromCCW::CheckVisibility) && (!pIntfComMT->IsComVisible())))
{
// If not, fail to return the interface.
@@ -3698,10 +3695,8 @@ IUnknown* ComCallWrapper::GetComIPFromCCW(ComCallWrapper *pWrap, REFIID riid, Me
ComMethodTable * pIntfComMT = ComMethodTable::ComMethodTableFromIP(pIntf);
// Manual inlining of GetComIPFromCCW_VisibilityCheck() for common case.
- if (// Ensure that the interface we are passing out was defined in trusted code.
- (!(flags & GetComIPFromCCW::SuppressSecurityCheck) && pIntfComMT->IsDefinedInUntrustedCode())
- // Do a visibility check if needed.
- || ((flags & GetComIPFromCCW::CheckVisibility) && (!pIntfComMT->IsComVisible())))
+ if (// Do a visibility check if needed.
+ ((flags & GetComIPFromCCW::CheckVisibility) && (!pIntfComMT->IsComVisible())))
{
// If not, fail to return the interface.
SafeRelease(pIntf);
@@ -5452,12 +5447,6 @@ ComMethodTable* ComCallWrapperTemplate::GetClassComMT()
MethodTable *pMT = m_thClass.GetMethodTable();
- // Preload the policy for these classes before we take the lock.
- for (MethodTable* pMethodTable = pMT; pMethodTable != NULL; pMethodTable = pMethodTable->GetParentMethodTable())
- {
- Security::CanCallUnmanagedCode(pMethodTable->GetModule());
- }
-
// We haven't set it up yet, generate one.
ComMethodTable* pClassComMT;
if (pMT->IsDelegate() && (pMT->IsProjectedFromWinRT() || WinRTTypeNameConverter::IsRedirectedType(pMT)))
@@ -5887,12 +5876,6 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForClass(MethodTable
if (IsTypeVisibleFromCom(TypeHandle(pComMT->m_pMT)))
pComMT->m_Flags |= enum_ComVisible;
- if (!Security::CanCallUnmanagedCode(pComMT->m_pMT->GetModule()))
- {
- pComMT->m_Flags |= enum_IsUntrusted;
- }
-
-
#if _DEBUG
{
// In debug set all the vtable slots to 0xDEADCA11.
@@ -5974,11 +5957,6 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForInterface(MethodT
if (pItfClass->GetClass()->IsComClassInterface())
pComMT->m_Flags |= enum_ComClassItf;
- if (!Security::CanCallUnmanagedCode(pComMT->m_pMT->GetModule()))
- {
- pComMT->m_Flags |= enum_IsUntrusted;
- }
-
#ifdef _DEBUG
{
// In debug set all the vtable slots to 0xDEADCA11.
@@ -6064,11 +6042,6 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForBasic(MethodTable
if (pMT->GetClass()->IsComClassInterface())
pComMT->m_Flags |= enum_ComClassItf;
- if (!Security::CanCallUnmanagedCode(pMT->GetModule()))
- {
- pComMT->m_Flags |= enum_IsUntrusted;
- }
-
#ifdef MDA_SUPPORTED
#ifdef _DEBUG
{
@@ -6150,11 +6123,6 @@ ComMethodTable* ComCallWrapperTemplate::CreateComMethodTableForDelegate(MethodTa
pComMT->m_Flags |= enum_GuidGenerated;
- if (!Security::CanCallUnmanagedCode(pComMT->m_pMT->GetModule()))
- {
- pComMT->m_Flags |= enum_IsUntrusted;
- }
-
#if _DEBUG
{
// In debug set all the vtable slots to 0xDEADCA11.
@@ -6287,11 +6255,6 @@ ComCallWrapperTemplate* ComCallWrapperTemplate::CreateTemplate(TypeHandle thClas
// Preload the policy for this interface
CCWInterfaceMapIterator it(thClass, pClsFact, true);
- while (it.Next())
- {
- Module *pModule = it.GetInterface()->GetModule();
- Security::CanCallUnmanagedCode(pModule);
- }
// Num interfaces in the template.
unsigned numInterfaces = it.GetCount();
diff --git a/src/vm/comcallablewrapper.h b/src/vm/comcallablewrapper.h
index 85647279f3..1a68135e77 100644
--- a/src/vm/comcallablewrapper.h
+++ b/src/vm/comcallablewrapper.h
@@ -572,7 +572,7 @@ enum Masks
enum_SigClassLoadChecked = 0x00000100,
enum_ComClassItf = 0x00000200,
enum_GuidGenerated = 0x00000400,
- enum_IsUntrusted = 0x00001000,
+ // enum_unused = 0x00001000,
enum_IsBasic = 0x00002000,
enum_IsWinRTDelegate = 0x00004000,
enum_IsWinRTTrivialAggregate = 0x00008000,
@@ -646,12 +646,6 @@ struct ComMethodTable
return (CorClassIfaceAttr)(m_Flags & enum_ClassInterfaceTypeMask);
}
- BOOL IsDefinedInUntrustedCode()
- {
- LIMITED_METHOD_CONTRACT;
- return (m_Flags & enum_IsUntrusted) ? TRUE : FALSE;
- }
-
BOOL IsIClassX()
{
LIMITED_METHOD_CONTRACT;
diff --git a/src/vm/comdelegate.cpp b/src/vm/comdelegate.cpp
index cee0d8c08a..961a758750 100644
--- a/src/vm/comdelegate.cpp
+++ b/src/vm/comdelegate.cpp
@@ -22,7 +22,6 @@
#include "mdaassistants.h"
#include "cgensys.h"
#include "asmconstants.h"
-#include "security.h"
#include "virtualcallstub.h"
#include "callingconvention.h"
#include "customattribute.h"
@@ -933,30 +932,6 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis,
pExactMethodType,
pTargetMethod->IsStatic() ? NULL : pInstanceMT,
pTargetMethod);
-
- // Ask for skip verification if a delegate over a .ctor or .cctor is requested.
- if (pTargetMethod->IsClassConstructorOrCtor())
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
-
-#ifdef FEATURE_COMINTEROP
- // Check if it's a COM object and if so, demand unmanaged code permission.
- // <TODO> I think we need a target check here. Investigate. </TODO>
- if (pTargetMethod && pTargetMethod->GetMethodTable()->IsComObjectType())
- Security::SpecialDemand(SSWT_DEMAND_FROM_NATIVE, SECURITY_UNMANAGED_CODE);
-#endif // FEATURE_COMINTEROP
-
- // Devdiv bug 296229: dangerous methods are those that make security decisions based on
- // the result of stack walks. When a delegate to such a method is invoked asynchronously
- // the stackwalker will stop at the remoting code and consider the caller unmanaged code.
- // Unmanaged code is allowed to bypass any security check.
- if (InvokeUtil::IsDangerousMethod(pTargetMethod))
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, REFLECTION_MEMBER_ACCESS);
-
- // Check whether the creator of the delegate lives in the same assembly as the target method. If not, and they aren't fully
- // trusted, we have to make this delegate a secure wrapper and allocate a new inner delegate to represent the real target.
- MethodDesc *pCreatorMethod = sCtx.GetCallerMethod();
- if (NeedsSecureDelegate(pCreatorMethod, sCtx.GetCallerDomain(), pTargetMethod))
- refRealDelegate = CreateSecureDelegate(*pRefThis, pCreatorMethod, pTargetMethod);
}
// If we didn't wrap the real delegate in a secure delegate then the real delegate is the one passed in.
@@ -1511,8 +1486,7 @@ OBJECTREF COMDelegate::ConvertToDelegate(LPVOID pCallback, MethodTable* pMT)
{
GCX_PREEMP();
- DWORD dwStubFlags = pMT->ClassRequiresUnmanagedCodeCheck() ? NDIRECTSTUB_FL_HASDECLARATIVESECURITY : 0;
- pMarshalStub = GetStubForInteropMethod(pMD, dwStubFlags, &(pClass->m_pForwardStubMD));
+ pMarshalStub = GetStubForInteropMethod(pMD, 0, &(pClass->m_pForwardStubMD));
// Save this new stub on the DelegateEEClass.
EnsureWritablePages(dac_cast<PVOID>(&pClass->m_pMarshalStub), sizeof(PCODE));
@@ -1633,9 +1607,6 @@ OBJECTREF COMDelegate::ConvertWinRTInterfaceToDelegate(IUnknown *pIdentity, Meth
DWORD dwStubFlags = NDIRECTSTUB_FL_COM | NDIRECTSTUB_FL_WINRT | NDIRECTSTUB_FL_WINRTDELEGATE;
- if (pMT->ClassRequiresUnmanagedCodeCheck())
- dwStubFlags |= NDIRECTSTUB_FL_HASDECLARATIVESECURITY;
-
pMarshalStub = GetStubForInteropMethod(pMD, dwStubFlags);
// At this point we must have a non-NULL ComPlusCallInfo
@@ -1737,9 +1708,6 @@ MethodDesc* COMDelegate::GetILStubMethodDesc(EEImplMethodDesc* pDelegateMD, DWOR
dwStubFlags |= NDIRECTSTUB_FL_DELEGATE;
}
- if (pMT->ClassRequiresUnmanagedCodeCheck())
- dwStubFlags |= NDIRECTSTUB_FL_HASDECLARATIVESECURITY;
-
PInvokeStaticSigInfo sigInfo(pDelegateMD);
return NDirect::CreateCLRToNativeILStub(&sigInfo, dwStubFlags, pDelegateMD);
}
@@ -1832,8 +1800,6 @@ FCIMPL3(PCODE, COMDelegate::AdjustTarget, Object* refThisUNSAFE, Object* targetU
#ifdef FEATURE_COMINTEROP
isComObject = pMTTarg->IsComObjectType();
- if (isComObject)
- DoUnmanagedCodeAccessCheck(pMeth);
#endif // FEATURE_COMINTEROP
if (!pMT->IsTransparentProxy())
@@ -1971,18 +1937,7 @@ FCIMPL3(void, COMDelegate::DelegateConstruct, Object* refThisUNSAFE, Object* tar
methodArgCount++; // count 'this'
}
- // do we need a secure delegate?
-
- // Devdiv bug 296229: dangerous methods are those that make security decisions based on
- // the result of stack walks. When a delegate to such a method is invoked asynchronously
- // the stackwalker will stop at the remoting code and consider the caller unmanaged code.
- // Unmanaged code is allowed to bypass any security check.
- if (InvokeUtil::IsDangerousMethod(pMeth))
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, REFLECTION_MEMBER_ACCESS);
-
- if (NeedsSecureDelegate(pCreatorMethod, GetAppDomain(), pMeth))
- gc.refThis = CreateSecureDelegate(gc.refThis, pCreatorMethod, pMeth);
- else if (NeedsWrapperDelegate(pMeth))
+ if (NeedsWrapperDelegate(pMeth))
gc.refThis = CreateSecureDelegate(gc.refThis, NULL, pMeth);
if (pMeth->GetLoaderAllocator()->IsCollectible())
@@ -2033,8 +1988,6 @@ FCIMPL3(void, COMDelegate::DelegateConstruct, Object* refThisUNSAFE, Object* tar
BOOL isComObject = false;
#ifdef FEATURE_COMINTEROP
isComObject = pMTTarg->IsComObjectType();
- if (isComObject)
- DoUnmanagedCodeAccessCheck(pMeth);
#endif // FEATURE_COMINTEROP
if (!pMTTarg->IsTransparentProxy())
@@ -2125,56 +2078,6 @@ FCIMPL3(void, COMDelegate::DelegateConstruct, Object* refThisUNSAFE, Object* tar
}
FCIMPLEND
-
-#ifdef FEATURE_COMINTEROP
-void COMDelegate::DoUnmanagedCodeAccessCheck(MethodDesc* pMeth)
-{
- // Skip if SuppressUnmanagedCodePermission is present
- if (pMeth->RequiresLinktimeCheck())
- {
- // Check whether this is actually a SuppressUnmanagedCodePermission attribute and
- // if so, don't do a demand
- {
- return;
- }
- }
-
- // If this method is defined directly on an interface, get that interface
- // Otherwise, from the class get the interface that this method is defined on.
- // Based on this interface, skip the check if the interface is DispatchOnly or
- // if the interface is defined in fully-trusted code.
- if (pMeth->IsComPlusCall())
- {
- ComPlusCallMethodDesc *pCMD = (ComPlusCallMethodDesc *)pMeth;
- MethodTable* pMTItf = (pCMD->m_pComPlusCallInfo == NULL ? NULL : pCMD->m_pComPlusCallInfo->m_pInterfaceMT);
-
- // If the interface methodtable is null, then the ComPlusCallMethodDesc hasn't been set up yet.
- if (pMTItf == NULL)
- {
- GCX_PREEMP();
- pMeth->DoPrestub(NULL);
- pMTItf = ((ComPlusCallMethodDesc*)pMeth)->m_pComPlusCallInfo->m_pInterfaceMT;
- }
- else
- {
- pMTItf->CheckRestore();
- }
-
- if (pMTItf->GetComInterfaceType() == ifDispatch)
- {
- return;
- }
- else if (Security::CanCallUnmanagedCode(pMTItf->GetModule()))
- {
- return;
- }
- }
-
- Security::SpecialDemand(SSWT_DEMAND_FROM_NATIVE, SECURITY_UNMANAGED_CODE);
-}
-#endif // FEATURE_COMINTEROP
-
-
MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate)
{
CONTRACTL
@@ -2463,20 +2366,6 @@ FCIMPLEND
#endif // CROSSGEN_COMPILE
-
-BOOL COMDelegate::NeedsSecureDelegate(MethodDesc* pCreatorMethod, AppDomain *pCreatorDomain, MethodDesc* pTargetMD)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- return FALSE;
-}
-
BOOL COMDelegate::NeedsWrapperDelegate(MethodDesc* pTargetMD)
{
LIMITED_METHOD_CONTRACT;
@@ -3422,19 +3311,13 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT
if (!isStatic)
methodArgCount++; // count 'this'
MethodDesc *pCallerMethod = (MethodDesc*)pCtorData->pMethod;
- BOOL needsSecureDelegate = NeedsSecureDelegate(pCallerMethod, GetAppDomain(), pTargetMethod);
- if (!needsSecureDelegate && NeedsWrapperDelegate(pTargetMethod))
+ if (NeedsWrapperDelegate(pTargetMethod))
{
// If we need a wrapper even it is not a secure delegate, go through slow path
return NULL;
}
- // If this is a secure delegate case, and the secure delegate would have a pointer to a collectible
- // method in it, then use the slow path. This could be optimized with a set of fast paths.
- if (needsSecureDelegate && (pCallerMethod->IsLCGMethod() || pCallerMethod->GetLoaderAllocator()->IsCollectible()))
- return NULL;
-
// Force the slow path for nullable so that we can give the user an error in case were the verifier is not run.
MethodTable* pMT = pTargetMethod->GetMethodTable();
if (!pTargetMethod->IsStatic() && Nullable::IsNullableType(pMT))
@@ -3486,10 +3369,6 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT
// Another is to pass a gchandle to the delegate ctor. This is fastest, but only works if we can predict the gc handle at this time.
// We will use this for the non secure variants
- // Collectible secure delegates can go down the slow path
- if (isCollectible && needsSecureDelegate)
- return NULL;
-
if (invokeArgCount == methodArgCount)
{
// case 2, 3, 6
@@ -3501,9 +3380,7 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT
if (!isStatic && pTargetMethod->IsVirtual() && !pTargetMethod->GetMethodTable()->IsValueType())
{
// case 3
- if (needsSecureDelegate)
- pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_SECURE_VIRTUAL_DISPATCH);
- else if (isCollectible)
+ if (isCollectible)
pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_COLLECTIBLE_VIRTUAL_DISPATCH);
else
pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_VIRTUAL_DISPATCH);
@@ -3511,9 +3388,7 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT
else
{
// case 2, 6
- if (needsSecureDelegate)
- pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_SECURE_OPENED);
- else if (isCollectible)
+ if (isCollectible)
pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_COLLECTIBLE_OPENED);
else
pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_OPENED);
@@ -3527,13 +3402,7 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT
if (!pShuffleThunk)
pShuffleThunk = SetupShuffleThunk(pDelMT, pTargetMethod);
pCtorData->pArg3 = (void*)pShuffleThunk->GetEntryPoint();
- if (needsSecureDelegate)
- {
- // need to fill the info for the secure delegate
- pCtorData->pArg4 = (void *)GetSecureInvoke(pDelegateInvoke);
- pCtorData->pArg5 = pCallerMethod;
- }
- else if (isCollectible)
+ if (isCollectible)
{
pCtorData->pArg4 = pTargetMethodLoaderAllocator->GetLoaderAllocatorObjectHandle();
}
@@ -3557,41 +3426,22 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT
(pTargetMethod->IsInterface() ||
(pTargetMethod->GetMethodTable()->IsValueType() && !pTargetMethod->IsUnboxingStub()));
- if (needsSecureDelegate)
- {
- if (needsRuntimeInfo)
- pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_SECURE_RT_CLOSED);
- else
- {
- if (!isStatic)
- pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_SECURE_CLOSED);
- else
- pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_SECURE_CLOSED_STATIC);
- }
-
- // need to fill the info for the secure delegate
- pCtorData->pArg3 = (void *)GetSecureInvoke(pDelegateInvoke);
- pCtorData->pArg4 = pCallerMethod;
- }
+ if (needsRuntimeInfo)
+ pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_RT_CLOSED);
else
{
- if (needsRuntimeInfo)
- pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_RT_CLOSED);
+ if (!isStatic)
+ pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_CLOSED);
else
{
- if (!isStatic)
- pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_CLOSED);
+ if (isCollectible)
+ {
+ pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_COLLECTIBLE_CLOSED_STATIC);
+ pCtorData->pArg3 = pTargetMethodLoaderAllocator->GetLoaderAllocatorObjectHandle();
+ }
else
{
- if (isCollectible)
- {
- pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_COLLECTIBLE_CLOSED_STATIC);
- pCtorData->pArg3 = pTargetMethodLoaderAllocator->GetLoaderAllocatorObjectHandle();
- }
- else
- {
- pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_CLOSED_STATIC);
- }
+ pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_CLOSED_STATIC);
}
}
}
diff --git a/src/vm/comdelegate.h b/src/vm/comdelegate.h
index 5630cf9a75..f6ca775b60 100644
--- a/src/vm/comdelegate.h
+++ b/src/vm/comdelegate.h
@@ -71,8 +71,6 @@ public:
static PCODE GetSecureInvoke(MethodDesc* pMD);
// determines where the delegate needs to be wrapped for non-security reason
static BOOL NeedsWrapperDelegate(MethodDesc* pTargetMD);
- // determines whether the delegate needs to be wrapped
- static BOOL NeedsSecureDelegate(MethodDesc* pCreatorMethod, AppDomain *pCreatorDomain, MethodDesc* pTargetMD);
// on entry delegate points to the delegate to wrap
static DELEGATEREF CreateSecureDelegate(DELEGATEREF delegate, MethodDesc* pCreatorMethod, MethodDesc* pTargetMD);
@@ -122,10 +120,6 @@ public:
static Stub *GenerateStubForHost(MethodDesc *pInvokeMD, MethodDesc *pStubMD, LPVOID pNativeTarget, Stub *pInnerStub);
#endif // _TARGET_X86_
-#ifdef FEATURE_COMINTEROP
- static void DoUnmanagedCodeAccessCheck(MethodDesc* pMeth);
-#endif // FEATURE_COMINTEROP
-
static MethodDesc * __fastcall GetMethodDesc(OBJECTREF obj);
static OBJECTREF GetTargetObject(OBJECTREF obj);
diff --git a/src/vm/commodule.cpp b/src/vm/commodule.cpp
index cb14967295..45b31bfe68 100644
--- a/src/vm/commodule.cpp
+++ b/src/vm/commodule.cpp
@@ -9,7 +9,6 @@
#include "reflectclasswriter.h"
#include "class.h"
#include "corpolicy.h"
-#include "security.h"
#include "ceesectionstring.h"
#include <cor.h>
#include "typeparse.h"
diff --git a/src/vm/compile.cpp b/src/vm/compile.cpp
index abfe07e6f4..b3d187c555 100644
--- a/src/vm/compile.cpp
+++ b/src/vm/compile.cpp
@@ -22,7 +22,6 @@
#include "compile.h"
#include "excep.h"
#include "field.h"
-#include "security.h"
#include "eeconfig.h"
#include "zapsig.h"
#include "gcrefmap.h"
diff --git a/src/vm/comsynchronizable.cpp b/src/vm/comsynchronizable.cpp
index 01ba49651b..8fce346142 100644
--- a/src/vm/comsynchronizable.cpp
+++ b/src/vm/comsynchronizable.cpp
@@ -20,7 +20,6 @@
#include "excep.h"
#include "vars.hpp"
#include "field.h"
-#include "security.h"
#include "comsynchronizable.h"
#include "dbginterface.h"
#include "comdelegate.h"
@@ -29,6 +28,10 @@
#include "appdomain.hpp"
#include "appdomain.inl"
+#ifndef FEATURE_PAL
+#include "utilcode.h"
+#endif
+
#include "newapis.h"
// To include definition of CAPTURE_BUCKETS_AT_TRANSITION
@@ -1543,9 +1546,18 @@ void QCALLTYPE ThreadNative::InformThreadNameChange(QCall::ThreadHandle thread,
QCALL_CONTRACT;
BEGIN_QCALL;
-
+
Thread* pThread = &(*thread);
+#ifndef FEATURE_PAL
+ // Set on Windows 10 Creators Update and later machines the unmanaged thread name as well. That will show up in ETW traces and debuggers which is very helpful
+ // if more and more threads get a meaningful name
+ if (len > 0 && name != NULL)
+ {
+ SetThreadName(pThread->GetThreadHandle(), name);
+ }
+#endif
+
#ifdef PROFILING_SUPPORTED
{
BEGIN_PIN_PROFILER(CORProfilerTrackThreads());
@@ -1612,22 +1624,41 @@ FCIMPL1(FC_BOOL_RET, ThreadNative::IsThreadpoolThread, ThreadBaseObject* thread)
}
FCIMPLEND
+INT32 QCALLTYPE ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration()
+{
+ QCALL_CONTRACT;
+
+ INT32 optimalMaxNormalizedYieldsPerSpinIteration;
+
+ BEGIN_QCALL;
+
+ Thread::EnsureYieldProcessorNormalizedInitialized();
+ optimalMaxNormalizedYieldsPerSpinIteration = Thread::GetOptimalMaxNormalizedYieldsPerSpinIteration();
+
+ END_QCALL;
+
+ return optimalMaxNormalizedYieldsPerSpinIteration;
+}
FCIMPL1(void, ThreadNative::SpinWait, int iterations)
{
FCALL_CONTRACT;
+ if (iterations <= 0)
+ {
+ return;
+ }
+
//
// If we're not going to spin for long, it's ok to remain in cooperative mode.
// The threshold is determined by the cost of entering preemptive mode; if we're
// spinning for less than that number of cycles, then switching to preemptive
- // mode won't help a GC start any faster. That number is right around 1000000
- // on my machine.
+ // mode won't help a GC start any faster.
//
- if (iterations <= 1000000)
+ if (iterations <= 100000 && Thread::IsYieldProcessorNormalizedInitialized())
{
- for(int i = 0; i < iterations; i++)
- YieldProcessor();
+ for (int i = 0; i < iterations; i++)
+ Thread::YieldProcessorNormalized();
return;
}
@@ -1637,8 +1668,9 @@ FCIMPL1(void, ThreadNative::SpinWait, int iterations)
HELPER_METHOD_FRAME_BEGIN_NOPOLL();
GCX_PREEMP();
- for(int i = 0; i < iterations; i++)
- YieldProcessor();
+ Thread::EnsureYieldProcessorNormalizedInitialized();
+ for (int i = 0; i < iterations; i++)
+ Thread::YieldProcessorNormalized();
HELPER_METHOD_FRAME_END();
}
diff --git a/src/vm/comsynchronizable.h b/src/vm/comsynchronizable.h
index 00b055c960..b280c605b8 100644
--- a/src/vm/comsynchronizable.h
+++ b/src/vm/comsynchronizable.h
@@ -97,6 +97,7 @@ public:
UINT64 QCALLTYPE GetProcessDefaultStackSize();
static FCDECL1(INT32, GetManagedThreadId, ThreadBaseObject* th);
+ static INT32 QCALLTYPE GetOptimalMaxSpinWaitsPerSpinIteration();
static FCDECL1(void, SpinWait, int iterations);
static BOOL QCALLTYPE YieldThread();
static FCDECL0(Object*, GetCurrentThread);
diff --git a/src/vm/comthreadpool.cpp b/src/vm/comthreadpool.cpp
index a9fad74cee..c49f83400c 100644
--- a/src/vm/comthreadpool.cpp
+++ b/src/vm/comthreadpool.cpp
@@ -23,7 +23,6 @@
#include "object.h"
#include "field.h"
#include "excep.h"
-#include "security.h"
#include "eeconfig.h"
#include "corhost.h"
#include "nativeoverlapped.h"
diff --git a/src/vm/comtoclrcall.cpp b/src/vm/comtoclrcall.cpp
index b6d59a859f..11f522431d 100644
--- a/src/vm/comtoclrcall.cpp
+++ b/src/vm/comtoclrcall.cpp
@@ -28,7 +28,6 @@
#include "siginfo.hpp"
#include "comcallablewrapper.h"
#include "field.h"
-#include "security.h"
#include "virtualcallstub.h"
#include "dllimport.h"
#include "mlinfo.h"
@@ -425,45 +424,6 @@ void COMToCLRInvokeTarget(PCODE pManagedTarget, OBJECTREF pObject, ComCallMethod
InvokeStub(pCMD, pManagedTarget, pObject, pFrame, pThread, pRetValOut);
}
-bool COMToCLRWorkerBody_SecurityCheck(ComCallMethodDesc * pCMD, MethodDesc * pMD, Thread * pThread, UINT64 * pRetValOut)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- SO_TOLERANT;
- }
- CONTRACTL_END;
-
- bool result = true;
-
- BEGIN_SO_INTOLERANT_CODE_NOTHROW(pThread, { *pRetValOut = COR_E_STACKOVERFLOW; return false; } );
-
- EX_TRY
- {
-
- // Need to check for the presence of a security link demand on the target
- // method. If we're hosted inside of an app domain with security, we perform
- // the link demand against that app domain's grant set.
- Security::CheckLinkDemandAgainstAppDomain(pMD);
-
- if (pCMD->IsEarlyBoundUnsafe())
- COMPlusThrow(kSecurityException);
-
- }
- EX_CATCH
- {
- *pRetValOut = SetupErrorInfo(GET_THROWABLE());
- result = false;
- }
- EX_END_CATCH(SwallowAllExceptions);
-
- END_SO_INTOLERANT_CODE;
-
- return result;
-}
-
NOINLINE
void COMToCLRWorkerBody_Rare(Thread * pThread, ComMethodFrame * pFrame, ComCallWrapper * pWrap,
MethodDesc * pRealMD, ComCallMethodDesc * pCMD, DWORD maskedFlags,
@@ -482,17 +442,12 @@ void COMToCLRWorkerBody_Rare(Thread * pThread, ComMethodFrame * pFrame, ComCallW
OBJECTREF pObject;
int fpReturnSize = 0;
- if (maskedFlags & enum_NeedsSecurityCheck)
- {
- if (!COMToCLRWorkerBody_SecurityCheck(pCMD, pRealMD, pThread, pRetValOut))
- return;
- }
if (maskedFlags & enum_NativeR8Retval)
fpReturnSize = 8;
if (maskedFlags & enum_NativeR4Retval)
fpReturnSize = 4;
- maskedFlags &= ~(enum_NeedsSecurityCheck|enum_NativeR4Retval|enum_NativeR8Retval);
+ maskedFlags &= ~(enum_NativeR4Retval|enum_NativeR8Retval);
CONSISTENCY_CHECK(maskedFlags != ( enum_IsWinRTCtor|enum_IsVirtual));
CONSISTENCY_CHECK(maskedFlags != (enum_IsDelegateInvoke|enum_IsWinRTCtor|enum_IsVirtual));
@@ -573,7 +528,6 @@ void COMToCLRWorkerBody(
OBJECTREF pObject;
DWORD mask = (
- enum_NeedsSecurityCheck |
enum_IsDelegateInvoke |
enum_IsWinRTCtor |
enum_IsVirtual |
@@ -1104,11 +1058,6 @@ static void FieldCallWorkerBody(Thread *pThread, ComMethodFrame* pFrame)
}
#endif // PROFILING_SUPPORTED
- if (pCMD->IsEarlyBoundUnsafe())
- {
- COMPlusThrow(kSecurityException);
- }
-
UINT64 retVal;
InvokeStub(pCMD, NULL, pWrap->GetObjectRef(), pFrame, pThread, &retVal);
@@ -1338,20 +1287,6 @@ void ComCallMethodDesc::InitMethod(MethodDesc *pMD, MethodDesc *pInterfaceMD, BO
{
// Initialize the native type information size of native stack, native retval flags, etc).
InitNativeInfo();
-
- // If this interface method is implemented on a class which lives
- // in an assembly without UnmanagedCodePermission, then
- // we mark the ComCallMethodDesc as unsafe for being called early-bound.
- Module* pModule = pMD->GetModule();
- if (!Security::CanCallUnmanagedCode(pModule))
- {
- m_flags |= (enum_NeedsSecurityCheck | enum_IsEarlyBoundUnsafe);
- }
- else if (pMD->RequiresLinktimeCheck())
- {
- // remember that we have to call Security::CheckLinkDemandAgainstAppDomain at invocation time
- m_flags |= enum_NeedsSecurityCheck;
- }
}
if (pMD->IsEEImpl() && COMDelegate::IsDelegateInvokeMethod(pMD))
@@ -1384,15 +1319,6 @@ void ComCallMethodDesc::InitField(FieldDesc* pFD, BOOL isGetter)
{
// Initialize the native type information size of native stack, native retval flags, etc).
InitNativeInfo();
-
- // If this interface method is implemented on a class which lives
- // in an assembly without UnmanagedCodePermission, then
- // we mark the ComCallMethodDesc as unsafe for being called early-bound.
- Module* pModule = pFD->GetModule();
- if (!Security::CanCallUnmanagedCode(pModule))
- {
- m_flags |= enum_IsEarlyBoundUnsafe;
- }
}
};
diff --git a/src/vm/comtoclrcall.h b/src/vm/comtoclrcall.h
index 145aaadbd7..d2f3891993 100644
--- a/src/vm/comtoclrcall.h
+++ b/src/vm/comtoclrcall.h
@@ -29,10 +29,10 @@ enum ComCallFlags
enum_NativeHResultRetVal = 0x0040, // Native ret val is an HRESULT
enum_NativeBoolRetVal = 0x0080, // Native ret val is 0 in the case of failure
enum_NativeVoidRetVal = 0x0100, // Native ret val is void
- enum_IsEarlyBoundUnsafe = 0x0200, // Is unsafe to be called early-bound
+ // unused = 0x0200,
enum_HasMarshalError = 0x0400, // The signature is not marshalable and m_StackBytes is a guess
enum_IsDelegateInvoke = 0x0800, // The method is an 'Invoke' on a delegate
- enum_NeedsSecurityCheck = 0x1000, // Security check is needed at every invocation
+ // unused = 0x1000,
enum_IsWinRTCall = 0x2000, // The method is declared on a WinRT interface/delegate
enum_IsWinRTCtor = 0x4000, // The method is a WinRT constructor
enum_IsWinRTStatic = 0x8000, // The method is a WinRT static
@@ -119,18 +119,6 @@ public:
return (m_flags & enum_IsFieldCall);
}
- BOOL IsEarlyBoundUnsafe()
- {
- LIMITED_METHOD_CONTRACT;
- return (m_flags & enum_IsEarlyBoundUnsafe);
- }
-
- BOOL NeedsSecurityCheck()
- {
- LIMITED_METHOD_CONTRACT;
- return (m_flags & enum_NeedsSecurityCheck);
- }
-
BOOL IsMethodCall()
{
WRAPPER_NO_CONTRACT;
diff --git a/src/vm/comutilnative.cpp b/src/vm/comutilnative.cpp
index b75f684992..766336ee7f 100644
--- a/src/vm/comutilnative.cpp
+++ b/src/vm/comutilnative.cpp
@@ -2624,14 +2624,146 @@ FCIMPL6(INT32, ManagedLoggingHelper::GetRegistryLoggingValues, CLR_BOOL* bLoggin
}
FCIMPLEND
-// Return true if the valuetype does not contain pointer and is tightly packed
+static BOOL HasOverriddenMethod(MethodTable* mt, MethodTable* classMT, WORD methodSlot)
+{
+ CONTRACTL{
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ _ASSERTE(mt != NULL);
+ _ASSERTE(classMT != NULL);
+ _ASSERTE(methodSlot != 0);
+
+ PCODE actual = mt->GetRestoredSlot(methodSlot);
+ PCODE base = classMT->GetRestoredSlot(methodSlot);
+
+ if (actual == base)
+ {
+ return FALSE;
+ }
+
+ if (!classMT->IsZapped())
+ {
+ // If mscorlib is JITed, the slots can be patched and thus we need to compare the actual MethodDescs
+ // to detect match reliably
+ if (MethodTable::GetMethodDescForSlotAddress(actual) == MethodTable::GetMethodDescForSlotAddress(base))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ _ASSERTE(mt != NULL);
+
+ if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
+ {
+ return mt->CanCompareBitsOrUseFastGetHashCode();
+ }
+
+ if (mt->ContainsPointers()
+ || mt->IsNotTightlyPacked())
+ {
+ mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode();
+ return FALSE;
+ }
+
+ MethodTable* valueTypeMT = MscorlibBinder::GetClass(CLASS__VALUE_TYPE);
+ WORD slotEquals = MscorlibBinder::GetMethod(METHOD__VALUE_TYPE__EQUALS)->GetSlot();
+ WORD slotGetHashCode = MscorlibBinder::GetMethod(METHOD__VALUE_TYPE__GET_HASH_CODE)->GetSlot();
+
+ // Check the input type.
+ if (HasOverriddenMethod(mt, valueTypeMT, slotEquals)
+ || HasOverriddenMethod(mt, valueTypeMT, slotGetHashCode))
+ {
+ mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode();
+
+ // If overridden Equals or GetHashCode found, stop searching further.
+ return FALSE;
+ }
+
+ BOOL canCompareBitsOrUseFastGetHashCode = TRUE;
+
+ // The type itself did not override Equals or GetHashCode, go for its fields.
+ ApproxFieldDescIterator iter = ApproxFieldDescIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS);
+ for (FieldDesc* pField = iter.Next(); pField != NULL; pField = iter.Next())
+ {
+ if (pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
+ {
+ // Check current field type.
+ MethodTable* fieldMethodTable = pField->GetApproxFieldTypeHandleThrowing().GetMethodTable();
+ if (!CanCompareBitsOrUseFastGetHashCode(fieldMethodTable))
+ {
+ canCompareBitsOrUseFastGetHashCode = FALSE;
+ break;
+ }
+ }
+ else if (pField->GetFieldType() == ELEMENT_TYPE_R8
+ || pField->GetFieldType() == ELEMENT_TYPE_R4)
+ {
+ // We have double/single field, cannot compare in fast path.
+ canCompareBitsOrUseFastGetHashCode = FALSE;
+ break;
+ }
+ }
+
+ // We've gone through all instance fields. It's time to cache the result.
+ // Note SetCanCompareBitsOrUseFastGetHashCode(BOOL) ensures the checked flag
+ // and canCompare flag being set atomically to avoid race.
+ mt->SetCanCompareBitsOrUseFastGetHashCode(canCompareBitsOrUseFastGetHashCode);
+
+ return canCompareBitsOrUseFastGetHashCode;
+}
+
+NOINLINE static FC_BOOL_RET CanCompareBitsHelper(MethodTable* mt, OBJECTREF objRef)
+{
+ FC_INNER_PROLOG(ValueTypeHelper::CanCompareBits);
+
+ _ASSERTE(mt != NULL);
+ _ASSERTE(objRef != NULL);
+
+ BOOL ret = FALSE;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef);
+
+ ret = CanCompareBitsOrUseFastGetHashCode(mt);
+
+ HELPER_METHOD_FRAME_END();
+ FC_INNER_EPILOG();
+
+ FC_RETURN_BOOL(ret);
+}
+
+// Return true if the valuetype does not contain pointer, is tightly packed,
+// does not have floating point number field and does not override Equals method.
FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj)
{
FCALL_CONTRACT;
_ASSERTE(obj != NULL);
MethodTable* mt = obj->GetMethodTable();
- FC_RETURN_BOOL(!mt->ContainsPointers() && !mt->IsNotTightlyPacked());
+
+ if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
+ {
+ FC_RETURN_BOOL(mt->CanCompareBitsOrUseFastGetHashCode());
+ }
+
+ OBJECTREF objRef(obj);
+
+ FC_INNER_RETURN(FC_BOOL_RET, CanCompareBitsHelper(mt, objRef));
}
FCIMPLEND
@@ -2650,12 +2782,6 @@ FCIMPL2(FC_BOOL_RET, ValueTypeHelper::FastEqualsCheck, Object* obj1, Object* obj
}
FCIMPLEND
-static BOOL CanUseFastGetHashCodeHelper(MethodTable *mt)
-{
- LIMITED_METHOD_CONTRACT;
- return !mt->ContainsPointers() && !mt->IsNotTightlyPacked();
-}
-
static INT32 FastGetValueTypeHashCodeHelper(MethodTable *mt, void *pObjRef)
{
CONTRACTL
@@ -2664,7 +2790,6 @@ static INT32 FastGetValueTypeHashCodeHelper(MethodTable *mt, void *pObjRef)
GC_NOTRIGGER;
MODE_COOPERATIVE;
SO_TOLERANT;
- PRECONDITION(CanUseFastGetHashCodeHelper(mt));
} CONTRACTL_END;
INT32 hashCode = 0;
@@ -2690,9 +2815,19 @@ static INT32 RegularGetValueTypeHashCode(MethodTable *mt, void *pObjRef)
INT32 hashCode = 0;
INT32 *pObj = (INT32*)pObjRef;
+ BOOL canUseFastGetHashCodeHelper = FALSE;
+ if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
+ {
+ canUseFastGetHashCodeHelper = mt->CanCompareBitsOrUseFastGetHashCode();
+ }
+ else
+ {
+ canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(mt);
+ }
+
// While we shouln't get here directly from ValueTypeHelper::GetHashCode, if we recurse we need to
// be able to handle getting the hashcode for an embedded structure whose hashcode is computed by the fast path.
- if (CanUseFastGetHashCodeHelper(mt))
+ if (canUseFastGetHashCodeHelper)
{
return FastGetValueTypeHashCodeHelper(mt, pObjRef);
}
@@ -2797,17 +2932,29 @@ FCIMPL1(INT32, ValueTypeHelper::GetHashCode, Object* objUNSAFE)
// we munge the class index with two big prime numbers
hashCode = typeID * 711650207 + 2506965631U;
- if (CanUseFastGetHashCodeHelper(pMT))
+ BOOL canUseFastGetHashCodeHelper = FALSE;
+ if (pMT->HasCheckedCanCompareBitsOrUseFastGetHashCode())
+ {
+ canUseFastGetHashCodeHelper = pMT->CanCompareBitsOrUseFastGetHashCode();
+ }
+ else
+ {
+ HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
+ canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(pMT);
+ HELPER_METHOD_FRAME_END();
+ }
+
+ if (canUseFastGetHashCodeHelper)
{
hashCode ^= FastGetValueTypeHashCodeHelper(pMT, obj->UnBox());
}
else
{
- HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
+ HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
hashCode ^= RegularGetValueTypeHashCode(pMT, obj->UnBox());
HELPER_METHOD_FRAME_END();
}
-
+
return hashCode;
}
FCIMPLEND
@@ -2850,14 +2997,11 @@ COMNlsHashProvider::COMNlsHashProvider()
{
LIMITED_METHOD_CONTRACT;
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
- bUseRandomHashing = FALSE;
pEntropy = NULL;
pDefaultSeed = NULL;
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
}
-INT32 COMNlsHashProvider::HashString(LPCWSTR szStr, SIZE_T strLen, BOOL forceRandomHashing, INT64 additionalEntropy)
+INT32 COMNlsHashProvider::HashString(LPCWSTR szStr, SIZE_T strLen)
{
CONTRACTL {
THROWS;
@@ -2866,40 +3010,15 @@ INT32 COMNlsHashProvider::HashString(LPCWSTR szStr, SIZE_T strLen, BOOL forceRan
}
CONTRACTL_END;
-#ifndef FEATURE_RANDOMIZED_STRING_HASHING
- _ASSERTE(forceRandomHashing == false);
- _ASSERTE(additionalEntropy == 0);
-#endif
-
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
- if(bUseRandomHashing || forceRandomHashing)
- {
- int marvinResult[SYMCRYPT_MARVIN32_RESULT_SIZE / sizeof(int)];
-
- if(additionalEntropy == 0)
- {
- SymCryptMarvin32(GetDefaultSeed(), (PCBYTE) szStr, strLen * sizeof(WCHAR), (PBYTE) &marvinResult);
- }
- else
- {
- SYMCRYPT_MARVIN32_EXPANDED_SEED seed;
- CreateMarvin32Seed(additionalEntropy, &seed);
- SymCryptMarvin32(&seed, (PCBYTE) szStr, strLen * sizeof(WCHAR), (PBYTE) &marvinResult);
- }
+ int marvinResult[SYMCRYPT_MARVIN32_RESULT_SIZE / sizeof(int)];
+
+ SymCryptMarvin32(GetDefaultSeed(), (PCBYTE) szStr, strLen * sizeof(WCHAR), (PBYTE) &marvinResult);
- return marvinResult[0] ^ marvinResult[1];
- }
- else
- {
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
- return ::HashString(szStr);
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
- }
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
+ return marvinResult[0] ^ marvinResult[1];
}
-INT32 COMNlsHashProvider::HashSortKey(PCBYTE pSrc, SIZE_T cbSrc, BOOL forceRandomHashing, INT64 additionalEntropy)
+INT32 COMNlsHashProvider::HashSortKey(PCBYTE pSrc, SIZE_T cbSrc)
{
CONTRACTL {
THROWS;
@@ -2908,141 +3027,15 @@ INT32 COMNlsHashProvider::HashSortKey(PCBYTE pSrc, SIZE_T cbSrc, BOOL forceRando
}
CONTRACTL_END;
-#ifndef FEATURE_RANDOMIZED_STRING_HASHING
- _ASSERTE(forceRandomHashing == false);
- _ASSERTE(additionalEntropy == 0);
-#endif
-
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
- if(bUseRandomHashing || forceRandomHashing)
- {
- int marvinResult[SYMCRYPT_MARVIN32_RESULT_SIZE / sizeof(int)];
-
- // Sort Keys are terminated with a null byte which we didn't hash using the old algorithm,
- // so we don't have it with Marvin32 either.
- if(additionalEntropy == 0)
- {
- SymCryptMarvin32(GetDefaultSeed(), pSrc, cbSrc - 1, (PBYTE) &marvinResult);
- }
- else
- {
- SYMCRYPT_MARVIN32_EXPANDED_SEED seed;
- CreateMarvin32Seed(additionalEntropy, &seed);
- SymCryptMarvin32(&seed, pSrc, cbSrc - 1, (PBYTE) &marvinResult);
- }
-
- return marvinResult[0] ^ marvinResult[1];
- }
- else
- {
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
- // Ok, lets build the hashcode -- mostly lifted from GetHashCode() in String.cs, for strings.
- int hash1 = 5381;
- int hash2 = hash1;
- const BYTE *pB = pSrc;
- BYTE c;
-
- while (pB != 0 && *pB != 0) {
- hash1 = ((hash1 << 5) + hash1) ^ *pB;
- c = pB[1];
-
- //
- // FUTURE: Update NewAPis::LCMapStringEx to perhaps use a different, bug free, Win32 API on Win2k3 to workaround the issue discussed below.
- //
- // On Win2k3 Server, LCMapStringEx(LCMAP_SORTKEY) output does not correspond to CompareString in all cases, breaking the .NET GetHashCode<->Equality Contract
- // Due to a fluke in our GetHashCode method, we avoided this issue due to the break out of the loop on the binary-zero byte.
- //
- if (c == 0)
- break;
-
- hash2 = ((hash2 << 5) + hash2) ^ c;
- pB += 2;
- }
-
- return hash1 + (hash2 * 1566083941);
-
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
- }
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
-
-}
-
-INT32 COMNlsHashProvider::HashiStringKnownLower80(LPCWSTR szStr, INT32 strLen, BOOL forceRandomHashing, INT64 additionalEntropy)
-{
- CONTRACTL {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
-#ifndef FEATURE_RANDOMIZED_STRING_HASHING
- _ASSERTE(forceRandomHashing == false);
- _ASSERTE(additionalEntropy == 0);
-#endif
-
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
- if(bUseRandomHashing || forceRandomHashing)
- {
- WCHAR buf[SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE * 8];
- SYMCRYPT_MARVIN32_STATE marvinState;
- SYMCRYPT_MARVIN32_EXPANDED_SEED seed;
-
- if(additionalEntropy == 0)
- {
- SymCryptMarvin32Init(&marvinState, GetDefaultSeed());
- }
- else
- {
- CreateMarvin32Seed(additionalEntropy, &seed);
- SymCryptMarvin32Init(&marvinState, &seed);
- }
-
- LPCWSTR szEnd = szStr + strLen;
-
- const UINT A_TO_Z_RANGE = (UINT)('z' - 'a');
-
- while (szStr != szEnd)
- {
- size_t count = (sizeof(buf) / sizeof(buf[0]));
-
- if ((size_t)(szEnd - szStr) < count)
- count = (size_t)(szEnd - szStr);
-
- for (size_t i = 0; i<count; i++)
- {
- WCHAR c = szStr[i];
-
- if ((UINT)(c - 'a') <= A_TO_Z_RANGE) // if (c >='a' && c <= 'z')
- {
- //If we have a lowercase character, ANDing off 0x20
- // will make it an uppercase character.
- c &= ~0x20;
- }
-
- buf[i] = c;
- }
-
- szStr += count;
-
- SymCryptMarvin32Append(&marvinState, (PCBYTE) &buf, sizeof(WCHAR) * count);
- }
+ int marvinResult[SYMCRYPT_MARVIN32_RESULT_SIZE / sizeof(int)];
+
+ // Sort Keys are terminated with a null byte which we didn't hash using the old algorithm,
+ // so we don't have it with Marvin32 either.
+ SymCryptMarvin32(GetDefaultSeed(), pSrc, cbSrc - 1, (PBYTE) &marvinResult);
- int marvinResult[SYMCRYPT_MARVIN32_RESULT_SIZE / sizeof(int)];
- SymCryptMarvin32Result(&marvinState, (PBYTE) &marvinResult);
- return marvinResult[0] ^ marvinResult[1];
- }
- else
- {
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
- return ::HashiStringKnownLower80(szStr);
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
- }
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
+ return marvinResult[0] ^ marvinResult[1];
}
-
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
void COMNlsHashProvider::InitializeDefaultSeed()
{
CONTRACTL {
@@ -3110,27 +3103,8 @@ PCBYTE COMNlsHashProvider::GetEntropy()
return (PCBYTE) pEntropy;
}
-
-void COMNlsHashProvider::CreateMarvin32Seed(INT64 additionalEntropy, PSYMCRYPT_MARVIN32_EXPANDED_SEED pExpandedMarvinSeed)
-{
- CONTRACTL {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- INT64 *pEntropy = (INT64*) GetEntropy();
- INT64 entropy;
-
- entropy = *pEntropy ^ additionalEntropy;
-
- SymCryptMarvin32ExpandSeed(pExpandedMarvinSeed, (PCBYTE) &entropy, SYMCRYPT_MARVIN32_SEED_SIZE);
-}
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
-
#ifdef FEATURE_COREFX_GLOBALIZATION
-INT32 QCALLTYPE CoreFxGlobalization::HashSortKey(PCBYTE pSortKey, INT32 cbSortKey, BOOL forceRandomizedHashing, INT64 additionalEntropy)
+INT32 QCALLTYPE CoreFxGlobalization::HashSortKey(PCBYTE pSortKey, INT32 cbSortKey)
{
QCALL_CONTRACT;
@@ -3138,7 +3112,7 @@ INT32 QCALLTYPE CoreFxGlobalization::HashSortKey(PCBYTE pSortKey, INT32 cbSortKe
BEGIN_QCALL;
- retVal = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pSortKey, cbSortKey, forceRandomizedHashing, additionalEntropy);
+ retVal = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pSortKey, cbSortKey);
END_QCALL;
diff --git a/src/vm/comutilnative.h b/src/vm/comutilnative.h
index 41df265e91..831e1c071e 100644
--- a/src/vm/comutilnative.h
+++ b/src/vm/comutilnative.h
@@ -27,7 +27,6 @@
#undef GetCurrentTime
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
#pragma warning(push)
#pragma warning(disable:4324)
#if !defined(CROSS_COMPILE) && defined(_TARGET_ARM_) && !defined(PLATFORM_UNIX)
@@ -35,7 +34,6 @@
#endif
#include "marvin32.h"
#pragma warning(pop)
-#endif
//
//
@@ -260,33 +258,24 @@ class COMNlsHashProvider {
public:
COMNlsHashProvider();
- INT32 HashString(LPCWSTR szStr, SIZE_T strLen, BOOL forceRandomHashing, INT64 additionalEntropy);
- INT32 HashSortKey(PCBYTE pSrc, SIZE_T cbSrc, BOOL forceRandomHashing, INT64 additionalEntropy);
- INT32 HashiStringKnownLower80(LPCWSTR lpszStr, INT32 strLen, BOOL forceRandomHashing, INT64 additionalEntropy);
+ INT32 HashString(LPCWSTR szStr, SIZE_T strLen);
+ INT32 HashSortKey(PCBYTE pSrc, SIZE_T cbSrc);
static COMNlsHashProvider s_NlsHashProvider;
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
- void SetUseRandomHashing(BOOL useRandomHashing) { LIMITED_METHOD_CONTRACT; bUseRandomHashing = useRandomHashing; }
- BOOL GetUseRandomHashing() { LIMITED_METHOD_CONTRACT; return bUseRandomHashing; }
-
-
private:
- BOOL bUseRandomHashing;
PBYTE pEntropy;
PCSYMCRYPT_MARVIN32_EXPANDED_SEED pDefaultSeed;
PCBYTE GetEntropy();
PCSYMCRYPT_MARVIN32_EXPANDED_SEED GetDefaultSeed();
void InitializeDefaultSeed();
- void CreateMarvin32Seed(INT64 additionalEntropy, PSYMCRYPT_MARVIN32_EXPANDED_SEED pExpandedMarvinSeed);
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
};
#ifdef FEATURE_COREFX_GLOBALIZATION
class CoreFxGlobalization {
public:
- static INT32 QCALLTYPE HashSortKey(PCBYTE pSortKey, INT32 cbSortKey, BOOL forceRandomizedHashing, INT64 additionalEntropy);
+ static INT32 QCALLTYPE HashSortKey(PCBYTE pSortKey, INT32 cbSortKey);
};
#endif // FEATURE_COREFX_GLOBALIZATION
diff --git a/src/vm/crossgen/CMakeLists.txt b/src/vm/crossgen/CMakeLists.txt
index 805e932dda..8c706885b8 100644
--- a/src/vm/crossgen/CMakeLists.txt
+++ b/src/vm/crossgen/CMakeLists.txt
@@ -36,6 +36,7 @@ set(VM_CROSSGEN_SOURCES
../generics.cpp
../genmeth.cpp
../hash.cpp
+ ../ilinstrumentation.cpp
../ilmarshalers.cpp
../ilstubcache.cpp
../ilstubresolver.cpp
@@ -46,7 +47,6 @@ set(VM_CROSSGEN_SOURCES
../contractimpl.cpp
../jitinterface.cpp
../loaderallocator.cpp
- ../listlock.cpp
../memberload.cpp
../method.cpp
../methodimpl.cpp
diff --git a/src/vm/crossgen_mscorlib/CMakeLists.txt b/src/vm/crossgen_mscorlib/CMakeLists.txt
index 598ee9952c..12fdf9064f 100644
--- a/src/vm/crossgen_mscorlib/CMakeLists.txt
+++ b/src/vm/crossgen_mscorlib/CMakeLists.txt
@@ -6,7 +6,6 @@ add_definitions(
-DFEATURE_EVENT_TRACE=1
-DFEATURE_LOADER_OPTIMIZATION
-DFEATURE_MULTICOREJIT
- -DFEATURE_RANDOMIZED_STRING_HASHING
-DFEATURE_VERSIONING_LOG
)
diff --git a/src/vm/crossgencompile.cpp b/src/vm/crossgencompile.cpp
index 367112e285..c4b9d3dfc3 100644
--- a/src/vm/crossgencompile.cpp
+++ b/src/vm/crossgencompile.cpp
@@ -16,7 +16,6 @@
#include "comdelegate.h"
#include "compile.h"
-#include "security.h"
#include "invokeutil.h"
#include "comcallablewrapper.h"
@@ -436,7 +435,3 @@ BOOL AppDomain::BindingByManifestFile()
{
return FALSE;
}
-
-ReJitManager::ReJitManager()
-{
-}
diff --git a/src/vm/crst.h b/src/vm/crst.h
index a353c6ea44..fa8c307f3f 100644
--- a/src/vm/crst.h
+++ b/src/vm/crst.h
@@ -115,14 +115,15 @@ class CrstBase
friend class Thread;
friend class ThreadStore;
friend class ThreadSuspend;
-friend class ListLock;
-friend class ListLockEntry;
+template <typename ELEMENT>
+friend class ListLockBase;
+template <typename ELEMENT>
+friend class ListLockEntryBase;
//friend class CExecutionEngine;
friend struct SavedExceptionInfo;
friend void EEEnterCriticalSection(CRITSEC_COOKIE cookie);
friend void EELeaveCriticalSection(CRITSEC_COOKIE cookie);
-friend class ReJitPublishMethodHolder;
-friend class ReJitPublishMethodTableHolder;
+friend class CodeVersionManager;
friend class Debugger;
friend class Crst;
diff --git a/src/vm/customattribute.cpp b/src/vm/customattribute.cpp
index 60e002eb71..6c765414c3 100644
--- a/src/vm/customattribute.cpp
+++ b/src/vm/customattribute.cpp
@@ -10,7 +10,6 @@
#include "threads.h"
#include "excep.h"
#include "corerror.h"
-#include "security.h"
#include "classnames.h"
#include "fcall.h"
#include "assemblynative.hpp"
diff --git a/src/vm/dataimage.cpp b/src/vm/dataimage.cpp
index fc584d7b39..4e276fe460 100644
--- a/src/vm/dataimage.cpp
+++ b/src/vm/dataimage.cpp
@@ -738,9 +738,7 @@ FORCEINLINE static CorCompileSection GetSectionForNodeType(ZapNodeType type)
// SECTION_READONLY_WARM
case NodeTypeForItemKind(DataImage::ITEM_METHOD_TABLE):
- case NodeTypeForItemKind(DataImage::ITEM_VTABLE_CHUNK):
case NodeTypeForItemKind(DataImage::ITEM_INTERFACE_MAP):
- case NodeTypeForItemKind(DataImage::ITEM_DICTIONARY):
case NodeTypeForItemKind(DataImage::ITEM_DISPATCH_MAP):
case NodeTypeForItemKind(DataImage::ITEM_GENERICS_STATIC_FIELDDESCS):
case NodeTypeForItemKind(DataImage::ITEM_GC_STATIC_HANDLES_COLD):
@@ -750,6 +748,10 @@ FORCEINLINE static CorCompileSection GetSectionForNodeType(ZapNodeType type)
case NodeTypeForItemKind(DataImage::ITEM_STORED_METHOD_SIG_READONLY_WARM):
return CORCOMPILE_SECTION_READONLY_WARM;
+ case NodeTypeForItemKind(DataImage::ITEM_DICTIONARY):
+ case NodeTypeForItemKind(DataImage::ITEM_VTABLE_CHUNK):
+ return CORCOMPILE_SECTION_READONLY_VCHUNKS_AND_DICTIONARY;
+
// SECTION_CLASS_COLD
case NodeTypeForItemKind(DataImage::ITEM_PARAM_TYPEDESC):
case NodeTypeForItemKind(DataImage::ITEM_ARRAY_TYPEDESC):
diff --git a/src/vm/dataimage.h b/src/vm/dataimage.h
index 5d48a710e7..0167ec5762 100644
--- a/src/vm/dataimage.h
+++ b/src/vm/dataimage.h
@@ -309,8 +309,58 @@ public:
void FixupPointerField(PVOID p, SSIZE_T offset);
void FixupRelativePointerField(PVOID p, SSIZE_T offset);
+ template<typename T, typename PT>
+ void FixupPlainOrRelativePointerField(const T *base, const RelativePointer<PT> T::* pPointerFieldMember)
+ {
+ STANDARD_VM_CONTRACT;
+ SSIZE_T offset = (SSIZE_T) &(base->*pPointerFieldMember) - (SSIZE_T) base;
+ FixupRelativePointerField((PVOID)base, offset);
+ }
+
+ template<typename T, typename C, typename PT>
+ void FixupPlainOrRelativePointerField(const T *base, const C T::* pFirstPointerFieldMember, const RelativePointer<PT> C::* pSecondPointerFieldMember)
+ {
+ STANDARD_VM_CONTRACT;
+ const RelativePointer<PT> *ptr = &(base->*pFirstPointerFieldMember.*pSecondPointerFieldMember);
+ SSIZE_T offset = (SSIZE_T) ptr - (SSIZE_T) base;
+ FixupRelativePointerField((PVOID)base, offset);
+ }
+
+ template<typename T, typename PT>
+ void FixupPlainOrRelativePointerField(const T *base, const PlainPointer<PT> T::* pPointerFieldMember)
+ {
+ STANDARD_VM_CONTRACT;
+ SSIZE_T offset = (SSIZE_T) &(base->*pPointerFieldMember) - (SSIZE_T) base;
+ FixupPointerField((PVOID)base, offset);
+ }
+
+ template<typename T, typename C, typename PT>
+ void FixupPlainOrRelativePointerField(const T *base, const C T::* pFirstPointerFieldMember, const PlainPointer<PT> C::* pSecondPointerFieldMember)
+ {
+ STANDARD_VM_CONTRACT;
+ const PlainPointer<PT> *ptr = &(base->*pFirstPointerFieldMember.*pSecondPointerFieldMember);
+ SSIZE_T offset = (SSIZE_T) ptr - (SSIZE_T) base;
+ FixupPointerField((PVOID)base, offset);
+ }
+
void FixupField(PVOID p, SSIZE_T offset, PVOID pTarget, SSIZE_T targetOffset = 0, ZapRelocationType type = IMAGE_REL_BASED_PTR);
+ template<typename T, typename PT>
+ void FixupPlainOrRelativeField(const T *base, const RelativePointer<PT> T::* pPointerFieldMember, PVOID pTarget, SSIZE_T targetOffset = 0)
+ {
+ STANDARD_VM_CONTRACT;
+ SSIZE_T offset = (SSIZE_T) &(base->*pPointerFieldMember) - (SSIZE_T) base;
+ FixupField((PVOID)base, offset, pTarget, targetOffset, IMAGE_REL_BASED_RELPTR);
+ }
+
+ template<typename T, typename PT>
+ void FixupPlainOrRelativeField(const T *base, const PlainPointer<PT> T::* pPointerFieldMember, PVOID pTarget, SSIZE_T targetOffset = 0)
+ {
+ STANDARD_VM_CONTRACT;
+ SSIZE_T offset = (SSIZE_T) &(base->*pPointerFieldMember) - (SSIZE_T) base;
+ FixupField((PVOID)base, offset, pTarget, targetOffset, IMAGE_REL_BASED_PTR);
+ }
+
void FixupFieldToNode(PVOID p, SSIZE_T offset, ZapNode * pTarget, SSIZE_T targetOffset = 0, ZapRelocationType type = IMAGE_REL_BASED_PTR);
void FixupFieldToNode(PVOID p, SSIZE_T offset, ZapStoredStructure * pTarget, SSIZE_T targetOffset = 0, ZapRelocationType type = IMAGE_REL_BASED_PTR)
@@ -318,6 +368,34 @@ public:
return FixupFieldToNode(p, offset, (ZapNode *)pTarget, targetOffset, type);
}
+ template<typename T, typename PT>
+ void FixupPlainOrRelativeFieldToNode(const T *base, const RelativePointer<PT> T::* pPointerFieldMember, ZapNode * pTarget, SSIZE_T targetOffset = 0)
+ {
+ STANDARD_VM_CONTRACT;
+ SSIZE_T offset = (SSIZE_T) &(base->*pPointerFieldMember) - (SSIZE_T) base;
+ FixupFieldToNode((PVOID)base, offset, pTarget, targetOffset, IMAGE_REL_BASED_RELPTR);
+ }
+
+ template<typename T, typename PT>
+ void FixupPlainOrRelativeFieldToNode(const T *base, const RelativePointer<PT> T::* pPointerFieldMember, ZapStoredStructure * pTarget, SSIZE_T targetOffset = 0)
+ {
+ return FixupPlainOrRelativeFieldToNode(base, pPointerFieldMember, (ZapNode *)pTarget, targetOffset);
+ }
+
+ template<typename T, typename PT>
+ void FixupPlainOrRelativeFieldToNode(const T *base, const PlainPointer<PT> T::* pPointerFieldMember, ZapNode * pTarget, SSIZE_T targetOffset = 0)
+ {
+ STANDARD_VM_CONTRACT;
+ SSIZE_T offset = (SSIZE_T) &(base->*pPointerFieldMember) - (SSIZE_T) base;
+ FixupFieldToNode((PVOID)base, offset, pTarget, targetOffset, IMAGE_REL_BASED_PTR);
+ }
+
+ template<typename T, typename PT>
+ void FixupPlainOrRelativeFieldToNode(const T *base, const PlainPointer<PT> T::* pPointerFieldMember, ZapStoredStructure * pTarget, SSIZE_T targetOffset = 0)
+ {
+ return FixupPlainOrRelativeFieldToNode(base, pPointerFieldMember, (ZapNode *)pTarget, targetOffset);
+ }
+
BOOL IsStored(const void *data)
{ WRAPPER_NO_CONTRACT; return m_structures.LookupPtr(data) != NULL; }
diff --git a/src/vm/debughelp.cpp b/src/vm/debughelp.cpp
index 376b88cd42..23443ceece 100644
--- a/src/vm/debughelp.cpp
+++ b/src/vm/debughelp.cpp
@@ -318,7 +318,7 @@ MethodDesc* AsMethodDesc(size_t addr)
// extra indirection if the address is tagged (the low bit is set).
// That could AV if we don't check it first.
- if (!ppMT->IsTagged((TADDR)ppMT) || isMemoryReadable((TADDR)ppMT->GetValuePtr((TADDR)ppMT), sizeof(MethodTable*)))
+ if (!ppMT->IsTagged((TADDR)ppMT) || isMemoryReadable((TADDR)ppMT->GetValuePtr(), sizeof(MethodTable*)))
{
if (AsMethodTable((size_t)RelativeFixupPointer<PTR_MethodTable>::GetValueAtPtr((TADDR)ppMT)) != 0)
{
diff --git a/src/vm/dispatchinfo.cpp b/src/vm/dispatchinfo.cpp
index ee29506d27..492603da05 100644
--- a/src/vm/dispatchinfo.cpp
+++ b/src/vm/dispatchinfo.cpp
@@ -28,7 +28,6 @@
#include "olevariant.h"
#include "commtmemberinfomap.h"
#include "dispparammarshaler.h"
-#include "security.h"
#include "reflectioninvocation.h"
#include "dbginterface.h"
@@ -1588,50 +1587,6 @@ void DispatchInfo::InvokeMemberWorker(DispatchMemberInfo* pDispMemberInfo,
pObjs->MemberInfo = ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo);
MemberType = pDispMemberInfo->GetMemberType();
- // Determine whether the member has a link time security check. If so we
- // need to emulate this (since the caller is obviously not jitted in this
- // case). Only methods and properties can have a link time check.
- MethodDesc *pMDforSecurity = NULL;
-
- if (MemberType == Method)
- {
- MethodDescCallSite getMethodHandle(METHOD__METHOD_BASE__GET_METHODDESC, &pObjs->MemberInfo);
- ARG_SLOT arg = ObjToArgSlot(pObjs->MemberInfo);
- pMDforSecurity = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&arg);
- }
- else if (MemberType == Property)
- {
- MethodDescCallSite getSetter(METHOD__PROPERTY__GET_SETTER, &pObjs->MemberInfo);
- ARG_SLOT args[] =
- {
- ObjToArgSlot(pObjs->MemberInfo),
- BoolToArgSlot(false)
- };
- OBJECTREF method = getSetter.Call_RetOBJECTREF(args);
- if (method == NULL)
- {
- MethodDescCallSite getGetter(METHOD__PROPERTY__GET_GETTER, &pObjs->MemberInfo);
- ARG_SLOT args1[] =
- {
- ObjToArgSlot(pObjs->MemberInfo),
- BoolToArgSlot(false)
- };
- method = getGetter.Call_RetOBJECTREF(args1);
- }
-
- if (method != NULL)
- {
- GCPROTECT_BEGIN(method)
- MethodDescCallSite getMethodHandle(METHOD__METHOD_BASE__GET_METHODDESC, &method);
- ARG_SLOT arg = ObjToArgSlot(method);
- pMDforSecurity = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&arg);
- GCPROTECT_END();
- }
- }
-
- if (pMDforSecurity)
- Security::CheckLinkDemandAgainstAppDomain(pMDforSecurity);
-
switch (MemberType)
{
case Field:
diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp
index 49c7d7a8b8..e7857e412d 100644
--- a/src/vm/dllimport.cpp
+++ b/src/vm/dllimport.cpp
@@ -19,7 +19,6 @@
#include "dllimport.h"
#include "method.hpp"
#include "siginfo.hpp"
-#include "security.h"
#include "comdelegate.h"
#include "ceeload.h"
#include "mlinfo.h"
@@ -1183,7 +1182,6 @@ public:
#endif // FEATURE_COMINTEROP
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING, " NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL, " NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL\n", facility, level);
- LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_HASDECLARATIVESECURITY, " NDIRECTSTUB_FL_HASDECLARATIVESECURITY\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_UNMANAGED_CALLI, " NDIRECTSTUB_FL_UNMANAGED_CALLI\n", facility, level);
LogOneFlag(dwStubFlags, NDIRECTSTUB_FL_TRIGGERCCTOR, " NDIRECTSTUB_FL_TRIGGERCCTOR\n", facility, level);
#ifdef FEATURE_COMINTEROP
@@ -1214,7 +1212,6 @@ public:
NDIRECTSTUB_FL_REVERSE_INTEROP |
NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING |
NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL |
- NDIRECTSTUB_FL_HASDECLARATIVESECURITY |
NDIRECTSTUB_FL_UNMANAGED_CALLI |
NDIRECTSTUB_FL_TRIGGERCCTOR |
#ifdef FEATURE_COMINTEROP
@@ -5010,44 +5007,7 @@ MethodDesc* NDirect::CreateCLRToNativeILStub(
pParamTokenArray = (mdParamDef*)_alloca(numParamTokens * sizeof(mdParamDef));
CollateParamTokens(pModule->GetMDImport(), pSigDesc->m_tkMethodDef, numArgs, pParamTokenArray);
- // for interop vectors that have declarative security, we need
- // to update the stub flags to ensure a unique stub hash
- // is generated based on the marshalling signature AND
- // any declarative security.
- // IMPORTANT: This will only inject the security callouts for
- // interop functionality which has a non-null target MethodDesc.
- // Currently, this is known to exclude things like native
- // function ptrs. It is assumed that if the target is not
- // attribute'able for metadata, then it cannot have declarative
- // security - and that the target is not attributable if it was
- // not passed to this function.
MethodDesc *pMD = pSigDesc->m_pMD;
- if (pMD != NULL && SF_IsForwardStub(dwStubFlags))
- {
- // In an AppX process there is only one fully trusted AppDomain, so there is never any need to insert
- // a security callout on the stubs.
- if (!AppX::IsAppXProcess())
- {
-#ifdef FEATURE_COMINTEROP
- if (pMD->IsComPlusCall() || pMD->IsGenericComPlusCall())
- {
- // To preserve Whidbey behavior, we only enforce the implicit demand for
- // unmanaged code permission.
- MethodTable* pMT = ComPlusCallInfo::FromMethodDesc(pMD)->m_pInterfaceMT;
- if (pMT->ClassRequiresUnmanagedCodeCheck() &&
- !pMD->HasSuppressUnmanagedCodeAccessAttr())
- {
- dwStubFlags |= NDIRECTSTUB_FL_HASDECLARATIVESECURITY;
- }
- }
- else
-#endif // FEATURE_COMPINTEROP
- if (pMD->IsInterceptedForDeclSecurity())
- {
- dwStubFlags |= NDIRECTSTUB_FL_HASDECLARATIVESECURITY;
- }
- }
- }
NewHolder<ILStubState> pStubState;
@@ -5413,8 +5373,7 @@ PCODE JitILStub(MethodDesc* pStubMD)
// A dynamically generated IL stub
//
- CORJIT_FLAGS jitFlags = pStubMD->AsDynamicMethodDesc()->GetILStubResolver()->GetJitFlags();
- pCode = pStubMD->MakeJitWorker(NULL, jitFlags);
+ pCode = pStubMD->PrepareInitialCode();
_ASSERTE(pCode == pStubMD->GetNativeCode());
}
diff --git a/src/vm/dllimport.h b/src/vm/dllimport.h
index c918f58651..058484c45e 100644
--- a/src/vm/dllimport.h
+++ b/src/vm/dllimport.h
@@ -161,7 +161,7 @@ enum NDirectStubFlags
#endif // FEATURE_COMINTEROP
NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING = 0x00000100,
NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL = 0x00000200,
- NDIRECTSTUB_FL_HASDECLARATIVESECURITY = 0x00000400,
+ // unused = 0x00000400,
NDIRECTSTUB_FL_UNMANAGED_CALLI = 0x00000800,
NDIRECTSTUB_FL_TRIGGERCCTOR = 0x00001000,
#ifdef FEATURE_COMINTEROP
@@ -223,7 +223,6 @@ inline bool SF_IsHRESULTSwapping (DWORD dwStubFlags) { LIMITED_METHOD_CONT
inline bool SF_IsReverseStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_REVERSE_INTEROP)); }
inline bool SF_IsNGENedStubForProfiling(DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING)); }
inline bool SF_IsDebuggableStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_GENERATEDEBUGGABLEIL)); }
-inline bool SF_IsStubWithDemand (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_HASDECLARATIVESECURITY)); }
inline bool SF_IsCALLIStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_UNMANAGED_CALLI)); }
inline bool SF_IsStubWithCctorTrigger (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_TRIGGERCCTOR)); }
inline bool SF_IsForNumParamBytes (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_FOR_NUMPARAMBYTES)); }
@@ -299,10 +298,6 @@ inline void SF_ConsistencyCheck(DWORD dwStubFlags)
CONSISTENCY_CHECK(!(SF_IsFieldGetterStub(dwStubFlags) && !SF_IsHRESULTSwapping(dwStubFlags)));
CONSISTENCY_CHECK(!(SF_IsFieldSetterStub(dwStubFlags) && !SF_IsHRESULTSwapping(dwStubFlags)));
- // Reverse and CALLI stubs don't have demands
- CONSISTENCY_CHECK(!(SF_IsReverseStub(dwStubFlags) && SF_IsStubWithDemand(dwStubFlags)));
- CONSISTENCY_CHECK(!(SF_IsCALLIStub(dwStubFlags) && SF_IsStubWithDemand(dwStubFlags)));
-
// Delegate stubs are not COM
CONSISTENCY_CHECK(!(SF_IsDelegateStub(dwStubFlags) && SF_IsCOMStub(dwStubFlags)));
}
diff --git a/src/vm/dllimportcallback.cpp b/src/vm/dllimportcallback.cpp
index 90c01a496b..8684c12167 100644
--- a/src/vm/dllimportcallback.cpp
+++ b/src/vm/dllimportcallback.cpp
@@ -1111,13 +1111,8 @@ UMEntryThunk* UMEntryThunk::CreateUMEntryThunk()
UMEntryThunk * p;
-#ifdef FEATURE_WINDOWSPHONE
// On the phone, use loader heap to save memory commit of regular executable heap
p = (UMEntryThunk *)(void *)SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->AllocMem(S_SIZE_T(sizeof(UMEntryThunk)));
-#else
- p = new (executable) UMEntryThunk;
- memset (p, 0, sizeof(*p));
-#endif
RETURN p;
}
@@ -1126,11 +1121,10 @@ void UMEntryThunk::Terminate()
{
WRAPPER_NO_CONTRACT;
-#ifdef FEATURE_WINDOWSPHONE
+ _ASSERTE(!SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->IsZeroInit());
+ m_code.Poison();
+
SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap()->BackoutMem(this, sizeof(UMEntryThunk));
-#else
- DeleteExecutable(this);
-#endif
}
VOID UMEntryThunk::FreeUMEntryThunk(UMEntryThunk* p)
diff --git a/src/vm/dllimportcallback.h b/src/vm/dllimportcallback.h
index af2a0b1d92..e79c5f03ef 100644
--- a/src/vm/dllimportcallback.h
+++ b/src/vm/dllimportcallback.h
@@ -326,10 +326,6 @@ public:
{
DestroyLongWeakHandle(GetObjectHandle());
}
-
-#ifdef _DEBUG
- FillMemory(this, sizeof(*this), 0xcc);
-#endif
}
void Terminate();
diff --git a/src/vm/domainfile.cpp b/src/vm/domainfile.cpp
index 32f35fd39a..e5736b7282 100644
--- a/src/vm/domainfile.cpp
+++ b/src/vm/domainfile.cpp
@@ -16,7 +16,6 @@
#include <shlwapi.h>
-#include "security.h"
#include "invokeutil.h"
#include "eeconfig.h"
#include "dynamicmethod.h"
@@ -1291,10 +1290,6 @@ void DomainFile::Activate()
m_bDisableActivationCheck=TRUE;
pMT->CheckRunClassInitThrowing();
}
- if (g_pConfig->VerifyModulesOnLoad())
- {
- m_pModule->VerifyAllMethods();
- }
#ifdef _DEBUG
if (g_pConfig->ExpandModulesOnLoad())
{
diff --git a/src/vm/dynamicmethod.cpp b/src/vm/dynamicmethod.cpp
index acfea3e7f6..2d0fa9ce56 100644
--- a/src/vm/dynamicmethod.cpp
+++ b/src/vm/dynamicmethod.cpp
@@ -11,7 +11,6 @@
#include "object.h"
#include "method.hpp"
#include "comdelegate.h"
-#include "security.h"
#include "field.h"
#include "contractimpl.h"
#include "nibblemapmacros.h"
diff --git a/src/vm/dynamicmethod.h b/src/vm/dynamicmethod.h
index f9a92b0af0..7fd63e59b9 100644
--- a/src/vm/dynamicmethod.h
+++ b/src/vm/dynamicmethod.h
@@ -287,7 +287,7 @@ private:
public:
// Space for header is reserved immediately before. It is not included in size.
virtual void* AllocMemForCode_NoThrow(size_t header, size_t size, DWORD alignment) DAC_EMPTY_RET(NULL);
-
+
virtual ~HostCodeHeap() DAC_EMPTY();
LoaderAllocator* GetAllocator() { return m_pAllocator; }
@@ -307,6 +307,11 @@ protected:
void FreeMemForCode(void * codeStart);
+#if defined(FEATURE_JIT_PITCHING)
+public:
+ PTR_EEJitManager GetJitManager() { return m_pJitManager; }
+#endif
+
}; // class HostCodeHeap
//---------------------------------------------------------------------------------------
diff --git a/src/vm/ecall.cpp b/src/vm/ecall.cpp
index f3b0099e57..6f5f11b894 100644
--- a/src/vm/ecall.cpp
+++ b/src/vm/ecall.cpp
@@ -36,6 +36,7 @@ static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 1 == METHOD__STRING__CTORF_CH
static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 2 == METHOD__STRING__CTORF_CHAR_COUNT);
static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 3 == METHOD__STRING__CTORF_CHARPTR);
static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 4 == METHOD__STRING__CTORF_CHARPTR_START_LEN);
+static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 5 == METHOD__STRING__CTORF_READONLYSPANOFCHAR);
// ECall::CtorCharXxx has to be in same order as METHOD__STRING__CTORF_XXX
#define ECallCtor_First ECall::CtorCharArrayManaged
@@ -44,8 +45,9 @@ static_assert_no_msg(ECallCtor_First + 1 == ECall::CtorCharArrayStartLengthManag
static_assert_no_msg(ECallCtor_First + 2 == ECall::CtorCharCountManaged);
static_assert_no_msg(ECallCtor_First + 3 == ECall::CtorCharPtrManaged);
static_assert_no_msg(ECallCtor_First + 4 == ECall::CtorCharPtrStartLengthManaged);
+static_assert_no_msg(ECallCtor_First + 5 == ECall::CtorReadOnlySpanOfCharManaged);
-#define NumberOfStringConstructors 5
+#define NumberOfStringConstructors 6
void ECall::PopulateManagedStringConstructors()
{
@@ -557,10 +559,6 @@ LPVOID ECall::GetQCallImpl(MethodDesc * pMD)
("%s::%s is not registered using QCFuncElement macro in ecall.cpp",
pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
- CONSISTENCY_CHECK_MSGF(pMD->HasSuppressUnmanagedCodeAccessAttr(),
- ("%s::%s is not marked with SuppressUnmanagedCodeSecurityAttribute()",
- pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
-
DWORD dwAttrs = pMD->GetAttrs();
BOOL fPublicOrProtected = IsMdPublic(dwAttrs) || IsMdFamily(dwAttrs) || IsMdFamORAssem(dwAttrs);
diff --git a/src/vm/ecall.h b/src/vm/ecall.h
index c4fed1ff42..26fa9eb478 100644
--- a/src/vm/ecall.h
+++ b/src/vm/ecall.h
@@ -110,6 +110,7 @@ class ECall
DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorCharCountManaged, NULL) \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorCharPtrManaged, NULL) \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorCharPtrStartLengthManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorReadOnlySpanOfCharManaged, NULL) \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(InternalGetCurrentThread, NULL) \
enum
diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h
index 39ba874b5a..76be0b172c 100644
--- a/src/vm/ecalllist.h
+++ b/src/vm/ecalllist.h
@@ -109,6 +109,7 @@ FCFuncStart(gStringFuncs)
FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharPtrManaged)
FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharPtrStartLengthManaged)
FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_Char_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorCharCountManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::CtorReadOnlySpanOfCharManaged)
FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_RetVoid, COMString::StringInitCharPtr)
FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_RetVoid, COMString::StringInitCharPtrPartial)
FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_Encoding_RetVoid, COMString::StringInitSBytPtrPartialEx)
@@ -118,17 +119,14 @@ FCFuncStart(gStringFuncs)
FCIntrinsic("get_Chars", COMString::GetCharAt, CORINFO_INTRINSIC_StringGetChar)
FCFuncElement("IsAscii", COMString::IsAscii)
FCFuncElement("CompareOrdinalHelper", COMString::CompareOrdinalEx)
- FCFuncElement("IndexOfAny", COMString::IndexOfCharArray)
+ FCFuncElement("IndexOfCharArray", COMString::IndexOfCharArray)
FCFuncElement("LastIndexOfAny", COMString::LastIndexOfCharArray)
FCFuncElementSig("ReplaceInternal", &gsig_IM_Str_Str_RetStr, COMString::ReplaceString)
#ifdef FEATURE_COMINTEROP
FCFuncElement("SetTrailByte", COMString::FCSetTrailByte)
FCFuncElement("TryGetTrailByte", COMString::FCTryGetTrailByte)
#endif // FEATURE_COMINTEROP
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
FCFuncElement("InternalMarvin32HashString", COMString::Marvin32HashString)
- QCFuncElement("InternalUseRandomizedHashing", COMString::UseRandomizedHashing)
-#endif // FEATURE_RANDOMIZED_STRING_HASHING
FCFuncEnd()
FCFuncStart(gStringBufferFuncs)
@@ -296,6 +294,7 @@ FCFuncStart(gCOMTypeHandleFuncs)
FCFuncElement("IsComObject", RuntimeTypeHandle::IsComObject)
FCFuncElement("IsValueType", RuntimeTypeHandle::IsValueType)
FCFuncElement("IsInterface", RuntimeTypeHandle::IsInterface)
+ FCFuncElement("IsByRefLike", RuntimeTypeHandle::IsByRefLike)
QCFuncElement("_IsVisible", RuntimeTypeHandle::IsVisible)
QCFuncElement("ConstructName", RuntimeTypeHandle::ConstructName)
FCFuncElement("CanCastTo", RuntimeTypeHandle::CanCastTo)
@@ -343,10 +342,6 @@ FCFuncStart(gMetaDataImport)
FCFuncElement("_GetMarshalAs", MetaDataImport::GetMarshalAs)
FCFuncEnd()
-FCFuncStart(gRuntimeFieldInfoFuncs)
- FCFuncElement("PerformVisibilityCheckOnField", ReflectionInvocation::PerformVisibilityCheckOnField)
-FCFuncEnd()
-
FCFuncStart(gSignatureNative)
FCFuncElement("GetSignature", SignatureNative::GetSignature)
FCFuncElement("GetCustomModifiers", SignatureNative::GetCustomModifiers)
@@ -369,6 +364,7 @@ FCFuncStart(gRuntimeMethodHandle)
QCFuncElement("GetMethodInstantiation", RuntimeMethodHandle::GetMethodInstantiation)
FCFuncElement("HasMethodInstantiation", RuntimeMethodHandle::HasMethodInstantiation)
FCFuncElement("IsGenericMethodDefinition", RuntimeMethodHandle::IsGenericMethodDefinition)
+ FCFuncElement("GetGenericParameterCount", RuntimeMethodHandle::GetGenericParameterCount)
FCFuncElement("IsTypicalMethodDefinition", RuntimeMethodHandle::IsTypicalMethodDefinition)
QCFuncElement("GetTypicalMethodDefinition", RuntimeMethodHandle::GetTypicalMethodDefinition)
QCFuncElement("StripMethodInstantiation", RuntimeMethodHandle::StripMethodInstantiation)
@@ -572,6 +568,7 @@ FCFuncStart(gAssemblyFuncs)
FCFuncElement("FCallIsDynamic", AssemblyNative::IsDynamic)
FCFuncElement("nLoad", AssemblyNative::Load)
QCFuncElement("GetType", AssemblyNative::GetType)
+ QCFuncElement("GetForwardedType", AssemblyNative::GetForwardedType)
QCFuncElement("GetManifestResourceInfo", AssemblyNative::GetManifestResourceInfo)
QCFuncElement("GetModules", AssemblyNative::GetModules)
QCFuncElement("GetModule", AssemblyNative::GetModule)
@@ -713,6 +710,7 @@ FCFuncStart(gRuntimeThreadFuncs)
#endif // FEATURE_COMINTEROP
FCFuncElement("InterruptInternal", ThreadNative::Interrupt)
FCFuncElement("JoinInternal", ThreadNative::Join)
+ QCFuncElement("GetOptimalMaxSpinWaitsPerSpinIterationInternal", ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration)
FCFuncEnd()
FCFuncStart(gThreadFuncs)
@@ -1246,6 +1244,7 @@ FCFuncStart(gEventPipeInternalFuncs)
QCFuncElement("DefineEvent", EventPipeInternal::DefineEvent)
QCFuncElement("DeleteProvider", EventPipeInternal::DeleteProvider)
QCFuncElement("WriteEvent", EventPipeInternal::WriteEvent)
+ QCFuncElement("WriteEventData", EventPipeInternal::WriteEventData)
FCFuncEnd()
#endif // FEATURE_PERFTRACING
@@ -1412,7 +1411,6 @@ FCClassElement("RegistrationServices", "System.Runtime.InteropServices", gRegist
#endif // FEATURE_COMINTEROP_MANAGED_ACTIVATION
#endif // FEATURE_COMINTEROP
-FCClassElement("RtFieldInfo", "System.Reflection", gRuntimeFieldInfoFuncs)
FCClassElement("RuntimeAssembly", "System.Reflection", gAssemblyFuncs)
#ifdef FEATURE_COMINTEROP
FCClassElement("RuntimeClass", "System.Runtime.InteropServices.WindowsRuntime", gRuntimeClassFuncs)
diff --git a/src/vm/eeconfig.cpp b/src/vm/eeconfig.cpp
index 05cdd0aa6c..4c49d1457f 100644
--- a/src/vm/eeconfig.cpp
+++ b/src/vm/eeconfig.cpp
@@ -241,7 +241,6 @@ HRESULT EEConfig::Init()
INDEBUG(fStressLog = true;)
- fVerifyAllOnLoad = false;
#ifdef _DEBUG
fExpandAllOnLoad = false;
fDebuggable = false;
@@ -382,6 +381,14 @@ HRESULT EEConfig::Init()
fTieredCompilation = false;
#endif
+#if defined(FEATURE_GDBJIT) && defined(_DEBUG)
+ pszGDBJitElfDump = NULL;
+#endif // FEATURE_GDBJIT && _DEBUG
+
+#if defined(FEATURE_GDBJIT_FRAME)
+ fGDBJitEmitDebugFrame = false;
+#endif
+
// After initialization, register the code:#GetConfigValueCallback method with code:CLRConfig to let
// CLRConfig access config files. This is needed because CLRConfig lives outside the VM and can't
// statically link to EEConfig.
@@ -1096,9 +1103,6 @@ HRESULT EEConfig::sync()
fEnableRCWCleanupOnSTAShutdown = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EnableRCWCleanupOnSTAShutdown) != 0);
#endif // FEATURE_COMINTEROP
- //Eager verification of all assemblies.
- fVerifyAllOnLoad = (GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_VerifyAllOnLoad, fVerifyAllOnLoad) != 0);
-
#ifdef _DEBUG
fExpandAllOnLoad = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_ExpandAllOnLoad, fExpandAllOnLoad) != 0);
#endif //_DEBUG
@@ -1240,6 +1244,17 @@ HRESULT EEConfig::sync()
fTieredCompilation = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TieredCompilation) != 0;
#endif
+#if defined(FEATURE_GDBJIT) && defined(_DEBUG)
+ {
+ LPWSTR pszGDBJitElfDumpW = NULL;
+ CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GDBJitElfDump, &pszGDBJitElfDumpW);
+ pszGDBJitElfDump = NarrowWideChar(pszGDBJitElfDumpW);
+ }
+#endif // FEATURE_GDBJIT && _DEBUG
+
+#if defined(FEATURE_GDBJIT_FRAME)
+ fGDBJitEmitDebugFrame = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GDBJitEmitDebugFrame) != 0;
+#endif
return hr;
}
diff --git a/src/vm/eeconfig.h b/src/vm/eeconfig.h
index ccd5cd28bd..c55ba06a0b 100644
--- a/src/vm/eeconfig.h
+++ b/src/vm/eeconfig.h
@@ -286,6 +286,21 @@ public:
bool TieredCompilation(void) const {LIMITED_METHOD_CONTRACT; return fTieredCompilation; }
#endif
+#if defined(FEATURE_GDBJIT) && defined(_DEBUG)
+ inline bool ShouldDumpElfOnMethod(LPCUTF8 methodName) const
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(methodName, NULL_OK));
+ } CONTRACTL_END
+ return RegexOrExactMatch(pszGDBJitElfDump, methodName);
+ }
+#endif // FEATURE_GDBJIT && _DEBUG
+
+#if defined(FEATURE_GDBJIT_FRAME)
+ inline bool ShouldEmitDebugFrame(void) const {LIMITED_METHOD_CONTRACT; return fGDBJitEmitDebugFrame;}
+#endif // FEATURE_GDBJIT_FRAME
BOOL PInvokeRestoreEsp(BOOL fDefault) const
{
LIMITED_METHOD_CONTRACT;
@@ -474,7 +489,6 @@ public:
}
#endif // FEATURE_COMINTEROP
- bool VerifyModulesOnLoad(void) const { LIMITED_METHOD_CONTRACT; return fVerifyAllOnLoad; }
#ifdef _DEBUG
bool ExpandModulesOnLoad(void) const { LIMITED_METHOD_CONTRACT; return fExpandAllOnLoad; }
#endif //_DEBUG
@@ -934,8 +948,6 @@ private: //----------------------------------------------------------------
bool m_fDeveloperInstallation; // We are on a developers machine
bool fAppDomainUnload; // Enable appdomain unloading
- bool fVerifyAllOnLoad; // True if we want to verify all methods in an assembly at load time.
-
DWORD dwADURetryCount;
#ifdef _DEBUG
@@ -1101,6 +1113,13 @@ private: //----------------------------------------------------------------
bool fTieredCompilation;
#endif
+#if defined(FEATURE_GDBJIT) && defined(_DEBUG)
+ LPCUTF8 pszGDBJitElfDump;
+#endif // FEATURE_GDBJIT && _DEBUG
+
+#if defined(FEATURE_GDBJIT_FRAME)
+ bool fGDBJitEmitDebugFrame;
+#endif
public:
HRESULT GetConfiguration_DontUse_(__in_z LPCWSTR pKey, ConfigSearch direction, __deref_out_opt LPCWSTR* value);
diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp
index 9a94923493..eebd2744a4 100644
--- a/src/vm/eventpipe.cpp
+++ b/src/vm/eventpipe.cpp
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#include "clrtypes.h"
+#include "safemath.h"
#include "common.h"
#include "eventpipe.h"
#include "eventpipebuffermanager.h"
@@ -38,6 +40,146 @@ extern "C" void InitProvidersAndEvents();
extern "C" void InitProvidersAndEvents();
#endif
+EventPipeEventPayload::EventPipeEventPayload(BYTE *pData, unsigned int length)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_pData = pData;
+ m_pEventData = NULL;
+ m_eventDataCount = 0;
+ m_allocatedData = false;
+
+ m_size = length;
+}
+
+EventPipeEventPayload::EventPipeEventPayload(EventData **pEventData, unsigned int eventDataCount)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ m_pData = NULL;
+ m_pEventData = pEventData;
+ m_eventDataCount = eventDataCount;
+ m_allocatedData = false;
+
+ S_UINT32 tmp_size = S_UINT32(0);
+ for (unsigned int i=0; i<m_eventDataCount; i++)
+ {
+ tmp_size += S_UINT32((*m_pEventData)[i].Size);
+ }
+
+ if (tmp_size.IsOverflow())
+ {
+ // If there is an overflow, drop the data and create an empty payload
+ m_pEventData = NULL;
+ m_eventDataCount = 0;
+ m_size = 0;
+ }
+ else
+ {
+ m_size = tmp_size.Value();
+ }
+}
+
+EventPipeEventPayload::~EventPipeEventPayload()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(m_allocatedData && m_pData != NULL)
+ {
+ delete[] m_pData;
+ m_pData = NULL;
+ }
+}
+
+void EventPipeEventPayload::Flatten()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(m_size > 0)
+ {
+ if (!IsFlattened())
+ {
+ BYTE* tmp_pData = new (nothrow) BYTE[m_size];
+ if (tmp_pData != NULL)
+ {
+ m_allocatedData = true;
+ CopyData(tmp_pData);
+ m_pData = tmp_pData;
+ }
+ }
+ }
+}
+
+void EventPipeEventPayload::CopyData(BYTE *pDst)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(m_size > 0)
+ {
+ if(IsFlattened())
+ {
+ memcpy(pDst, m_pData, m_size);
+ }
+
+ else if(m_pEventData != NULL)
+ {
+ unsigned int offset = 0;
+ for(unsigned int i=0; i<m_eventDataCount; i++)
+ {
+ memcpy(pDst + offset, (BYTE*)(*m_pEventData)[i].Ptr, (*m_pEventData)[i].Size);
+ offset += (*m_pEventData)[i].Size;
+ }
+ }
+ }
+}
+
+BYTE* EventPipeEventPayload::GetFlatData()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (!IsFlattened())
+ {
+ Flatten();
+ }
+ return m_pData;
+}
+
void EventPipe::Initialize()
{
STANDARD_VM_CONTRACT;
@@ -238,7 +380,7 @@ bool EventPipe::Enabled()
return enabled;
}
-EventPipeProvider* EventPipe::CreateProvider(const GUID &providerID, EventPipeCallback pCallbackFunction, void *pCallbackData)
+EventPipeProvider* EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
{
CONTRACTL
{
@@ -248,7 +390,7 @@ EventPipeProvider* EventPipe::CreateProvider(const GUID &providerID, EventPipeCa
}
CONTRACTL_END;
- return new EventPipeProvider(providerID, pCallbackFunction, pCallbackData);
+ return new EventPipeProvider(providerName, pCallbackFunction, pCallbackData);
}
void EventPipe::DeleteProvider(EventPipeProvider *pProvider)
@@ -289,6 +431,34 @@ void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int leng
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ EventPipeEventPayload payload(pData, length);
+ EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId);
+}
+
+void EventPipe::WriteEvent(EventPipeEvent &event, EventData **pEventData, unsigned int eventDataCount, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ EventPipeEventPayload payload(pEventData, eventDataCount);
+ EventPipe::WriteEventInternal(event, payload, pActivityId, pRelatedActivityId);
+}
+
+void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
PRECONDITION(s_pBufferManager != NULL);
}
CONTRACTL_END;
@@ -309,7 +479,7 @@ void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int leng
if(!s_pConfig->RundownEnabled() && s_pBufferManager != NULL)
{
- if(!s_pBufferManager->WriteEvent(pThread, event, pData, length, pActivityId, pRelatedActivityId))
+ if(!s_pBufferManager->WriteEvent(pThread, event, payload, pActivityId, pRelatedActivityId))
{
// This is used in DEBUG to make sure that we don't log an event synchronously that we didn't log to the buffer.
return;
@@ -317,19 +487,23 @@ void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int leng
}
else if(s_pConfig->RundownEnabled())
{
- // Write synchronously to the file.
- // We're under lock and blocking the disabling thread.
- EventPipeEventInstance instance(
- event,
- pThread->GetOSThreadId(),
- pData,
- length,
- pActivityId,
- pRelatedActivityId);
-
- if(s_pFile != NULL)
+ BYTE *pData = payload.GetFlatData();
+ if (pData != NULL)
{
- s_pFile->WriteEvent(instance);
+ // Write synchronously to the file.
+ // We're under lock and blocking the disabling thread.
+ EventPipeEventInstance instance(
+ event,
+ pThread->GetOSThreadId(),
+ pData,
+ payload.GetSize(),
+ pActivityId,
+ pRelatedActivityId);
+
+ if(s_pFile != NULL)
+ {
+ s_pFile->WriteEvent(instance);
+ }
}
}
@@ -337,25 +511,29 @@ void EventPipe::WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int leng
{
GCX_PREEMP();
- // Create an instance of the event for the synchronous path.
- EventPipeEventInstance instance(
- event,
- pThread->GetOSThreadId(),
- pData,
- length,
- pActivityId,
- pRelatedActivityId);
-
- // Write to the EventPipeFile if it exists.
- if(s_pSyncFile != NULL)
+ BYTE *pData = payload.GetFlatData();
+ if (pData != NULL)
{
- s_pSyncFile->WriteEvent(instance);
- }
+ // Create an instance of the event for the synchronous path.
+ EventPipeEventInstance instance(
+ event,
+ pThread->GetOSThreadId(),
+ pData,
+ payload.GetSize(),
+ pActivityId,
+ pRelatedActivityId);
+
+ // Write to the EventPipeFile if it exists.
+ if(s_pSyncFile != NULL)
+ {
+ s_pSyncFile->WriteEvent(instance);
+ }
- // Write to the EventPipeJsonFile if it exists.
- if(s_pJsonFile != NULL)
- {
- s_pJsonFile->WriteEvent(instance);
+ // Write to the EventPipeJsonFile if it exists.
+ if(s_pJsonFile != NULL)
+ {
+ s_pJsonFile->WriteEvent(instance);
+ }
}
}
#endif // _DEBUG
@@ -371,12 +549,14 @@ void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent
}
CONTRACTL_END;
+ EventPipeEventPayload payload(pData, length);
+
// Write the event to the thread's buffer.
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.
- if(!s_pBufferManager->WriteEvent(pSamplingThread, *pEvent, pData, length, NULL /* pActivityId */, NULL /* pRelatedActivityId */, pTargetThread, &stackContents))
+ if(!s_pBufferManager->WriteEvent(pSamplingThread, *pEvent, payload, NULL /* pActivityId */, NULL /* pRelatedActivityId */, pTargetThread, &stackContents))
{
// This is used in DEBUG to make sure that we don't log an event synchronously that we didn't log to the buffer.
return;
@@ -520,7 +700,7 @@ void QCALLTYPE EventPipeInternal::Disable()
}
INT_PTR QCALLTYPE EventPipeInternal::CreateProvider(
- GUID providerID,
+ __in_z LPCWSTR providerName,
EventPipeCallback pCallbackFunc)
{
QCALL_CONTRACT;
@@ -529,7 +709,7 @@ INT_PTR QCALLTYPE EventPipeInternal::CreateProvider(
BEGIN_QCALL;
- pProvider = EventPipe::CreateProvider(providerID, pCallbackFunc, NULL);
+ pProvider = EventPipe::CreateProvider(providerName, pCallbackFunc, NULL);
END_QCALL;
@@ -595,4 +775,22 @@ void QCALLTYPE EventPipeInternal::WriteEvent(
END_QCALL;
}
+void QCALLTYPE EventPipeInternal::WriteEventData(
+ INT_PTR eventHandle,
+ unsigned int eventID,
+ EventData **pEventData,
+ unsigned int 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;
+}
+
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h
index fa7d734280..bac7be6ac8 100644
--- a/src/vm/eventpipe.h
+++ b/src/vm/eventpipe.h
@@ -29,6 +29,69 @@ typedef void (*EventPipeCallback)(
void *FilterData,
void *CallbackContext);
+struct EventData
+{
+public:
+ unsigned long Ptr;
+ unsigned int Size;
+ unsigned int Reserved;
+};
+
+class EventPipeEventPayload
+{
+private:
+ BYTE *m_pData;
+ EventData **m_pEventData;
+ unsigned int m_eventDataCount;
+ unsigned int m_size;
+ bool m_allocatedData;
+
+ // If the data is stored only as an array of EventData objects, create a flat buffer and copy into it
+ void Flatten();
+
+public:
+ // Build this payload with a flat buffer inside
+ EventPipeEventPayload(BYTE *pData, unsigned int length);
+
+ // Build this payload to contain an array of EventData objects
+ EventPipeEventPayload(EventData **pEventData, unsigned int eventDataCount);
+
+ // If a buffer was allocated internally, delete it
+ ~EventPipeEventPayload();
+
+ // Copy the data (whether flat or array of objects) into a flat buffer at pDst
+ // Assumes that pDst points to an appropriatly sized buffer
+ void CopyData(BYTE *pDst);
+
+ // 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();
+
+ // Return true is the data is stored in a flat buffer
+ bool IsFlattened() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pData != NULL;
+ }
+
+ // The the size of buffer needed to contain the stored data
+ unsigned int GetSize() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_size;
+ }
+
+ EventData** GetEventDataArray() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pEventData;
+ }
+};
+
class StackContents
{
private:
@@ -181,15 +244,19 @@ class EventPipe
static bool Enabled();
// Create a provider.
- static EventPipeProvider* CreateProvider(const GUID &providerID, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL);
+ static EventPipeProvider* CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL);
// Delete a provider.
static void DeleteProvider(EventPipeProvider *pProvider);
- // Write out an event.
+ // 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);
@@ -199,6 +266,11 @@ class EventPipe
// Get the managed call stack for the specified thread.
static bool WalkManagedStackForThread(Thread *pThread, StackContents &stackContents);
+ protected:
+
+ // The counterpart to WriteEvent which after the payload is constructed
+ static void WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
+
private:
// Callback function for the stack walker. For each frame walked, this callback is invoked.
@@ -286,7 +358,7 @@ public:
static void QCALLTYPE Disable();
static INT_PTR QCALLTYPE CreateProvider(
- GUID providerID,
+ __in_z LPCWSTR providerName,
EventPipeCallback pCallbackFunc);
static INT_PTR QCALLTYPE DefineEvent(
@@ -307,6 +379,13 @@ public:
void *pData,
unsigned int length,
LPCGUID pActivityId, LPCGUID pRelatedActivityId);
+
+ static void QCALLTYPE WriteEventData(
+ INT_PTR eventHandle,
+ unsigned int eventID,
+ EventData **pEventData,
+ unsigned int eventDataCount,
+ LPCGUID pActivityId, LPCGUID pRelatedActivityId);
};
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipebuffer.cpp b/src/vm/eventpipebuffer.cpp
index 00652c9fac..80b4a4f1b7 100644
--- a/src/vm/eventpipebuffer.cpp
+++ b/src/vm/eventpipebuffer.cpp
@@ -4,6 +4,7 @@
#include "common.h"
+#include "eventpipe.h"
#include "eventpipeeventinstance.h"
#include "eventpipebuffer.h"
@@ -46,7 +47,7 @@ EventPipeBuffer::~EventPipeBuffer()
}
}
-bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int dataLength, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack)
+bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack)
{
CONTRACTL
{
@@ -58,7 +59,7 @@ bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *p
CONTRACTL_END;
// Calculate the size of the event.
- unsigned int eventSize = sizeof(EventPipeEventInstance) + dataLength;
+ unsigned int eventSize = sizeof(EventPipeEventInstance) + payload.GetSize();
// Make sure we have enough space to write the event.
if(m_pCurrent + eventSize >= m_pLimit)
@@ -77,7 +78,7 @@ bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *p
event,
pThread->GetOSThreadId(),
pDataDest,
- dataLength,
+ payload.GetSize(),
pActivityId,
pRelatedActivityId);
@@ -89,9 +90,9 @@ bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *p
}
// Write the event payload data to the buffer.
- if(dataLength > 0)
+ if(payload.GetSize() > 0)
{
- memcpy(pDataDest, pData, dataLength);
+ payload.CopyData(pDataDest);
}
// Save the most recent event timestamp.
diff --git a/src/vm/eventpipebuffer.h b/src/vm/eventpipebuffer.h
index f279a2865c..c96ad26609 100644
--- a/src/vm/eventpipebuffer.h
+++ b/src/vm/eventpipebuffer.h
@@ -7,6 +7,7 @@
#ifdef FEATURE_PERFTRACING
+#include "eventpipe.h"
#include "eventpipeevent.h"
#include "eventpipeeventinstance.h"
@@ -81,7 +82,7 @@ public:
// Returns:
// - true: The write succeeded.
// - false: The write failed. In this case, the buffer should be considered full.
- bool WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int dataLength, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack = NULL);
+ bool WriteEvent(Thread *pThread, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack = NULL);
// Get the timestamp of the most recent event in the buffer.
LARGE_INTEGER GetMostRecentTimeStamp() const;
diff --git a/src/vm/eventpipebuffermanager.cpp b/src/vm/eventpipebuffermanager.cpp
index 86a3e03c59..e7d97d5732 100644
--- a/src/vm/eventpipebuffermanager.cpp
+++ b/src/vm/eventpipebuffermanager.cpp
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
#include "common.h"
+#include "eventpipe.h"
#include "eventpipeconfiguration.h"
#include "eventpipebuffer.h"
#include "eventpipebuffermanager.h"
@@ -32,6 +33,49 @@ EventPipeBufferManager::EventPipeBufferManager()
#endif // _DEBUG
}
+EventPipeBufferManager::~EventPipeBufferManager()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if(m_pPerThreadBufferList != NULL)
+ {
+ SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
+ while(pElem != NULL)
+ {
+ SListElem<EventPipeBufferList*> *pCurElem = pElem;
+
+ EventPipeBufferList *pThreadBufferList = pCurElem->GetValue();
+ if (!pThreadBufferList->OwnedByThread())
+ {
+ Thread *pThread = NULL;
+ while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
+ {
+ if (pThread->GetEventPipeBufferList() == pThreadBufferList)
+ {
+ pThread->SetEventPipeBufferList(NULL);
+ break;
+ }
+ }
+
+ // We don't delete buffers themself because they can be in-use
+ delete(pThreadBufferList);
+ }
+
+ pElem = m_pPerThreadBufferList->GetNext(pElem);
+ delete(pCurElem);
+ }
+
+ delete(m_pPerThreadBufferList);
+ m_pPerThreadBufferList = NULL;
+ }
+}
+
EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(Thread *pThread, unsigned int requestSize)
{
CONTRACTL
@@ -217,7 +261,7 @@ void EventPipeBufferManager::DeAllocateBuffer(EventPipeBuffer *pBuffer)
}
}
-bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread, StackContents *pStack)
+bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread, StackContents *pStack)
{
CONTRACTL
{
@@ -276,7 +320,7 @@ bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeEvent &event,
else
{
// Attempt to write the event to the buffer. If this fails, we should allocate a new buffer.
- allocNewBuffer = !pBuffer->WriteEvent(pEventThread, event, pData, length, pActivityId, pRelatedActivityId, pStack);
+ allocNewBuffer = !pBuffer->WriteEvent(pEventThread, event, payload, pActivityId, pRelatedActivityId, pStack);
}
}
@@ -290,7 +334,7 @@ bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeEvent &event,
// However, the GC is waiting on this call to return so that it can make forward progress. Thus it is not safe
// to switch to preemptive mode here.
- unsigned int requestSize = sizeof(EventPipeEventInstance) + length;
+ unsigned int requestSize = sizeof(EventPipeEventInstance) + payload.GetSize();
pBuffer = AllocateBufferForThread(pThread, requestSize);
}
@@ -299,7 +343,7 @@ bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeEvent &event,
// This is the second time if this thread did have one or more buffers, but they were full.
if(allocNewBuffer && pBuffer != NULL)
{
- allocNewBuffer = !pBuffer->WriteEvent(pEventThread, event, pData, length, pActivityId, pRelatedActivityId, pStack);
+ allocNewBuffer = !pBuffer->WriteEvent(pEventThread, event, payload, pActivityId, pRelatedActivityId, pStack);
}
// Mark that the thread is no longer writing an event.
@@ -435,8 +479,15 @@ void EventPipeBufferManager::DeAllocateBuffers()
// In DEBUG, make sure that the element was found and removed.
_ASSERTE(pElem != NULL);
+
+ SListElem<EventPipeBufferList*> *pCurElem = pElem;
+ pElem = m_pPerThreadBufferList->GetNext(pElem);
+ delete(pCurElem);
+ }
+ else
+ {
+ pElem = m_pPerThreadBufferList->GetNext(pElem);
}
- pElem = m_pPerThreadBufferList->GetNext(pElem);
}
// Remove the list reference from the thread.
@@ -482,12 +533,18 @@ void EventPipeBufferManager::DeAllocateBuffers()
pElem = m_pPerThreadBufferList->FindAndRemove(pElem);
_ASSERTE(pElem != NULL);
+ SListElem<EventPipeBufferList*> *pCurElem = pElem;
+ pElem = m_pPerThreadBufferList->GetNext(pElem);
+ delete(pCurElem);
+
// Now that all of the list elements have been freed, free the list itself.
delete(pBufferList);
pBufferList = NULL;
}
-
- pElem = m_pPerThreadBufferList->GetNext(pElem);
+ else
+ {
+ pElem = m_pPerThreadBufferList->GetNext(pElem);
+ }
}
}
diff --git a/src/vm/eventpipebuffermanager.h b/src/vm/eventpipebuffermanager.h
index a53721b7b8..942d4e2242 100644
--- a/src/vm/eventpipebuffermanager.h
+++ b/src/vm/eventpipebuffermanager.h
@@ -7,6 +7,7 @@
#ifdef FEATURE_PERFTRACING
+#include "eventpipe.h"
#include "eventpipefile.h"
#include "eventpipebuffer.h"
#include "spinlock.h"
@@ -61,13 +62,14 @@ private:
public:
EventPipeBufferManager();
+ ~EventPipeBufferManager();
// Write an event to the input thread's current event buffer.
// An optional eventThread can be provided for sample profiler events.
// This is because the thread that writes the events is not the same as the "event thread".
// An optional stack trace can be provided for sample profiler events.
// Otherwise, if a stack trace is needed, one will be automatically collected.
- bool WriteEvent(Thread *pThread, EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread = NULL, StackContents *pStack = NULL);
+ bool WriteEvent(Thread *pThread, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread = NULL, StackContents *pStack = NULL);
// Write the contents of the managed buffers to the specified file.
// The stopTimeStamp is used to determine when tracing was stopped to ensure that we
diff --git a/src/vm/eventpipeconfiguration.cpp b/src/vm/eventpipeconfiguration.cpp
index 0a266e4849..80c4878782 100644
--- a/src/vm/eventpipeconfiguration.cpp
+++ b/src/vm/eventpipeconfiguration.cpp
@@ -10,9 +10,7 @@
#ifdef FEATURE_PERFTRACING
-// {5291C09C-2660-4D6A-83A3-C383FD020DEC}
-const GUID EventPipeConfiguration::s_configurationProviderID =
- { 0x5291c09c, 0x2660, 0x4d6a, { 0x83, 0xa3, 0xc3, 0x83, 0xfd, 0x2, 0xd, 0xec } };
+const WCHAR* EventPipeConfiguration::s_configurationProviderName = W("Microsoft-DotNETCore-EventPipeConfiguration");
EventPipeConfiguration::EventPipeConfiguration()
{
@@ -35,6 +33,12 @@ EventPipeConfiguration::~EventPipeConfiguration()
}
CONTRACTL_END;
+ if(m_pConfigProvider != NULL)
+ {
+ delete(m_pConfigProvider);
+ m_pConfigProvider = NULL;
+ }
+
if(m_pEnabledProviderList != NULL)
{
delete(m_pEnabledProviderList);
@@ -43,6 +47,15 @@ EventPipeConfiguration::~EventPipeConfiguration()
if(m_pProviderList != 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;
+ pElem = m_pProviderList->GetNext(pElem);
+ delete(pCurElem);
+ }
+
delete(m_pProviderList);
m_pProviderList = NULL;
}
@@ -59,7 +72,7 @@ void EventPipeConfiguration::Initialize()
CONTRACTL_END;
// Create the configuration provider.
- m_pConfigProvider = EventPipe::CreateProvider(s_configurationProviderID);
+ m_pConfigProvider = EventPipe::CreateProvider(SL(s_configurationProviderName));
// Create the metadata event.
m_pMetadataEvent = m_pConfigProvider->AddEvent(
@@ -84,7 +97,7 @@ bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
CrstHolder _crst(EventPipe::GetLock());
// See if we've already registered this provider.
- EventPipeProvider *pExistingProvider = GetProviderNoLock(provider.GetProviderID());
+ EventPipeProvider *pExistingProvider = GetProviderNoLock(provider.GetProviderName());
if(pExistingProvider != NULL)
{
return false;
@@ -139,6 +152,7 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider)
{
if(m_pProviderList->FindAndRemove(pElem) != NULL)
{
+ delete(pElem);
return true;
}
}
@@ -146,7 +160,7 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider)
return false;
}
-EventPipeProvider* EventPipeConfiguration::GetProvider(const GUID &providerID)
+EventPipeProvider* EventPipeConfiguration::GetProvider(const SString &providerName)
{
CONTRACTL
{
@@ -160,10 +174,10 @@ EventPipeProvider* EventPipeConfiguration::GetProvider(const GUID &providerID)
// modify the list.
CrstHolder _crst(EventPipe::GetLock());
- return GetProviderNoLock(providerID);
+ return GetProviderNoLock(providerName);
}
-EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const GUID &providerID)
+EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &providerName)
{
CONTRACTL
{
@@ -178,7 +192,7 @@ EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const GUID &provide
while(pElem != NULL)
{
EventPipeProvider *pProvider = pElem->GetValue();
- if(pProvider->GetProviderID() == providerID)
+ if(pProvider->GetProviderName().Equals(providerName))
{
return pProvider;
}
@@ -305,8 +319,8 @@ void EventPipeConfiguration::EnableRundown()
_ASSERTE(m_pEnabledProviderList == NULL);
const unsigned int numRundownProviders = 2;
EventPipeProviderConfiguration rundownProviders[numRundownProviders];
- rundownProviders[0] = EventPipeProviderConfiguration(W("e13c0d23-ccbc-4e12-931b-d9cc2eee27e4"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose)); // Public provider.
- rundownProviders[1] = EventPipeProviderConfiguration(W("a669021c-c450-4609-a035-5af59af4df18"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose)); // Rundown provider.
+ rundownProviders[0] = EventPipeProviderConfiguration(W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose)); // Public provider.
+ rundownProviders[1] = EventPipeProviderConfiguration(W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose)); // Rundown provider.
// Enable rundown.
m_rundownEnabled = true;
@@ -333,12 +347,13 @@ EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPip
// Calculate the size of the event.
EventPipeEvent &sourceEvent = *sourceInstance.GetEvent();
- const GUID &providerID = sourceEvent.GetProvider()->GetProviderID();
+ const SString &providerName = sourceEvent.GetProvider()->GetProviderName();
unsigned int eventID = sourceEvent.GetEventID();
unsigned int eventVersion = sourceEvent.GetEventVersion();
BYTE *pPayloadData = sourceEvent.GetMetadata();
unsigned int payloadLength = sourceEvent.GetMetadataLength();
- unsigned int instancePayloadSize = sizeof(providerID) + sizeof(eventID) + sizeof(eventVersion) + sizeof(payloadLength) + payloadLength;
+ unsigned int providerNameLength = (providerName.GetCount() + 1) * sizeof(WCHAR);
+ unsigned int instancePayloadSize = providerNameLength + sizeof(eventID) + sizeof(eventVersion) + sizeof(payloadLength) + payloadLength;
// Allocate the payload.
BYTE *pInstancePayload = new BYTE[instancePayloadSize];
@@ -347,10 +362,10 @@ EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPip
BYTE *currentPtr = pInstancePayload;
// Write the provider ID.
- memcpy(currentPtr, (BYTE*)&providerID, sizeof(providerID));
- currentPtr += sizeof(providerID);
+ memcpy(currentPtr, (BYTE*)providerName.GetUnicode(), providerNameLength);
+ currentPtr += providerNameLength;
- // Write the event ID.
+ // Write the event name as null-terminated unicode.
memcpy(currentPtr, &eventID, sizeof(eventID));
currentPtr += sizeof(eventID);
@@ -402,9 +417,14 @@ void EventPipeConfiguration::DeleteDeferredProviders()
{
// The act of deleting the provider unregisters it and removes it from the list.
delete(pProvider);
+ SListElem<EventPipeProvider*> *pCurElem = pElem;
+ pElem = m_pProviderList->GetNext(pElem);
+ delete(pCurElem);
+ }
+ else
+ {
+ pElem = m_pProviderList->GetNext(pElem);
}
-
- pElem = m_pProviderList->GetNext(pElem);
}
}
@@ -494,16 +514,7 @@ EventPipeEnabledProvider* EventPipeEnabledProviderList::GetEnabledProvider(
return NULL;
}
- // TEMPORARY: Convert the provider GUID to a string.
- const unsigned int guidSize = 39;
- WCHAR wszProviderID[guidSize];
- if(!StringFromGUID2(pProvider->GetProviderID(), wszProviderID, guidSize))
- {
- wszProviderID[0] = '\0';
- }
-
- // Strip off the {}.
- SString providerNameStr(&wszProviderID[1], guidSize-3);
+ SString providerNameStr = pProvider->GetProviderName();
LPCWSTR providerName = providerNameStr.GetUnicode();
EventPipeEnabledProvider *pEnabledProvider = NULL;
diff --git a/src/vm/eventpipeconfiguration.h b/src/vm/eventpipeconfiguration.h
index aac9bd6065..1d161367b2 100644
--- a/src/vm/eventpipeconfiguration.h
+++ b/src/vm/eventpipeconfiguration.h
@@ -42,7 +42,7 @@ public:
bool UnregisterProvider(EventPipeProvider &provider);
// Get the provider with the specified provider ID if it exists.
- EventPipeProvider* GetProvider(const GUID &providerID);
+ EventPipeProvider* GetProvider(const SString &providerID);
// Get the configured size of the circular buffer.
size_t GetCircularBufferSize() const;
@@ -77,7 +77,7 @@ public:
private:
// Get the provider without taking the lock.
- EventPipeProvider* GetProviderNoLock(const GUID &providerID);
+ EventPipeProvider* GetProviderNoLock(const SString &providerID);
// Determines whether or not the event pipe is enabled.
Volatile<bool> m_enabled;
@@ -98,9 +98,9 @@ private:
// The event used to write event information to the event stream.
EventPipeEvent *m_pMetadataEvent;
- // The provider ID for the configuration event pipe provider.
+ // The provider name for the configuration event pipe provider.
// This provider is used to emit configuration events.
- static const GUID s_configurationProviderID;
+ const static WCHAR* s_configurationProviderName;
// True if rundown is enabled.
Volatile<bool> m_rundownEnabled;
diff --git a/src/vm/eventpipeeventinstance.cpp b/src/vm/eventpipeeventinstance.cpp
index afde2c0547..305b6dac04 100644
--- a/src/vm/eventpipeeventinstance.cpp
+++ b/src/vm/eventpipeeventinstance.cpp
@@ -182,19 +182,11 @@ void EventPipeEventInstance::SerializeToJsonFile(EventPipeJsonFile *pFile)
EX_TRY
{
- const unsigned int guidSize = 39;
- WCHAR wszProviderID[guidSize];
- if(!StringFromGUID2(m_pEvent->GetProvider()->GetProviderID(), wszProviderID, guidSize))
- {
- wszProviderID[0] = '\0';
- }
-
- // Strip off the {}.
StackScratchBuffer scratch;
- SString guidStr(&wszProviderID[1], guidSize-3);
+ SString providerName = m_pEvent->GetProvider()->GetProviderName();
SString message;
- message.Printf("Provider=%s/EventID=%d/Version=%d", guidStr.GetANSI(scratch), m_pEvent->GetEventID(), m_pEvent->GetEventVersion());
+ message.Printf("Provider=%s/EventID=%d/Version=%d", providerName.GetANSI(scratch), m_pEvent->GetEventID(), m_pEvent->GetEventVersion());
pFile->WriteEvent(m_timeStamp, m_threadID, message, m_stackContents);
}
EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
diff --git a/src/vm/eventpipefile.cpp b/src/vm/eventpipefile.cpp
index f574814586..26e04480ee 100644
--- a/src/vm/eventpipefile.cpp
+++ b/src/vm/eventpipefile.cpp
@@ -25,6 +25,9 @@ EventPipeFile::EventPipeFile(
}
CONTRACTL_END;
+ SetObjectVersion(2);
+ SetMinReaderVersion(0);
+
m_pSerializer = new FastSerializer(outputFilePath, *this);
m_serializationLock.Init(LOCK_TYPE_DEFAULT);
m_pMetadataLabels = new MapSHashWithRemove<EventPipeEvent*, StreamLabel>();
diff --git a/src/vm/eventpipeprovider.cpp b/src/vm/eventpipeprovider.cpp
index 896f9b2650..7361541e77 100644
--- a/src/vm/eventpipeprovider.cpp
+++ b/src/vm/eventpipeprovider.cpp
@@ -7,10 +7,11 @@
#include "eventpipeconfiguration.h"
#include "eventpipeevent.h"
#include "eventpipeprovider.h"
+#include "sha1.h"
#ifdef FEATURE_PERFTRACING
-EventPipeProvider::EventPipeProvider(const GUID &providerID, EventPipeCallback pCallbackFunction, void *pCallbackData)
+EventPipeProvider::EventPipeProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
{
CONTRACTL
{
@@ -20,7 +21,7 @@ EventPipeProvider::EventPipeProvider(const GUID &providerID, EventPipeCallback p
}
CONTRACTL_END;
- m_providerID = providerID;
+ m_providerName = providerName;
m_enabled = false;
m_keywords = 0;
m_providerLevel = EventPipeEventLevel::Critical;
@@ -65,7 +66,9 @@ EventPipeProvider::~EventPipeProvider()
EventPipeEvent *pEvent = pElem->GetValue();
delete pEvent;
+ SListElem<EventPipeEvent*> *pCurElem = pElem;
pElem = m_pEventList->GetNext(pElem);
+ delete pCurElem;
}
delete m_pEventList;
@@ -73,11 +76,11 @@ EventPipeProvider::~EventPipeProvider()
}
}
-const GUID& EventPipeProvider::GetProviderID() const
+const SString& EventPipeProvider::GetProviderName() const
{
LIMITED_METHOD_CONTRACT;
- return m_providerID;
+ return m_providerName;
}
bool EventPipeProvider::Enabled() const
@@ -198,7 +201,7 @@ void EventPipeProvider::InvokeCallback()
if(m_pCallbackFunction != NULL && !g_fEEShutDown)
{
(*m_pCallbackFunction)(
- &m_providerID,
+ NULL, /* providerId */
m_enabled,
(UCHAR) m_providerLevel,
m_keywords,
diff --git a/src/vm/eventpipeprovider.h b/src/vm/eventpipeprovider.h
index d2c459ef32..7b92faca72 100644
--- a/src/vm/eventpipeprovider.h
+++ b/src/vm/eventpipeprovider.h
@@ -34,6 +34,9 @@ private:
// The GUID of the provider.
GUID m_providerID;
+ // The name of the provider.
+ SString m_providerName;
+
// True if the provider is enabled.
bool m_enabled;
@@ -61,14 +64,14 @@ private:
bool m_deleteDeferred;
// Private constructor because all providers are created through EventPipe::CreateProvider.
- EventPipeProvider(const GUID &providerID, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL);
+ EventPipeProvider(const SString &providerName, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL);
public:
~EventPipeProvider();
- // Get the provider ID.
- const GUID& GetProviderID() const;
+ // Get the provider Name.
+ const SString& GetProviderName() const;
// Determine if the provider is enabled.
bool Enabled() const;
diff --git a/src/vm/eventtrace.cpp b/src/vm/eventtrace.cpp
index 16f729d505..6325edb462 100644
--- a/src/vm/eventtrace.cpp
+++ b/src/vm/eventtrace.cpp
@@ -6834,7 +6834,7 @@ VOID ETW::MethodLog::SendEventsForJitMethodsHelper(BaseDomain *pDomainFilter,
// manager locks.
// see code:#TableLockHolder
ReJITID rejitID =
- fGetReJitIDs ? pMD->GetReJitManager()->GetReJitIdNoLock(pMD, codeStart) : 0;
+ fGetReJitIDs ? ReJitManager::GetReJitIdNoLock(pMD, codeStart) : 0;
// There are small windows of time where the heap iterator may come across a
// codeStart that is not yet published to the MethodDesc. This may happen if
@@ -6962,8 +6962,8 @@ VOID ETW::MethodLog::SendEventsForJitMethods(BaseDomain *pDomainFilter, LoaderAl
// We only support getting rejit IDs when filtering by domain.
if (pDomainFilter)
{
- ReJitManager::TableLockHolder lkRejitMgrSharedDomain(SharedDomain::GetDomain()->GetReJitManager());
- ReJitManager::TableLockHolder lkRejitMgrModule(pDomainFilter->GetReJitManager());
+ CodeVersionManager::TableLockHolder lkRejitMgrSharedDomain(SharedDomain::GetDomain()->GetCodeVersionManager());
+ CodeVersionManager::TableLockHolder lkRejitMgrModule(pDomainFilter->GetCodeVersionManager());
SendEventsForJitMethodsHelper(pDomainFilter,
pLoaderAllocatorFilter,
dwEventOptions,
diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp
index c6d42eddd7..a52ccd7c2a 100644
--- a/src/vm/exceptionhandling.cpp
+++ b/src/vm/exceptionhandling.cpp
@@ -5186,6 +5186,38 @@ BOOL IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD e
IsIPInMarkedJitHelper(controlPc));
}
+#ifdef _TARGET_ARM_
+static inline BOOL HandleArmSingleStep(PCONTEXT pContext, PEXCEPTION_RECORD pExceptionRecord, Thread *pThread)
+{
+#ifdef __linux__
+ // On ARM Linux exception point to the break instruction,
+ // but the rest of the code expects that it points to an instruction after the break
+ if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
+ {
+ SetIP(pContext, GetIP(pContext) + CORDbg_BREAK_INSTRUCTION_SIZE);
+ pExceptionRecord->ExceptionAddress = (void *)GetIP(pContext);
+ }
+#endif
+ // On ARM we don't have any reliable hardware support for single stepping so it is emulated in software.
+ // The implementation will end up throwing an EXCEPTION_BREAKPOINT rather than an EXCEPTION_SINGLE_STEP
+ // and leaves other aspects of the thread context in an invalid state. Therefore we use this opportunity
+ // to fixup the state before any other part of the system uses it (we do it here since only the debugger
+ // uses single step functionality).
+
+ // First ask the emulation itself whether this exception occurred while single stepping was enabled. If so
+ // it will fix up the context to be consistent again and return true. If so and the exception was
+ // EXCEPTION_BREAKPOINT then we translate it to EXCEPTION_SINGLE_STEP (otherwise we leave it be, e.g. the
+ // instruction stepped caused an access violation).
+ if (pThread->HandleSingleStep(pContext, pExceptionRecord->ExceptionCode) && (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT))
+ {
+ pExceptionRecord->ExceptionCode = EXCEPTION_SINGLE_STEP;
+ pExceptionRecord->ExceptionAddress = (void *)GetIP(pContext);
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif // _TARGET_ARM_
+
BOOL HandleHardwareException(PAL_SEHException* ex)
{
_ASSERTE(IsSafeToHandleHardwareException(ex->GetContextRecord(), ex->GetExceptionRecord()));
@@ -5249,6 +5281,9 @@ BOOL HandleHardwareException(PAL_SEHException* ex)
Thread *pThread = GetThread();
if (pThread != NULL && g_pDebugInterface != NULL)
{
+#ifdef _TARGET_ARM_
+ HandleArmSingleStep(ex->GetContextRecord(), ex->GetExceptionRecord(), pThread);
+#endif
if (ex->GetExceptionRecord()->ExceptionCode == STATUS_BREAKPOINT)
{
// If this is breakpoint context, it is set up to point to an instruction after the break instruction.
diff --git a/src/vm/fastserializableobject.h b/src/vm/fastserializableobject.h
index cbfcfc9f0e..ec162e3cc3 100644
--- a/src/vm/fastserializableobject.h
+++ b/src/vm/fastserializableobject.h
@@ -25,6 +25,41 @@ public:
// Get the type name for the current object.
virtual const char* GetTypeName() = 0;
+
+ int GetObjectVersion() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_objectVersion;
+ }
+
+ int GetMinReaderVersion() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_minReaderVersion;
+ }
+
+protected:
+
+ void SetObjectVersion(int version)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ m_objectVersion = version;
+ }
+
+ void SetMinReaderVersion(int version)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ m_minReaderVersion = version;
+ }
+
+private:
+
+ int m_objectVersion = 1;
+ int m_minReaderVersion = 0;
};
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/fastserializer.cpp b/src/vm/fastserializer.cpp
index 7f9b4e20a6..8e0e0ad768 100644
--- a/src/vm/fastserializer.cpp
+++ b/src/vm/fastserializer.cpp
@@ -226,8 +226,8 @@ void FastSerializer::WriteSerializationType(FastSerializableObject *pObject)
// Write the SerializationType version fields.
int serializationType[2];
- serializationType[0] = 1; // Object Version.
- serializationType[1] = 0; // Minimum Reader Version.
+ serializationType[0] = pObject->GetObjectVersion();
+ serializationType[1] = pObject->GetMinReaderVersion();
WriteBuffer((BYTE*) &serializationType, sizeof(serializationType));
// Write the SerializationType TypeName field.
diff --git a/src/vm/finalizerthread.cpp b/src/vm/finalizerthread.cpp
index 0a4da165a1..3ba3468407 100644
--- a/src/vm/finalizerthread.cpp
+++ b/src/vm/finalizerthread.cpp
@@ -909,7 +909,7 @@ void FinalizerThread::FinalizerThreadCreate()
// actual thread terminates.
GetFinalizerThread()->IncExternalCount();
- if (GetFinalizerThread()->CreateNewThread(0, &FinalizerThreadStart, NULL))
+ if (GetFinalizerThread()->CreateNewThread(0, &FinalizerThreadStart, NULL, W("Finalizer")) )
{
DWORD dwRet = GetFinalizerThread()->StartThread();
diff --git a/src/vm/frames.cpp b/src/vm/frames.cpp
index 86bb97b8c7..d38762b87e 100644
--- a/src/vm/frames.cpp
+++ b/src/vm/frames.cpp
@@ -13,7 +13,6 @@
#include "method.hpp"
#include "class.h"
#include "excep.h"
-#include "security.h"
#include "stublink.h"
#include "fieldmarshaler.h"
#include "siginfo.hpp"
diff --git a/src/vm/gccover.cpp b/src/vm/gccover.cpp
index 3e195796b4..895c176460 100644
--- a/src/vm/gccover.cpp
+++ b/src/vm/gccover.cpp
@@ -160,7 +160,7 @@ void SetupGcCoverage(MethodDesc* pMD, BYTE* methodStartPtr) {
{
BaseDomain* pDomain = pMD->GetDomain();
// Enter the global lock which protects the list of all functions being JITd
- ListLockHolder pJitLock(pDomain->GetJitLock());
+ JitListLock::LockHolder pJitLock(pDomain->GetJitLock());
// It is possible that another thread stepped in before we entered the global lock for the first time.
@@ -175,14 +175,14 @@ void SetupGcCoverage(MethodDesc* pMD, BYTE* methodStartPtr) {
#ifdef _DEBUG
description = pMD->m_pszDebugMethodName;
#endif
- ListLockEntryHolder pEntry(ListLockEntry::Find(pJitLock, pMD, description));
+ ReleaseHolder<JitListLockEntry> pEntry(JitListLockEntry::Find(pJitLock, pMD->GetInitialCodeVersion(), description));
// We have an entry now, we can release the global lock
pJitLock.Release();
// Take the entry lock
{
- ListLockEntryLockHolder pEntryLock(pEntry, FALSE);
+ JitListLockEntry::LockHolder pEntryLock(pEntry, FALSE);
if (pEntryLock.DeadlockAwareAcquire())
{
@@ -1035,6 +1035,10 @@ static SLOT getTargetOfCall(SLOT instrPtr, PCONTEXT regs, SLOT*nextInstr) {
unsigned int regnum = (instrPtr[0] & 0x78) >> 3;
return (BYTE *)getRegVal(regnum, regs);
}
+ else
+ {
+ return 0; // Not a call.
+ }
#elif defined(_TARGET_ARM64_)
if (((*reinterpret_cast<DWORD*>(instrPtr)) & 0xFC000000) == 0x94000000)
{
diff --git a/src/vm/gcenv.ee.cpp b/src/vm/gcenv.ee.cpp
index 6fe9a71def..b135173c0f 100644
--- a/src/vm/gcenv.ee.cpp
+++ b/src/vm/gcenv.ee.cpp
@@ -445,7 +445,7 @@ Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threa
return NULL;
}
- if (threadStubArgs.thread->CreateNewThread(0, (LPTHREAD_START_ROUTINE)BackgroundThreadStub, &threadStubArgs))
+ if (threadStubArgs.thread->CreateNewThread(0, (LPTHREAD_START_ROUTINE)BackgroundThreadStub, &threadStubArgs, W("Background GC")))
{
threadStubArgs.thread->SetBackground (TRUE, FALSE);
threadStubArgs.thread->StartThread();
@@ -861,7 +861,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
#endif
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
- if (args->write_watch_table != nullptr)
+ if (g_sw_ww_enabled_for_gc_heap && (args->write_watch_table != nullptr))
{
assert(args->is_runtime_suspended);
g_sw_ww_table = args->write_watch_table;
@@ -888,6 +888,17 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
g_lowest_address = args->lowest_address;
VolatileStore(&g_highest_address, args->highest_address);
+
+#if defined(_ARM64_)
+ // Need to reupdate for changes to g_highest_address g_lowest_address
+ ::StompWriteBarrierResize(args->is_runtime_suspended, args->requires_upper_bounds_check);
+
+ if(!args->is_runtime_suspended)
+ {
+ // If runtime is not suspended, force updated state to be visible to all threads
+ MemoryBarrier();
+ }
+#endif
return;
case WriteBarrierOp::StompEphemeral:
// StompEphemeral requires a new ephemeral low and a new ephemeral high
@@ -945,6 +956,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
case WriteBarrierOp::SwitchToNonWriteWatch:
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
assert(args->is_runtime_suspended && "the runtime must be suspended here!");
+ g_sw_ww_table = 0;
g_sw_ww_enabled_for_gc_heap = false;
::SwitchToNonWriteWatchBarrier(true);
#else
@@ -1031,7 +1043,7 @@ bool GCToEEInterface::GetBooleanConfigValue(const char* key, bool* value)
if (strcmp(key, "gcConcurrent") == 0)
{
- *value = g_IGCconcurrent != 0;
+ *value = !!g_pConfig->GetGCconcurrent();
return true;
}
diff --git a/src/vm/gchandleutilities.h b/src/vm/gchandleutilities.h
index 762a37b524..08d27141e5 100644
--- a/src/vm/gchandleutilities.h
+++ b/src/vm/gchandleutilities.h
@@ -40,12 +40,13 @@ inline OBJECTREF ObjectFromHandle(OBJECTHANDLE handle)
{
_ASSERTE(handle);
-#ifdef _DEBUG_IMPL
+#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);
OBJECTREF objRef = ObjectToOBJECTREF(*(Object**)handle);
ValidateObjectAndAppDomain(objRef, ADIndex(context));
-#endif // _DEBUG_IMPL
+#endif // defined(_DEBUG_IMPL) && !defined(DACCESS_COMPILE)
// Wrap the raw OBJECTREF and return it
return UNCHECKED_OBJECTREF_TO_OBJECTREF(*PTR_UNCHECKED_OBJECTREF(handle));
diff --git a/src/vm/gdbjit.cpp b/src/vm/gdbjit.cpp
index 1857f60407..9557b0bf3e 100644
--- a/src/vm/gdbjit.cpp
+++ b/src/vm/gdbjit.cpp
@@ -15,6 +15,22 @@
#include "gdbjit.h"
#include "gdbjithelpers.h"
+__declspec(thread) bool tls_isSymReaderInProgress = false;
+
+#ifdef _DEBUG
+static void DumpElf(const char* methodName, const char *addr, size_t size)
+{
+ char dump[1024] = { 0, };
+
+ strcat(dump, methodName);
+ strcat(dump, ".o");
+
+ FILE *f = fopen(dump, "wb");
+ fwrite(addr, sizeof(char), size, f);
+ fclose(f);
+}
+#endif
+
TypeInfoBase*
GetTypeInfoFromTypeHandle(TypeHandle typeHandle,
NotifyGdb::PTK_TypeInfoMap pTypeMap,
@@ -648,46 +664,6 @@ struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 };
// END of GDB JIT interface
-/* Predefined section names */
-const char* SectionNames[] = {
- "",
- ".text",
- ".shstrtab",
- ".debug_str",
- ".debug_abbrev",
- ".debug_info",
- ".debug_pubnames",
- ".debug_pubtypes",
- ".debug_line",
- ".symtab",
- ".strtab"
- /* After the last (.strtab) section zero or more .thunk_* sections are generated.
-
- Each .thunk_* section contains a single .thunk_#.
- These symbols are mapped to methods (or trampolines) called by currently compiled method. */
-};
-
-const int SectionNamesCount = sizeof(SectionNames) / sizeof(SectionNames[0]); // Does not include .thunk_* sections
-
-/* Static data for section headers */
-struct SectionHeader {
- uint32_t m_type;
- uint64_t m_flags;
-} Sections[] = {
- {SHT_NULL, 0},
- {SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR},
- {SHT_STRTAB, 0},
- {SHT_PROGBITS, SHF_MERGE | SHF_STRINGS },
- {SHT_PROGBITS, 0},
- {SHT_PROGBITS, 0},
- {SHT_PROGBITS, 0},
- {SHT_PROGBITS, 0},
- {SHT_PROGBITS, 0},
- {SHT_SYMTAB, 0},
- {SHT_STRTAB, 0},
- {SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR}
-};
-
/* Static data for .debug_str section */
const char* DebugStrings[] = {
"CoreCLR", "" /* module name */, "" /* module path */
@@ -780,6 +756,11 @@ const int AbbrevTableSize = sizeof(AbbrevTable);
#define DWARF_LINE_RANGE 14
#define DWARF_OPCODE_BASE 13
+#ifdef FEATURE_GDBJIT_LANGID_CS
+/* TODO: use corresponding constant when it will be added to llvm */
+#define DW_LANG_MICROSOFT_CSHARP 0x9e57
+#endif
+
DwarfLineNumHeader LineNumHeader = {
0, 2, 0, 1, 1, DWARF_LINE_BASE, DWARF_LINE_RANGE, DWARF_OPCODE_BASE, {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1}
};
@@ -793,7 +774,11 @@ struct __attribute__((packed)) DebugInfoCU
uint32_t m_cu_name;
uint32_t m_line_num;
} debugInfoCU = {
+#ifdef FEATURE_GDBJIT_LANGID_CS
+ 1, 0, DW_LANG_MICROSOFT_CSHARP, 0, 0
+#else
1, 0, DW_LANG_C89, 0, 0
+#endif
};
struct __attribute__((packed)) DebugInfoTryCatchSub
@@ -979,11 +964,14 @@ void TypeDefInfo::DumpStrings(char *ptr, int &offset)
void TypeDefInfo::DumpDebugInfo(char *ptr, int &offset)
{
- if (m_typedef_type_offset != 0)
+ if (m_is_visited && m_base_ptr == ptr)
{
return;
}
+ m_base_ptr = ptr;
+ m_is_visited = true;
+
if (ptr != nullptr)
{
DebugInfoTypeDef buf;
@@ -1054,10 +1042,14 @@ void PrimitiveTypeInfo::DumpStrings(char* ptr, int& offset)
void PrimitiveTypeInfo::DumpDebugInfo(char *ptr, int &offset)
{
- if (m_type_offset != 0)
+ if (m_is_visited && m_base_ptr == ptr)
{
return;
}
+
+ m_base_ptr = ptr;
+ m_is_visited = true;
+
m_typedef_info->DumpDebugInfo(ptr, offset);
if (ptr != nullptr)
@@ -1071,13 +1063,12 @@ void PrimitiveTypeInfo::DumpDebugInfo(char *ptr, int &offset)
memcpy(ptr + offset,
&bufType,
sizeof(DebugInfoType));
- m_type_offset = offset;
+
+ // Replace offset from real type to typedef
+ m_type_offset = m_typedef_info->m_typedef_type_offset;
}
offset += sizeof(DebugInfoType);
- // Replace offset from real type to typedef
- if (ptr != nullptr)
- m_type_offset = m_typedef_info->m_typedef_type_offset;
}
ClassTypeInfo::ClassTypeInfo(TypeHandle typeHandle, int num_members, FunctionMemberPtrArrayHolder &method)
@@ -1146,7 +1137,21 @@ void TypeMember::DumpDebugInfo(char* ptr, int& offset)
void TypeMember::DumpStaticDebugInfo(char* ptr, int& offset)
{
const int ptrSize = sizeof(TADDR);
- int bufSize = 0;
+ const int valueTypeBufSize = ptrSize + 6;
+ const int refTypeBufSize = ptrSize + 2;
+
+ bool isValueType = m_member_type->GetTypeHandle().GetSignatureCorElementType() ==
+ ELEMENT_TYPE_VALUETYPE;
+ int bufSize;
+ if (isValueType)
+ {
+ bufSize = valueTypeBufSize;
+ }
+ else
+ {
+ bufSize = refTypeBufSize;
+ }
+
if (ptr != nullptr)
{
DebugInfoStaticMember memberEntry;
@@ -1157,12 +1162,9 @@ void TypeMember::DumpStaticDebugInfo(char* ptr, int& offset)
// for value type static fields compute address as:
// addr = (*addr+sizeof(OBJECTREF))
- if (m_member_type->GetTypeHandle().GetSignatureCorElementType() ==
- ELEMENT_TYPE_VALUETYPE)
+ if (isValueType)
{
- bufSize = ptrSize + 6;
-
- char buf[ptrSize + 6] = {0};
+ char buf[valueTypeBufSize] = {0};
buf[0] = ptrSize + 5;
buf[1] = DW_OP_addr;
@@ -1180,9 +1182,7 @@ void TypeMember::DumpStaticDebugInfo(char* ptr, int& offset)
}
else
{
- bufSize = ptrSize + 2;
-
- char buf[ptrSize + 2] = {0};
+ char buf[refTypeBufSize] = {0};
buf[0] = ptrSize + 1;
buf[1] = DW_OP_addr;
@@ -1540,10 +1540,14 @@ void RefTypeInfo::DumpStrings(char* ptr, int& offset)
void RefTypeInfo::DumpDebugInfo(char* ptr, int& offset)
{
- if (m_type_offset != 0)
+ if (m_is_visited && m_base_ptr == ptr)
{
return;
}
+
+ m_base_ptr = ptr;
+ m_is_visited = true;
+
m_type_offset = offset;
offset += sizeof(DebugInfoRefType);
m_value_type->DumpDebugInfo(ptr, offset);
@@ -1563,10 +1567,14 @@ void RefTypeInfo::DumpDebugInfo(char* ptr, int& offset)
void NamedRefTypeInfo::DumpDebugInfo(char* ptr, int& offset)
{
- if (m_type_offset != 0)
+ if (m_is_visited && m_base_ptr == ptr)
{
return;
}
+
+ m_base_ptr = ptr;
+ m_is_visited = true;
+
m_type_offset = offset;
offset += sizeof(DebugInfoRefType) + sizeof(DebugInfoTypeDef);
m_value_type->DumpDebugInfo(ptr, offset);
@@ -1593,28 +1601,23 @@ void NamedRefTypeInfo::DumpDebugInfo(char* ptr, int& offset)
void ClassTypeInfo::DumpDebugInfo(char* ptr, int& offset)
{
- if (m_type_offset != 0)
+ if (m_is_visited && m_base_ptr == ptr)
{
return;
}
+ m_base_ptr = ptr;
+ m_is_visited = true;
+
if (m_parent != nullptr)
{
- if (m_parent->m_type_offset == 0)
- {
- m_parent->DumpDebugInfo(ptr, offset);
- }
- else if (RefTypeInfo* m_p = dynamic_cast<RefTypeInfo*>(m_parent))
- {
- if (m_p->m_value_type->m_type_offset == 0)
- m_p->m_value_type->DumpDebugInfo(ptr, offset);
- }
+ m_parent->DumpDebugInfo(ptr, offset);
}
// make sure that types of all members are dumped
for (int i = 0; i < m_num_members; ++i)
{
- if (members[i].m_member_type->m_type_offset == 0 && members[i].m_member_type != this)
+ if (members[i].m_member_type != this)
{
members[i].m_member_type->DumpDebugInfo(ptr, offset);
}
@@ -1678,14 +1681,16 @@ void ClassTypeInfo::DumpDebugInfo(char* ptr, int& offset)
void ArrayTypeInfo::DumpDebugInfo(char* ptr, int& offset)
{
- if (m_type_offset != 0)
+ if (m_is_visited && m_base_ptr == ptr)
{
return;
}
- if (m_elem_type->m_type_offset == 0)
- {
- m_elem_type->DumpDebugInfo(ptr, offset);
- }
+
+ m_base_ptr = ptr;
+ m_is_visited = true;
+
+ m_elem_type->DumpDebugInfo(ptr, offset);
+
if (ptr != nullptr)
{
DebugInfoArrayType arrType;
@@ -1754,11 +1759,12 @@ struct Elf_Symbol {
Elf_Symbol() : m_name(nullptr), m_off(0), m_value(0), m_section(0), m_size(0) {}
};
-static int countFuncs(const SymbolsInfo *lines, int nlines)
+template <class T>
+static int countFuncs(T &arr, int n)
{
int count = 0;
- for (int i = 0; i < nlines; i++) {
- if (lines[i].ilOffset == ICorDebugInfo::PROLOG)
+ for (int i = 0; i < n; i++) {
+ if (arr[i].ilOffset == ICorDebugInfo::PROLOG)
{
count++;
}
@@ -1766,10 +1772,11 @@ static int countFuncs(const SymbolsInfo *lines, int nlines)
return count;
}
-static int getNextPrologueIndex(int from, const SymbolsInfo *lines, int nlines)
+template <class T>
+static int getNextPrologueIndex(int from, T &arr, int n)
{
- for (int i = from; i < nlines; ++i) {
- if (lines[i].ilOffset == ICorDebugInfo::PROLOG)
+ for (int i = from; i < n; ++i) {
+ if (arr[i].ilOffset == ICorDebugInfo::PROLOG)
{
return i;
}
@@ -1777,14 +1784,672 @@ static int getNextPrologueIndex(int from, const SymbolsInfo *lines, int nlines)
return -1;
}
+static inline bool isListedModule(const WCHAR *wszModuleFile)
+{
+ static NewArrayHolder<WCHAR> wszModuleNames = nullptr;
+ static DWORD cBytesNeeded = 0;
+
+ // Get names of interesting modules from environment
+ if (wszModuleNames == nullptr && cBytesNeeded == 0)
+ {
+ DWORD cCharsNeeded = GetEnvironmentVariableW(W("CORECLR_GDBJIT"), NULL, 0);
+
+ if (cCharsNeeded == 0)
+ {
+ cBytesNeeded = 0xffffffff;
+ return false;
+ }
+
+ WCHAR *wszModuleNamesBuf = new WCHAR[cCharsNeeded+1];
+
+ cCharsNeeded = GetEnvironmentVariableW(W("CORECLR_GDBJIT"), wszModuleNamesBuf, cCharsNeeded);
+
+ if (cCharsNeeded == 0)
+ {
+ delete[] wszModuleNamesBuf;
+ cBytesNeeded = 0xffffffff;
+ return false;
+ }
+
+ wszModuleNames = wszModuleNamesBuf;
+ cBytesNeeded = cCharsNeeded + 1;
+ }
+ else if (wszModuleNames == nullptr)
+ {
+ return false;
+ }
+
+ _ASSERTE(wszModuleNames != nullptr && cBytesNeeded > 0);
+
+ BOOL isUserDebug = FALSE;
+
+ NewArrayHolder<WCHAR> wszModuleName = new WCHAR[cBytesNeeded];
+ LPWSTR pComma = wcsstr(wszModuleNames, W(","));
+ LPWSTR tmp = wszModuleNames;
+
+ while (pComma != NULL)
+ {
+ wcsncpy(wszModuleName, tmp, pComma - tmp);
+ wszModuleName[pComma - tmp] = W('\0');
+
+ if (wcscmp(wszModuleName, wszModuleFile) == 0)
+ {
+ isUserDebug = TRUE;
+ break;
+ }
+ tmp = pComma + 1;
+ pComma = wcsstr(tmp, W(","));
+ }
+ if (isUserDebug == FALSE)
+ {
+ wcsncpy(wszModuleName, tmp, wcslen(tmp));
+ wszModuleName[wcslen(tmp)] = W('\0');
+ if (wcscmp(wszModuleName, wszModuleFile) == 0)
+ {
+ isUserDebug = TRUE;
+ }
+ }
+
+ return isUserDebug;
+}
+
static NotifyGdb::AddrSet codeAddrs;
+class Elf_SectionTracker
+{
+ private:
+ unsigned int m_Flag;
+
+ private:
+ NewArrayHolder<char> m_NamePtr;
+ unsigned int m_NameLen;
+
+ private:
+ unsigned int m_Ind;
+ unsigned int m_Off;
+ unsigned int m_Len;
+
+ private:
+ Elf_Shdr m_Hdr;
+
+ private:
+ Elf_SectionTracker *m_Next;
+
+ public:
+ Elf_SectionTracker(const char *name, unsigned ind, unsigned off, uint32_t type, uint64_t flags);
+ ~Elf_SectionTracker();
+
+ public:
+ bool NeedHeaderUpdate() const;
+ void DisableHeaderUpdate();
+
+ public:
+ unsigned int GetIndex() const { return m_Ind; }
+ unsigned int GetOffset() const { return m_Off; }
+ unsigned int GetSize() const { return m_Len; }
+
+ public:
+ const char *GetName() const { return m_NamePtr; }
+ unsigned int GetNameLen() const { return m_NameLen; }
+
+ public:
+ Elf_SectionTracker *GetNext(void);
+ void SetNext(Elf_SectionTracker *next);
+
+ public:
+ void Forward(unsigned int len);
+
+ public:
+ Elf_Shdr *Header(void);
+ const Elf_Shdr *Header(void) const;
+
+};
+
+Elf_SectionTracker::Elf_SectionTracker(const char *name,
+ unsigned ind, unsigned off,
+ uint32_t type, uint64_t flags)
+ : m_Flag(0),
+ m_NamePtr(nullptr),
+ m_NameLen(0),
+ m_Ind(ind),
+ m_Off(off),
+ m_Len(0),
+ m_Next(nullptr)
+{
+ if (name)
+ {
+ unsigned int len = strlen(name);
+ char *ptr = new char[len + 1];
+
+ strncpy(ptr, name, len + 1);
+
+ m_NamePtr = ptr;
+ m_NameLen = len;
+ }
+
+ m_Hdr.sh_type = type;
+ m_Hdr.sh_flags = flags;
+ m_Hdr.sh_name = 0;
+ m_Hdr.sh_addr = 0;
+ m_Hdr.sh_offset = 0;
+ m_Hdr.sh_size = 0;
+ m_Hdr.sh_link = SHN_UNDEF;
+ m_Hdr.sh_info = 0;
+ m_Hdr.sh_addralign = 1;
+ m_Hdr.sh_entsize = 0;
+}
+
+Elf_SectionTracker::~Elf_SectionTracker()
+{
+}
+
+#define ESTF_NO_HEADER_UPDATE 0x00000001
+
+bool Elf_SectionTracker::NeedHeaderUpdate() const
+{
+ return !(m_Flag & ESTF_NO_HEADER_UPDATE);
+}
+
+void Elf_SectionTracker::DisableHeaderUpdate()
+{
+ m_Flag |= ESTF_NO_HEADER_UPDATE;
+}
+
+void Elf_SectionTracker::Forward(unsigned int len)
+{
+ m_Len += len;
+}
+
+void Elf_SectionTracker::SetNext(Elf_SectionTracker *next)
+{
+ m_Next = next;
+}
+
+Elf_SectionTracker *Elf_SectionTracker::GetNext(void)
+{
+ return m_Next;
+}
+
+Elf_Shdr *Elf_SectionTracker::Header(void)
+{
+ return &m_Hdr;
+}
+
+const Elf_Shdr *Elf_SectionTracker::Header(void) const
+{
+ return &m_Hdr;
+}
+
+class Elf_Buffer
+{
+ private:
+ NewArrayHolder<char> m_Ptr;
+ unsigned int m_Len;
+ unsigned int m_Pos;
+
+ public:
+ Elf_Buffer(unsigned int len);
+
+ private:
+ char *Ensure(unsigned int len);
+ void Forward(unsigned int len);
+
+ public:
+ unsigned int GetPos() const
+ {
+ return m_Pos;
+ }
+
+ char *GetPtr(unsigned int off = 0)
+ {
+ return m_Ptr.GetValue() + off;
+ }
+
+ public:
+ char *Reserve(unsigned int len);
+ template <typename T> T *ReserveT(unsigned int len = sizeof(T))
+ {
+ _ASSERTE(len >= sizeof(T));
+ return reinterpret_cast<T *>(Reserve(len));
+ }
+
+ public:
+ void Append(const char *src, unsigned int len);
+ template <typename T> void AppendT(T *src)
+ {
+ Append(reinterpret_cast<const char *>(src), sizeof(T));
+ }
+};
+
+Elf_Buffer::Elf_Buffer(unsigned int len)
+ : m_Ptr(new char[len])
+ , m_Len(len)
+ , m_Pos(0)
+{
+}
+
+char *Elf_Buffer::Ensure(unsigned int len)
+{
+ bool bAdjusted = false;
+
+ while (m_Pos + len > m_Len)
+ {
+ m_Len *= 2;
+ bAdjusted = true;
+ }
+
+ if (bAdjusted)
+ {
+ char *ptr = new char [m_Len * 2];
+ memcpy(ptr, m_Ptr.GetValue(), m_Pos);
+ m_Ptr = ptr;
+ }
+
+ return GetPtr(m_Pos);
+}
+
+void Elf_Buffer::Forward(unsigned int len)
+{
+ m_Pos += len;
+}
+
+char *Elf_Buffer::Reserve(unsigned int len)
+{
+ char *ptr = Ensure(len);
+ Forward(len);
+ return ptr;
+}
+
+void Elf_Buffer::Append(const char *src, unsigned int len)
+{
+ char *dst = Reserve(len);
+ memcpy(dst, src, len);
+}
+
+#define ELF_BUILDER_TEXT_SECTION_INDEX 1
+
+class Elf_Builder
+{
+ private:
+ Elf_Buffer m_Buffer;
+
+ private:
+ unsigned int m_SectionCount;
+ Elf_SectionTracker *m_First;
+ Elf_SectionTracker *m_Last;
+ Elf_SectionTracker *m_Curr;
+
+ public:
+ Elf_Builder();
+ ~Elf_Builder();
+
+ public:
+ unsigned int GetSectionCount(void) { return m_SectionCount; }
+
+ public:
+ void Initialize(PCODE codePtr, TADDR codeLen);
+
+ public:
+ Elf_SectionTracker *OpenSection(const char *name, uint32_t type, uint64_t flags);
+ void CloseSection();
+
+ public:
+ char *Reserve(unsigned int len);
+ template <typename T> T *ReserveT(unsigned int len = sizeof(T))
+ {
+ _ASSERTE(len >= sizeof(T));
+ return reinterpret_cast<T *>(Reserve(len));
+ }
+
+ public:
+ void Append(const char *src, unsigned int len);
+ template <typename T> void AppendT(T *src)
+ {
+ Append(reinterpret_cast<const char *>(src), sizeof(T));
+ }
+
+ public:
+ void Finalize(void);
+
+ public:
+ char *Export(size_t *len);
+};
+
+Elf_Builder::Elf_Builder()
+ : m_Buffer(128),
+ m_SectionCount(0),
+ m_First(nullptr),
+ m_Last(nullptr),
+ m_Curr(nullptr)
+{
+}
+
+Elf_Builder::~Elf_Builder()
+{
+ Elf_SectionTracker *curr = m_First;
+
+ while (curr)
+ {
+ Elf_SectionTracker *next = curr->GetNext();
+ delete curr;
+ curr = next;
+ }
+}
+
+void Elf_Builder::Initialize(PCODE codePtr, TADDR codeLen)
+{
+ //
+ // Reserve ELF Header
+ //
+ m_Buffer.Reserve(sizeof(Elf_Ehdr));
+
+ //
+ // Create NULL section
+ //
+ Elf_SectionTracker *null = OpenSection("", SHT_NULL, 0);
+ {
+ null->DisableHeaderUpdate();
+ null->Header()->sh_addralign = 0;
+ }
+ CloseSection();
+
+ //
+ // Create '.text' section
+ //
+ Elf_SectionTracker *text = OpenSection(".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
+ {
+ text->DisableHeaderUpdate();
+ text->Header()->sh_addr = codePtr;
+ text->Header()->sh_size = codeLen;
+
+ _ASSERTE(text->GetIndex() == ELF_BUILDER_TEXT_SECTION_INDEX);
+ }
+ CloseSection();
+}
+
+char *Elf_Builder::Reserve(unsigned int len)
+{
+ _ASSERTE(m_Curr != nullptr && "Section should be opened before");
+ char *ptr = m_Buffer.Reserve(len);
+ m_Curr->Forward(len);
+ return ptr;
+}
+
+void Elf_Builder::Append(const char *src, unsigned int len)
+{
+ _ASSERTE(m_Curr != nullptr && "Section should be opened before");
+ char *dst = Reserve(len);
+ memcpy(dst, src, len);
+}
+
+Elf_SectionTracker *Elf_Builder::OpenSection(const char *name, uint32_t type, uint64_t flags)
+{
+ _ASSERTE(m_Curr == nullptr && "Section should be closed before");
+
+ Elf_SectionTracker *next = new Elf_SectionTracker(name, m_SectionCount, m_Buffer.GetPos(), type, flags);
+
+ if (m_First == NULL)
+ {
+ m_First = next;
+ }
+
+ if (m_Last != NULL)
+ {
+ m_Last->SetNext(next);
+ }
+
+ m_SectionCount++;
+
+ m_Last = next;
+ m_Curr = next;
+
+ return next;
+}
+
+void Elf_Builder::CloseSection()
+{
+ _ASSERTE(m_Curr != nullptr && "Section should be opened before");
+ m_Curr = nullptr;
+}
+
+char *Elf_Builder::Export(size_t *pLen)
+{
+ unsigned int len = m_Buffer.GetPos();
+ const char *src = m_Buffer.GetPtr();
+ char *dst = new char[len];
+
+ memcpy(dst, src, len);
+
+ if (pLen)
+ {
+ *pLen = len;
+ }
+
+ return dst;
+}
+
+void Elf_Builder::Finalize()
+{
+ //
+ // Create '.shstrtab'
+ //
+ Elf_SectionTracker *shstrtab = OpenSection(".shstrtab", SHT_STRTAB, 0);
+ {
+ Elf_SectionTracker *curr = m_First;
+
+ while (curr)
+ {
+ unsigned int off = shstrtab->GetSize();
+ unsigned int len = curr->GetNameLen();
+
+ char *dst = Reserve(len + 1);
+ memcpy(dst, curr->GetName(), len);
+ dst[len] = '\0';
+
+ curr->Header()->sh_name = off;
+
+ curr = curr->GetNext();
+ }
+ }
+ CloseSection();
+
+ //
+ // Create Section Header(s) Table
+ //
+ unsigned int shtOffset = m_Buffer.GetPos();
+ {
+ Elf_SectionTracker *curr = m_First;
+
+ while (curr)
+ {
+ if (curr->NeedHeaderUpdate())
+ {
+ curr->Header()->sh_offset = curr->GetOffset();
+ curr->Header()->sh_size = curr->GetSize();
+ }
+ m_Buffer.AppendT(curr->Header());
+ curr = curr->GetNext();
+ }
+ }
+
+ //
+ // Update ELF Header
+ //
+ Elf_Ehdr *elfHeader = new (m_Buffer.GetPtr()) Elf_Ehdr;
+
+#ifdef _TARGET_ARM_
+ elfHeader->e_flags = EF_ARM_EABI_VER5;
+#ifdef ARM_SOFTFP
+ elfHeader->e_flags |= EF_ARM_SOFT_FLOAT;
+#else
+ elfHeader->e_flags |= EF_ARM_VFP_FLOAT;
+#endif
+#endif
+ elfHeader->e_shoff = shtOffset;
+ elfHeader->e_shentsize = sizeof(Elf_Shdr);
+ elfHeader->e_shnum = m_SectionCount;
+ elfHeader->e_shstrndx = shstrtab->GetIndex();
+}
+
+#ifdef FEATURE_GDBJIT_FRAME
+struct __attribute__((packed)) Length
+{
+ UINT32 value;
+
+ Length &operator=(UINT32 n)
+ {
+ value = n;
+ return *this;
+ }
+
+ Length()
+ {
+ value = 0;
+ }
+};
+
+struct __attribute__((packed)) CIE
+{
+ Length length;
+ UINT32 id;
+ UINT8 version;
+ UINT8 augmentation;
+ UINT8 code_alignment_factor;
+ INT8 data_alignment_factor;
+ UINT8 return_address_register;
+ UINT8 instructions[0];
+};
+
+struct __attribute__((packed)) FDE
+{
+ Length length;
+ UINT32 cie;
+ PCODE initial_location;
+ TADDR address_range;
+ UINT8 instructions[0];
+};
+
+static void BuildDebugFrame(Elf_Builder &elfBuilder, PCODE pCode, TADDR codeSize)
+{
+#if defined(_TARGET_ARM_)
+ const unsigned int code_alignment_factor = 2;
+ const int data_alignment_factor = -4;
+
+ UINT8 cieCode[] = {
+ // DW_CFA_def_cfa 13[sp], 0
+ 0x0c, 0x0d, 0x00,
+ };
+
+ UINT8 fdeCode[] = {
+ // DW_CFA_advance_loc 1
+ 0x02, 0x01,
+ // DW_CFA_def_cfa_offset 8
+ 0x0e, 0x08,
+ // DW_CFA_offset 11(r11), -8(= -4 * 2)
+ (0x02 << 6) | 0x0b, 0x02,
+ // DW_CFA_offset 14(lr), -4(= -4 * 1)
+ (0x02 << 6) | 0x0e, 0x01,
+ // DW_CFA_def_cfa_register 11(r11)
+ 0x0d, 0x0b,
+ };
+#elif defined(_TARGET_X86_)
+ const unsigned int code_alignment_factor = 1;
+ const int data_alignment_factor = -4;
+
+ UINT8 cieCode[] = {
+ // DW_CFA_def_cfa 4(esp), 4
+ 0x0c, 0x04, 0x04,
+ // DW_CFA_offset 8(eip), -4(= -4 * 1)
+ (0x02 << 6) | 0x08, 0x01,
+ };
+
+ UINT8 fdeCode[] = {
+ // DW_CFA_advance_loc 1
+ 0x02, 0x01,
+ // DW_CFA_def_cfa_offset 8
+ 0x0e, 0x08,
+ // DW_CFA_offset 5(ebp), -8(= -4 * 2)
+ (0x02 << 6) | 0x05, 0x02,
+ // DW_CFA_def_cfa_register 5(ebp)
+ 0x0d, 0x05,
+ };
+#elif defined(_TARGET_AMD64_)
+ const unsigned int code_alignment_factor = 1;
+ const int data_alignment_factor = -8;
+
+ UINT8 cieCode[] = {
+ // DW_CFA_def_cfa 7(rsp), 8
+ 0x0c, 0x07, 0x08,
+ // DW_CFA_offset 16, -16 (= -8 * 2)
+ (0x02 << 6) | 0x10, 0x01,
+ };
+
+ UINT8 fdeCode[] = {
+ // DW_CFA_advance_loc(1)
+ 0x02, 0x01,
+ // DW_CFA_def_cfa_offset(16)
+ 0x0e, 0x10,
+ // DW_CFA_offset 6, -16 (= -8 * 2)
+ (0x02 << 6) | 0x06, 0x02,
+ // DW_CFA_def_cfa_register(6)
+ 0x0d, 0x06,
+ };
+#else
+#error "Unsupported architecture"
+#endif
+
+ elfBuilder.OpenSection(".debug_frame", SHT_PROGBITS, 0);
+
+ //
+ // Common Information Entry
+ //
+ int cieLen = ALIGN_UP(sizeof(CIE) + sizeof(cieCode), ADDRESS_SIZE) + sizeof(Length);
+
+ CIE *pCIE = elfBuilder.ReserveT<CIE>(cieLen);
+
+ memset(pCIE, 0, cieLen);
+
+ pCIE->length = cieLen - sizeof(Length);
+ pCIE->id = 0xffffffff;
+ pCIE->version = 3;
+ pCIE->augmentation = 0;
+ Leb128Encode(code_alignment_factor, reinterpret_cast<char *>(&pCIE->code_alignment_factor), 1);
+ Leb128Encode(data_alignment_factor, reinterpret_cast<char *>(&pCIE->data_alignment_factor), 1);
+
+ pCIE->return_address_register = 0;
+
+ memcpy(&pCIE->instructions, cieCode, sizeof(cieCode));
+
+ //
+ // Frame Description Entry
+ //
+ int fdeLen = ALIGN_UP((sizeof(FDE) + sizeof(fdeCode)), ADDRESS_SIZE) + sizeof(Length);
+
+ FDE *pFDE = elfBuilder.ReserveT<FDE>(fdeLen);
+
+ memset(pFDE, 0, fdeLen);
+
+ pFDE->length = fdeLen - sizeof(Length);
+ pFDE->cie = 0;
+ pFDE->initial_location = pCode;
+ pFDE->address_range = codeSize;
+ memcpy(&pFDE->instructions, fdeCode, sizeof(fdeCode));
+
+ elfBuilder.CloseSection();
+}
+#endif // FEATURE_GDBJIT_FRAME
+
/* Create ELF/DWARF debug info for jitted method */
-void NotifyGdb::MethodCompiled(MethodDesc* methodDescPtr)
+void NotifyGdb::MethodPrepared(MethodDesc* methodDescPtr)
{
EX_TRY
{
- NotifyGdb::OnMethodCompiled(methodDescPtr);
+ if (!tls_isSymReaderInProgress)
+ {
+ tls_isSymReaderInProgress = true;
+ NotifyGdb::OnMethodPrepared(methodDescPtr);
+ tls_isSymReaderInProgress = false;
+ }
}
EX_CATCH
{
@@ -1792,21 +2457,19 @@ void NotifyGdb::MethodCompiled(MethodDesc* methodDescPtr)
EX_END_CATCH(SwallowAllExceptions);
}
-void NotifyGdb::OnMethodCompiled(MethodDesc* methodDescPtr)
+void NotifyGdb::OnMethodPrepared(MethodDesc* methodDescPtr)
{
- int symbolCount = 0;
- NewArrayHolder<Elf_Symbol> symbolNames;
-
PCODE pCode = methodDescPtr->GetNativeCode();
if (pCode == NULL)
return;
- unsigned int symInfoLen = 0;
- NewArrayHolder<SymbolsInfo> symInfo = nullptr;
- LocalsInfo locals;
/* Get method name & size of jitted code */
- LPCUTF8 methodName = methodDescPtr->GetName();
EECodeInfo codeInfo(pCode);
+ if (!codeInfo.IsValid())
+ {
+ return;
+ }
+
TADDR codeSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfoToken());
pCode = PCODEToPINSTR(pCode);
@@ -1829,67 +2492,195 @@ void NotifyGdb::OnMethodCompiled(MethodDesc* methodDescPtr)
if (length == 0)
return;
- static NewArrayHolder<WCHAR> wszModuleNames = nullptr;
- DWORD cCharsNeeded = 0;
+ bool bNotify = false;
- // Get names of interesting modules from environment
- if (wszModuleNames == nullptr)
+ Elf_Builder elfBuilder;
+
+ elfBuilder.Initialize(pCode, codeSize);
+
+#ifdef FEATURE_GDBJIT_FRAME
+ if (g_pConfig->ShouldEmitDebugFrame())
{
- cCharsNeeded = GetEnvironmentVariableW(W("CORECLR_GDBJIT"), NULL, 0);
+ bool bEmitted = EmitFrameInfo(elfBuilder, pCode, codeSize);
+ bNotify = bNotify || bEmitted;
+ }
+#endif
- if(cCharsNeeded == 0)
- return;
- wszModuleNames = new WCHAR[cCharsNeeded+1];
- cCharsNeeded = GetEnvironmentVariableW(W("CORECLR_GDBJIT"), wszModuleNames, cCharsNeeded);
- if(cCharsNeeded == 0)
- return;
+ if (isListedModule(wszModuleFile))
+ {
+ bool bEmitted = EmitDebugInfo(elfBuilder, methodDescPtr, pCode, codeSize, szModuleFile);
+ bNotify = bNotify || bEmitted;
}
+#ifdef FEATURE_GDBJIT_SYMTAB
else
{
- cCharsNeeded = wcslen(wszModuleNames);
+ bool bEmitted = EmitSymtab(elfBuilder, methodDescPtr, pCode, codeSize);
+ bNotify = bNotify || bEmitted;
}
+#endif
- BOOL isUserDebug = FALSE;
+ if (!bNotify)
+ {
+ return;
+ }
- NewArrayHolder<WCHAR> wszModuleName = new WCHAR[cCharsNeeded+1];
- LPWSTR pComma = wcsstr(wszModuleNames, W(","));
- LPWSTR tmp = wszModuleNames;
+ elfBuilder.Finalize();
- while (pComma != NULL)
+ char *symfile_addr = NULL;
+ size_t symfile_size = 0;
+
+ symfile_addr = elfBuilder.Export(&symfile_size);
+
+#ifdef _DEBUG
+ LPCUTF8 methodName = methodDescPtr->GetName();
+
+ if (g_pConfig->ShouldDumpElfOnMethod(methodName))
{
- wcsncpy(wszModuleName, tmp, pComma - tmp);
- wszModuleName[pComma - tmp] = W('\0');
+ DumpElf(methodName, symfile_addr, symfile_size);
+ }
+#endif
- if (wcscmp(wszModuleName, wszModuleFile) == 0)
- {
- isUserDebug = TRUE;
- break;
- }
- tmp = pComma + 1;
- pComma = wcsstr(tmp, W(","));
+ /* Create GDB JIT structures */
+ NewHolder<jit_code_entry> jit_symbols = new jit_code_entry;
+
+ /* Fill the new entry */
+ jit_symbols->next_entry = jit_symbols->prev_entry = 0;
+ jit_symbols->symfile_addr = symfile_addr;
+ jit_symbols->symfile_size = symfile_size;
+
+ /* Link into list */
+ jit_code_entry *head = __jit_debug_descriptor.first_entry;
+ __jit_debug_descriptor.first_entry = jit_symbols;
+ if (head != 0)
+ {
+ jit_symbols->next_entry = head;
+ head->prev_entry = jit_symbols;
}
- if (isUserDebug == FALSE)
+
+ jit_symbols.SuppressRelease();
+
+ /* Notify the debugger */
+ __jit_debug_descriptor.relevant_entry = jit_symbols;
+ __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
+ __jit_debug_register_code();
+}
+
+#ifdef FEATURE_GDBJIT_FRAME
+bool NotifyGdb::EmitFrameInfo(Elf_Builder &elfBuilder, PCODE pCode, TADDR codeSize)
+{
+ BuildDebugFrame(elfBuilder, pCode, codeSize);
+ return true;
+}
+#endif // FEATURE_GDBJIT_FRAME
+
+#ifdef FEATURE_GDBJIT_SYMTAB
+bool NotifyGdb::EmitSymtab(Elf_Builder &elfBuilder, MethodDesc* methodDescPtr, PCODE pCode, TADDR codeSize)
+{
+ NewArrayHolder<DebuggerILToNativeMap> map = nullptr;
+ NewArrayHolder<Elf_Symbol> symbols = nullptr;
+ NewArrayHolder<NewArrayHolder<char>> symbolNames = nullptr;
+
+ ULONG32 numMap;
+ int symbolCount;
+
+ LPCUTF8 methodName = methodDescPtr->GetName();
+
+ if (GetMethodNativeMap(methodDescPtr, &numMap, map, NULL, NULL) == S_OK)
{
- wcsncpy(wszModuleName, tmp, wcslen(tmp));
- wszModuleName[wcslen(tmp)] = W('\0');
- if (wcscmp(wszModuleName, wszModuleFile) == 0)
+ int methodCount = countFuncs(map, numMap);
+ symbolCount = methodCount + 1;
+ symbols = new Elf_Symbol[symbolCount];
+
+ if (methodCount > 1)
+ symbolNames = new NewArrayHolder<char>[methodCount - 1];
+
+ int startIndex = getNextPrologueIndex(0, map, numMap);
+
+ int methodNameSize = strlen(methodName) + 10;
+
+ for (int i = 1; i < symbolCount; ++i)
{
- isUserDebug = TRUE;
+ int endIndex = getNextPrologueIndex(startIndex + 1, map, numMap);
+
+ PCODE methodStart = map[startIndex].nativeStartOffset;
+ TADDR methodSize = endIndex == -1 ? codeSize - methodStart : map[endIndex].nativeStartOffset - methodStart;
+
+ if (i == 1)
+ {
+ symbols[i].m_name = methodName;
+ }
+ else
+ {
+ int symbolNameIndex = i - 2;
+ symbolNames[symbolNameIndex] = new char[methodNameSize];
+ sprintf_s(symbolNames[symbolNameIndex], methodNameSize, "%s_%d", methodName, symbolNameIndex + 1);
+ symbols[i].m_name = symbolNames[symbolNameIndex];
+ }
+
+ symbols[i].m_value = pCode + methodStart;
+ symbols[i].m_size = methodSize;
+
+ startIndex = endIndex;
}
}
+ else
+ {
+ symbolCount = 2;
+ symbols = new Elf_Symbol[symbolCount];
- if (isUserDebug == FALSE)
+ symbols[1].m_name = methodName;
+ symbols[1].m_value = pCode;
+ symbols[1].m_size = codeSize;
+ }
+
+ symbols[0].m_name = "";
+
+ MemBuf sectSymTab, sectStrTab;
+
+ if (!BuildStringTableSection(sectStrTab, symbols, symbolCount))
{
- return;
+ return false;
+ }
+
+ if (!BuildSymbolTableSection(sectSymTab, pCode, codeSize, symbolCount - 1, symbols, symbolCount, 0))
+ {
+ return false;
}
+ Elf_SectionTracker *strtab = elfBuilder.OpenSection(".strtab", SHT_STRTAB, 0);
+ elfBuilder.Append(sectStrTab.MemPtr, sectStrTab.MemSize);
+ elfBuilder.CloseSection();
+
+ Elf_SectionTracker *symtab = elfBuilder.OpenSection(".symtab", SHT_SYMTAB, 0);
+ elfBuilder.Append(sectSymTab.MemPtr, sectSymTab.MemSize);
+ symtab->Header()->sh_link = strtab->GetIndex();
+ symtab->Header()->sh_entsize = sizeof(Elf_Sym);
+ elfBuilder.CloseSection();
+
+ return true;
+}
+#endif // FEATURE_GDBJIT_SYMTAB
+
+bool NotifyGdb::EmitDebugInfo(Elf_Builder &elfBuilder, MethodDesc* methodDescPtr, PCODE pCode, TADDR codeSize, const char *szModuleFile)
+{
+ unsigned int thunkIndexBase = elfBuilder.GetSectionCount();
+
+ LPCUTF8 methodName = methodDescPtr->GetName();
+
+ int symbolCount = 0;
+ NewArrayHolder<Elf_Symbol> symbolNames;
+
+ unsigned int symInfoLen = 0;
+ NewArrayHolder<SymbolsInfo> symInfo = nullptr;
+ LocalsInfo locals;
+
NewHolder<TK_TypeInfoMap> pTypeMap = new TK_TypeInfoMap();
/* Get debug info for method from portable PDB */
HRESULT hr = GetDebugInfoFromPDB(methodDescPtr, symInfo, symInfoLen, locals);
if (FAILED(hr) || symInfoLen == 0)
{
- return;
+ return false;
}
int method_count = countFuncs(symInfo, symInfoLen);
@@ -1900,7 +2691,7 @@ void NotifyGdb::OnMethodCompiled(MethodDesc* methodDescPtr)
/* Collect addresses of thunks called by method */
if (!CollectCalledMethods(pCalledMethods, (TADDR)methodDescPtr->GetNativeCode(), method, symbolNames, symbolCount))
{
- return;
+ return false;
}
pCH->SetCalledMethods(NULL);
@@ -1916,7 +2707,7 @@ void NotifyGdb::OnMethodCompiled(MethodDesc* methodDescPtr)
if (firstLineIndex >= symInfoLen)
{
- return;
+ return false;
}
int start_index = getNextPrologueIndex(0, symInfo, symInfoLen);
@@ -1950,19 +2741,19 @@ void NotifyGdb::OnMethodCompiled(MethodDesc* methodDescPtr)
start_index = end_index;
}
- MemBuf elfHeader, sectHeaders, sectStr, sectSymTab, sectStrTab, dbgInfo, dbgAbbrev, dbgPubname, dbgPubType, dbgLine,
- dbgStr, elfFile;
+ MemBuf sectSymTab, sectStrTab, dbgInfo, dbgAbbrev, dbgPubname, dbgPubType, dbgLine,
+ dbgStr;
/* Build .debug_abbrev section */
if (!BuildDebugAbbrev(dbgAbbrev))
{
- return;
+ return false;
}
/* Build .debug_line section */
if (!BuildLineTable(dbgLine, pCode, codeSize, symInfo, symInfoLen))
{
- return;
+ return false;
}
DebugStrings[1] = szModuleFile;
@@ -1970,13 +2761,13 @@ void NotifyGdb::OnMethodCompiled(MethodDesc* methodDescPtr)
/* Build .debug_str section */
if (!BuildDebugStrings(dbgStr, pTypeMap, method))
{
- return;
+ return false;
}
/* Build .debug_info section */
if (!BuildDebugInfo(dbgInfo, pTypeMap, method))
{
- return;
+ return false;
}
for (int i = 0; i < method.GetCount(); ++i)
@@ -1988,13 +2779,13 @@ void NotifyGdb::OnMethodCompiled(MethodDesc* methodDescPtr)
/* Build .debug_pubname section */
if (!BuildDebugPub(dbgPubname, methodName, dbgInfo.MemSize, 0x28))
{
- return;
+ return false;
}
/* Build debug_pubtype section */
if (!BuildDebugPub(dbgPubType, "int", dbgInfo.MemSize, 0x1a))
{
- return;
+ return false;
}
/* Build .strtab section */
@@ -2008,158 +2799,64 @@ void NotifyGdb::OnMethodCompiled(MethodDesc* methodDescPtr)
}
if (!BuildStringTableSection(sectStrTab, symbolNames, symbolCount))
{
- return;
+ return false;
}
/* Build .symtab section */
- if (!BuildSymbolTableSection(sectSymTab, pCode, codeSize, method, symbolNames, symbolCount))
+ if (!BuildSymbolTableSection(sectSymTab, pCode, codeSize, method.GetCount(), symbolNames, symbolCount, thunkIndexBase))
{
- return;
+ return false;
}
- /* Build section headers table and section names table */
- BuildSectionTables(sectHeaders, sectStr, method, symbolCount);
-
- /* Patch section offsets & sizes */
- long offset = sizeof(Elf_Ehdr);
- Elf_Shdr* pShdr = reinterpret_cast<Elf_Shdr*>(sectHeaders.MemPtr.GetValue());
- ++pShdr; // .text
- pShdr->sh_addr = pCode;
- pShdr->sh_size = codeSize;
- ++pShdr; // .shstrtab
- pShdr->sh_offset = offset;
- pShdr->sh_size = sectStr.MemSize;
- offset += sectStr.MemSize;
- ++pShdr; // .debug_str
- pShdr->sh_offset = offset;
- pShdr->sh_size = dbgStr.MemSize;
- offset += dbgStr.MemSize;
- ++pShdr; // .debug_abbrev
- pShdr->sh_offset = offset;
- pShdr->sh_size = dbgAbbrev.MemSize;
- offset += dbgAbbrev.MemSize;
- ++pShdr; // .debug_info
- pShdr->sh_offset = offset;
- pShdr->sh_size = dbgInfo.MemSize;
- offset += dbgInfo.MemSize;
- ++pShdr; // .debug_pubnames
- pShdr->sh_offset = offset;
- pShdr->sh_size = dbgPubname.MemSize;
- offset += dbgPubname.MemSize;
- ++pShdr; // .debug_pubtypes
- pShdr->sh_offset = offset;
- pShdr->sh_size = dbgPubType.MemSize;
- offset += dbgPubType.MemSize;
- ++pShdr; // .debug_line
- pShdr->sh_offset = offset;
- pShdr->sh_size = dbgLine.MemSize;
- offset += dbgLine.MemSize;
- ++pShdr; // .symtab
- pShdr->sh_offset = offset;
- pShdr->sh_size = sectSymTab.MemSize;
- pShdr->sh_link = GetSectionIndex(".strtab");
- offset += sectSymTab.MemSize;
- ++pShdr; // .strtab
- pShdr->sh_offset = offset;
- pShdr->sh_size = sectStrTab.MemSize;
- offset += sectStrTab.MemSize;
-
- // .thunks
for (int i = 1 + method.GetCount(); i < symbolCount; i++)
{
- ++pShdr;
- pShdr->sh_addr = PCODEToPINSTR(symbolNames[i].m_value);
- pShdr->sh_size = 8;
- }
+ char name[256];
- /* Build ELF header */
- if (!BuildELFHeader(elfHeader))
- {
- return;
+ sprintf_s(name, _countof(name), ".thunk_%i", i);
+
+ Elf_SectionTracker *thunk = elfBuilder.OpenSection(name, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
+ thunk->DisableHeaderUpdate();
+ elfBuilder.CloseSection();
}
- Elf_Ehdr* header = reinterpret_cast<Elf_Ehdr*>(elfHeader.MemPtr.GetValue());
-#ifdef _TARGET_ARM_
- header->e_flags = EF_ARM_EABI_VER5;
-#ifdef ARM_SOFTFP
- header->e_flags |= EF_ARM_SOFT_FLOAT;
-#else
- header->e_flags |= EF_ARM_VFP_FLOAT;
-#endif
-#endif
- header->e_shoff = offset;
- header->e_shentsize = sizeof(Elf_Shdr);
- int thunks_count = symbolCount - method.GetCount() - 1;
- header->e_shnum = SectionNamesCount + thunks_count;
- header->e_shstrndx = GetSectionIndex(".shstrtab");
-
- /* Build ELF image in memory */
- elfFile.MemSize = elfHeader.MemSize + sectStr.MemSize + dbgStr.MemSize + dbgAbbrev.MemSize + dbgInfo.MemSize +
- dbgPubname.MemSize + dbgPubType.MemSize + dbgLine.MemSize + sectSymTab.MemSize +
- sectStrTab.MemSize + sectHeaders.MemSize;
- elfFile.MemPtr = new char[elfFile.MemSize];
-
- /* Copy section data */
- offset = 0;
- memcpy(elfFile.MemPtr, elfHeader.MemPtr, elfHeader.MemSize);
- offset += elfHeader.MemSize;
- memcpy(elfFile.MemPtr + offset, sectStr.MemPtr, sectStr.MemSize);
- offset += sectStr.MemSize;
- memcpy(elfFile.MemPtr + offset, dbgStr.MemPtr, dbgStr.MemSize);
- offset += dbgStr.MemSize;
- memcpy(elfFile.MemPtr + offset, dbgAbbrev.MemPtr, dbgAbbrev.MemSize);
- offset += dbgAbbrev.MemSize;
- memcpy(elfFile.MemPtr + offset, dbgInfo.MemPtr, dbgInfo.MemSize);
- offset += dbgInfo.MemSize;
- memcpy(elfFile.MemPtr + offset, dbgPubname.MemPtr, dbgPubname.MemSize);
- offset += dbgPubname.MemSize;
- memcpy(elfFile.MemPtr + offset, dbgPubType.MemPtr, dbgPubType.MemSize);
- offset += dbgPubType.MemSize;
- memcpy(elfFile.MemPtr + offset, dbgLine.MemPtr, dbgLine.MemSize);
- offset += dbgLine.MemSize;
- memcpy(elfFile.MemPtr + offset, sectSymTab.MemPtr, sectSymTab.MemSize);
- offset += sectSymTab.MemSize;
- memcpy(elfFile.MemPtr + offset, sectStrTab.MemPtr, sectStrTab.MemSize);
- offset += sectStrTab.MemSize;
-
- memcpy(elfFile.MemPtr + offset, sectHeaders.MemPtr, sectHeaders.MemSize);
-
- elfFile.MemPtr.SuppressRelease();
-
-#ifdef GDBJIT_DUMPELF
- DumpElf(methodName, elfFile);
-#endif
- /* Create GDB JIT structures */
- NewHolder<jit_code_entry> jit_symbols = new jit_code_entry;
+ elfBuilder.OpenSection(".debug_str", SHT_PROGBITS, SHF_MERGE | SHF_STRINGS);
+ elfBuilder.Append(dbgStr.MemPtr, dbgStr.MemSize);
+ elfBuilder.CloseSection();
- /* Fill the new entry */
- jit_symbols->next_entry = jit_symbols->prev_entry = 0;
- jit_symbols->symfile_addr = elfFile.MemPtr;
- jit_symbols->symfile_size = elfFile.MemSize;
+ elfBuilder.OpenSection(".debug_abbrev", SHT_PROGBITS, 0);
+ elfBuilder.Append(dbgAbbrev.MemPtr, dbgAbbrev.MemSize);
+ elfBuilder.CloseSection();
- /* Link into list */
- jit_code_entry *head = __jit_debug_descriptor.first_entry;
- __jit_debug_descriptor.first_entry = jit_symbols;
- if (head != 0)
- {
- jit_symbols->next_entry = head;
- head->prev_entry = jit_symbols;
- }
+ elfBuilder.OpenSection(".debug_info", SHT_PROGBITS, 0);
+ elfBuilder.Append(dbgInfo.MemPtr, dbgInfo.MemSize);
+ elfBuilder.CloseSection();
- jit_symbols.SuppressRelease();
+ elfBuilder.OpenSection(".debug_pubnames", SHT_PROGBITS, 0);
+ elfBuilder.Append(dbgPubname.MemPtr, dbgPubname.MemSize);
+ elfBuilder.CloseSection();
- /* Notify the debugger */
- __jit_debug_descriptor.relevant_entry = jit_symbols;
- __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
- __jit_debug_register_code();
-}
+ elfBuilder.OpenSection(".debug_pubtypes", SHT_PROGBITS, 0);
+ elfBuilder.Append(dbgPubType.MemPtr, dbgPubType.MemSize);
+ elfBuilder.CloseSection();
-void NotifyGdb::MethodDropped(MethodDesc* methodDescPtr)
-{
- static const int textSectionIndex = GetSectionIndex(".text");
+ elfBuilder.OpenSection(".debug_line", SHT_PROGBITS, 0);
+ elfBuilder.Append(dbgLine.MemPtr, dbgLine.MemSize);
+ elfBuilder.CloseSection();
- if (textSectionIndex < 0)
- return;
+ Elf_SectionTracker *strtab = elfBuilder.OpenSection(".strtab", SHT_STRTAB, 0);
+ elfBuilder.Append(sectStrTab.MemPtr, sectStrTab.MemSize);
+ elfBuilder.CloseSection();
+
+ Elf_SectionTracker *symtab = elfBuilder.OpenSection(".symtab", SHT_SYMTAB, 0);
+ elfBuilder.Append(sectSymTab.MemPtr, sectSymTab.MemSize);
+ symtab->Header()->sh_link = strtab->GetIndex();
+ symtab->Header()->sh_entsize = sizeof(Elf_Sym);
+ elfBuilder.CloseSection();
+
+ return true;
+}
+void NotifyGdb::MethodPitched(MethodDesc* methodDescPtr)
+{
PCODE pCode = methodDescPtr->GetNativeCode();
if (pCode == NULL)
@@ -2173,7 +2870,7 @@ void NotifyGdb::MethodDropped(MethodDesc* methodDescPtr)
const Elf_Ehdr* pEhdr = reinterpret_cast<const Elf_Ehdr*>(ptr);
const Elf_Shdr* pShdr = reinterpret_cast<const Elf_Shdr*>(ptr + pEhdr->e_shoff);
- pShdr += textSectionIndex; // bump to .text section
+ pShdr += ELF_BUILDER_TEXT_SECTION_INDEX; // bump to .text section
if (pShdr->sh_addr == pCode)
{
/* Notify the debugger */
@@ -2665,11 +3362,10 @@ bool NotifyGdb::BuildStringTableSection(MemBuf& buf, NewArrayHolder<Elf_Symbol>
}
/* Build ELF .symtab section */
-bool NotifyGdb::BuildSymbolTableSection(MemBuf& buf, PCODE addr, TADDR codeSize, FunctionMemberPtrArrayHolder &method,
- NewArrayHolder<Elf_Symbol> &symbolNames, int symbolCount)
+bool NotifyGdb::BuildSymbolTableSection(MemBuf& buf, PCODE addr, TADDR codeSize, int methodCount,
+ NewArrayHolder<Elf_Symbol> &symbolNames, int symbolCount,
+ unsigned int thunkIndexBase)
{
- static const int textSectionIndex = GetSectionIndex(".text");
-
buf.MemSize = symbolCount * sizeof(Elf_Sym);
buf.MemPtr = new char[buf.MemSize];
@@ -2682,22 +3378,22 @@ bool NotifyGdb::BuildSymbolTableSection(MemBuf& buf, PCODE addr, TADDR codeSize,
sym[0].st_size = 0;
sym[0].st_shndx = SHN_UNDEF;
- for (int i = 1; i < 1 + method.GetCount(); ++i)
+ for (int i = 1; i < 1 + methodCount; ++i)
{
sym[i].st_name = symbolNames[i].m_off;
sym[i].setBindingAndType(STB_GLOBAL, STT_FUNC);
sym[i].st_other = 0;
sym[i].st_value = PINSTRToPCODE(symbolNames[i].m_value - addr);
- sym[i].st_shndx = textSectionIndex;
+ sym[i].st_shndx = ELF_BUILDER_TEXT_SECTION_INDEX;
sym[i].st_size = symbolNames[i].m_size;
}
- for (int i = 1 + method.GetCount(); i < symbolCount; ++i)
+ for (int i = 1 + methodCount; i < symbolCount; ++i)
{
sym[i].st_name = symbolNames[i].m_off;
sym[i].setBindingAndType(STB_GLOBAL, STT_FUNC);
sym[i].st_other = 0;
- sym[i].st_shndx = SectionNamesCount + (i - (1 + method.GetCount())); // .thunks section index
+ sym[i].st_shndx = thunkIndexBase + (i - (1 + methodCount)); // .thunks section index
sym[i].st_size = 8;
#ifdef _TARGET_ARM_
sym[i].st_value = 1; // for THUMB code
@@ -2708,97 +3404,6 @@ bool NotifyGdb::BuildSymbolTableSection(MemBuf& buf, PCODE addr, TADDR codeSize,
return true;
}
-int NotifyGdb::GetSectionIndex(const char *sectName)
-{
- for (int i = 0; i < SectionNamesCount; ++i)
- if (strcmp(SectionNames[i], sectName) == 0)
- return i;
- return -1;
-}
-
-/* Build the ELF section headers table and section names table */
-void NotifyGdb::BuildSectionTables(MemBuf& sectBuf, MemBuf& strBuf, FunctionMemberPtrArrayHolder &method,
- int symbolCount)
-{
- static const int symtabSectionIndex = GetSectionIndex(".symtab");
- static const int nullSectionIndex = GetSectionIndex("");
-
- const int thunks_count = symbolCount - 1 - method.GetCount();
-
- // Approximate length of single section name.
- // Used only to reduce memory reallocations.
- static const int SECT_NAME_LENGTH = 11;
-
- strBuf.Resize(SECT_NAME_LENGTH * (SectionNamesCount + thunks_count));
-
- Elf_Shdr* sectionHeaders = new Elf_Shdr[SectionNamesCount + thunks_count];
- sectBuf.MemPtr = reinterpret_cast<char*>(sectionHeaders);
- sectBuf.MemSize = sizeof(Elf_Shdr) * (SectionNamesCount + thunks_count);
-
- Elf_Shdr* pSh = sectionHeaders;
- uint32_t sectNameOffset = 0;
-
- // Additional memory for remaining section names,
- // grows twice on each reallocation.
- int addSize = SECT_NAME_LENGTH;
-
- // Fill section headers and names
- for (int i = 0; i < SectionNamesCount + thunks_count; ++i, ++pSh)
- {
- char thunkSectNameBuf[256]; // temporary buffer for .thunk_# section name
- const char *sectName;
-
- bool isThunkSection = i >= SectionNamesCount;
- if (isThunkSection)
- {
- sprintf_s(thunkSectNameBuf, _countof(thunkSectNameBuf), ".thunk_%i", i);
- sectName = thunkSectNameBuf;
- }
- else
- {
- sectName = SectionNames[i];
- }
-
- // Ensure that there is enough memory for section name,
- // reallocate if necessary.
- pSh->sh_name = sectNameOffset;
- sectNameOffset += strlen(sectName) + 1;
- if (sectNameOffset > strBuf.MemSize)
- {
- // Allocate more memory for remaining section names
- strBuf.Resize(sectNameOffset + addSize);
- addSize *= 2;
- }
-
- strcpy(strBuf.MemPtr + pSh->sh_name, sectName);
-
- // All .thunk_* sections have the same type and flags
- int index = isThunkSection ? SectionNamesCount : i;
- pSh->sh_type = Sections[index].m_type;
- pSh->sh_flags = Sections[index].m_flags;
-
- pSh->sh_addr = 0;
- pSh->sh_offset = 0;
- pSh->sh_size = 0;
- pSh->sh_link = SHN_UNDEF;
- pSh->sh_info = 0;
- pSh->sh_addralign = i == nullSectionIndex ? 0 : 1;
- pSh->sh_entsize = i == symtabSectionIndex ? sizeof(Elf_Sym) : 0;
- }
-
- // Set actual used size to avoid garbage in ELF section
- strBuf.MemSize = sectNameOffset;
-}
-
-/* Build the ELF header */
-bool NotifyGdb::BuildELFHeader(MemBuf& buf)
-{
- Elf_Ehdr* header = new Elf_Ehdr;
- buf.MemPtr = reinterpret_cast<char*>(header);
- buf.MemSize = sizeof(Elf_Ehdr);
- return true;
-}
-
/* Split full path name into directory & file names */
void NotifyGdb::SplitPathname(const char* path, const char*& pathName, const char*& fileName)
{
@@ -2817,19 +3422,6 @@ void NotifyGdb::SplitPathname(const char* path, const char*& pathName, const cha
}
}
-#ifdef _DEBUG
-void NotifyGdb::DumpElf(const char* methodName, const MemBuf& elfFile)
-{
- char dump[1024];
- strcpy(dump, "./");
- strcat(dump, methodName);
- strcat(dump, ".o");
- FILE *f = fopen(dump, "wb");
- fwrite(elfFile.MemPtr, sizeof(char),elfFile.MemSize, f);
- fclose(f);
-}
-#endif
-
/* ELF 32bit header */
Elf32_Ehdr::Elf32_Ehdr()
{
diff --git a/src/vm/gdbjit.h b/src/vm/gdbjit.h
index 6bde3f27ba..f7267ad2a1 100644
--- a/src/vm/gdbjit.h
+++ b/src/vm/gdbjit.h
@@ -121,12 +121,21 @@ struct SymbolsInfo
class DwarfDumpable
{
public:
+ DwarfDumpable() :
+ m_base_ptr(nullptr),
+ m_is_visited(false)
+ {
+ }
+
// writes all string literals this type needs to ptr
virtual void DumpStrings(char* ptr, int& offset) = 0;
virtual void DumpDebugInfo(char* ptr, int& offset) = 0;
virtual ~DwarfDumpable() {}
+
+ char *m_base_ptr;
+ bool m_is_visited;
};
class LocalsInfo
@@ -326,12 +335,13 @@ public:
};
struct Elf_Symbol;
+class Elf_Builder;
class NotifyGdb
{
public:
- static void MethodCompiled(MethodDesc* methodDescPtr);
- static void MethodDropped(MethodDesc* methodDescPtr);
+ static void MethodPrepared(MethodDesc* methodDescPtr);
+ static void MethodPitched(MethodDesc* methodDescPtr);
template <typename PARENT_TRAITS>
class DeleteValuesOnDestructSHashTraits : public PARENT_TRAITS
{
@@ -404,14 +414,19 @@ private:
}
};
- static void OnMethodCompiled(MethodDesc* methodDescPtr);
+ static void OnMethodPrepared(MethodDesc* methodDescPtr);
+
+#ifdef FEATURE_GDBJIT_FRAME
+ static bool EmitFrameInfo(Elf_Builder &, PCODE pCode, TADDR codeSzie);
+#endif // FEATURE_GDBJIT_FRAME
+#ifdef FEATURE_GDBJIT_SYMTAB
+ static bool EmitSymtab(Elf_Builder &, MethodDesc* methodDescPtr, PCODE pCode, TADDR codeSize);
+#endif // FEATURE_GDBJIT_SYMTAB
+ static bool EmitDebugInfo(Elf_Builder &, MethodDesc* methodDescPtr, PCODE pCode, TADDR codeSize, const char *szModuleFile);
- static int GetSectionIndex(const char *sectName);
- static bool BuildELFHeader(MemBuf& buf);
- static void BuildSectionTables(MemBuf& sectBuf, MemBuf& strBuf, FunctionMemberPtrArrayHolder &method,
- int symbolCount);
- static bool BuildSymbolTableSection(MemBuf& buf, PCODE addr, TADDR codeSize, FunctionMemberPtrArrayHolder &method,
- NewArrayHolder<Elf_Symbol> &symbolNames, int symbolCount);
+ static bool BuildSymbolTableSection(MemBuf& buf, PCODE addr, TADDR codeSize, int methodCount,
+ NewArrayHolder<Elf_Symbol> &symbolNames, int symbolCount,
+ unsigned int thunkIndexBase);
static bool BuildStringTableSection(MemBuf& strTab, NewArrayHolder<Elf_Symbol> &symbolNames, int symbolCount);
static bool BuildDebugStrings(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMemberPtrArrayHolder &method);
static bool BuildDebugAbbrev(MemBuf& buf);
@@ -427,9 +442,6 @@ private:
static void SplitPathname(const char* path, const char*& pathName, const char*& fileName);
static bool CollectCalledMethods(CalledMethod* pCM, TADDR nativeCode, FunctionMemberPtrArrayHolder &method,
NewArrayHolder<Elf_Symbol> &symbolNames, int &symbolCount);
-#ifdef _DEBUG
- static void DumpElf(const char* methodName, const MemBuf& buf);
-#endif
};
class FunctionMember: public TypeMember
diff --git a/src/vm/genericdict.cpp b/src/vm/genericdict.cpp
index c93e583345..5fad30f4b8 100644
--- a/src/vm/genericdict.cpp
+++ b/src/vm/genericdict.cpp
@@ -742,7 +742,7 @@ Dictionary::PopulateEntry(
}
// MethodTable is expected to be normalized
- _ASSERTE(pDictionary == pMT->GetPerInstInfo()[dictionaryIndex]);
+ _ASSERTE(pDictionary == pMT->GetPerInstInfo()[dictionaryIndex].GetValueMaybeNull());
}
else
{
diff --git a/src/vm/generics.cpp b/src/vm/generics.cpp
index 51e6d7bbac..ed5313263f 100644
--- a/src/vm/generics.cpp
+++ b/src/vm/generics.cpp
@@ -255,7 +255,7 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation(
// Bytes are required for the vtable itself
S_SIZE_T safe_cbMT = S_SIZE_T( cbGC ) + S_SIZE_T( sizeof(MethodTable) );
- safe_cbMT += MethodTable::GetNumVtableIndirections(cSlots) * sizeof(PTR_PCODE);
+ safe_cbMT += MethodTable::GetNumVtableIndirections(cSlots) * sizeof(MethodTable::VTableIndir_t);
if (safe_cbMT.IsOverflow())
{
ThrowHR(COR_E_OVERFLOW);
@@ -364,7 +364,7 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation(
pMT->ClearFlag(MethodTable::enum_flag_IsZapped);
pMT->ClearFlag(MethodTable::enum_flag_IsPreRestored);
- pMT->ClearFlag(MethodTable::enum_flag_HasIndirectParent);
+ pMT->m_pParentMethodTable.SetValueMaybeNull(NULL);
// Non non-virtual slots
pMT->ClearFlag(MethodTable::enum_flag_HasSingleNonVirtualSlot);
@@ -440,7 +440,7 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation(
if (canShareVtableChunks)
{
// Share the canonical chunk
- it.SetIndirectionSlot(pOldMT->GetVtableIndirections()[it.GetIndex()]);
+ it.SetIndirectionSlot(pOldMT->GetVtableIndirections()[it.GetIndex()].GetValueMaybeNull());
}
else
{
@@ -499,7 +499,7 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation(
_ASSERTE(pOldMT->HasPerInstInfo());
// Fill in per-inst map pointer (which points to the array of generic dictionary pointers)
- pMT->SetPerInstInfo ((Dictionary**) (pMemory + cbMT + cbOptional + cbIMap + sizeof(GenericsDictInfo)));
+ pMT->SetPerInstInfo((MethodTable::PerInstInfoElem_t *) (pMemory + cbMT + cbOptional + cbIMap + sizeof(GenericsDictInfo)));
_ASSERTE(FitsIn<WORD>(pOldMT->GetNumDicts()));
_ASSERTE(FitsIn<WORD>(pOldMT->GetNumGenericArgs()));
pMT->SetDictInfo(static_cast<WORD>(pOldMT->GetNumDicts()), static_cast<WORD>(pOldMT->GetNumGenericArgs()));
@@ -508,7 +508,8 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation(
// The others are filled in by LoadExactParents which copied down any inherited generic
// dictionary pointers.
Dictionary * pDict = (Dictionary*) (pMemory + cbMT + cbOptional + cbIMap + cbPerInst);
- *(pMT->GetPerInstInfo() + (pOldMT->GetNumDicts()-1)) = pDict;
+ MethodTable::PerInstInfoElem_t *pPInstInfo = (MethodTable::PerInstInfoElem_t *) (pMT->GetPerInstInfo() + (pOldMT->GetNumDicts()-1));
+ pPInstInfo->SetValueMaybeNull(pDict);
// Fill in the instantiation section of the generic dictionary. The remainder of the
// generic dictionary will be zeroed, which is the correct initial state.
diff --git a/src/vm/genmeth.cpp b/src/vm/genmeth.cpp
index dc55221308..dd8e3283cc 100644
--- a/src/vm/genmeth.cpp
+++ b/src/vm/genmeth.cpp
@@ -120,34 +120,6 @@ static MethodDesc* CreateMethodDesc(LoaderAllocator *pAllocator,
{
pMD->SetSynchronized();
}
- if (pTemplateMD->RequiresLinktimeCheck())
- {
- pMD->SetRequiresLinktimeCheck();
- }
- if (pTemplateMD->RequiresInheritanceCheck())
- {
- pMD->SetRequiresInheritanceCheck();
- }
- if (pTemplateMD->ParentRequiresInheritanceCheck())
- {
- pMD->SetParentRequiresInheritanceCheck();
- }
- if (pTemplateMD->IsInterceptedForDeclSecurity())
- {
- pMD->SetInterceptedForDeclSecurity();
- }
- if (pTemplateMD->IsInterceptedForDeclSecurityCASDemandsOnly())
- {
- pMD->SetInterceptedForDeclSecurityCASDemandsOnly();
- }
- if (pTemplateMD->HasCriticalTransparentInfo())
- {
- pMD->SetCriticalTransparentInfo(pTemplateMD->IsCritical(), pTemplateMD->IsTreatAsSafe());
- }
- if (pTemplateMD->RequiresLinkTimeCheckHostProtectionOnly())
- {
- pMD->SetRequiresLinkTimeCheckHostProtectionOnly();
- }
pMD->SetMemberDef(token);
pMD->SetSlot(pTemplateMD->GetSlot());
diff --git a/src/vm/hosting.cpp b/src/vm/hosting.cpp
index d47bc28238..035fff8812 100644
--- a/src/vm/hosting.cpp
+++ b/src/vm/hosting.cpp
@@ -480,12 +480,15 @@ BOOL EEHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
#ifdef _DEBUG
GlobalAllocStore::RemoveAlloc (lpMem);
- // Check the heap handle to detect heap contamination
- lpMem = (BYTE*)lpMem - OS_HEAP_ALIGN;
- HANDLE storedHeapHandle = *((HANDLE*)lpMem);
- if(storedHeapHandle != hHeap)
- _ASSERTE(!"Heap contamination detected! HeapFree was called on a heap other than the one that memory was allocated from.\n"
- "Possible cause: you used new (executable) to allocate the memory, but didn't use DeleteExecutable() to free it.");
+ if (lpMem != NULL)
+ {
+ // Check the heap handle to detect heap contamination
+ lpMem = (BYTE*)lpMem - OS_HEAP_ALIGN;
+ HANDLE storedHeapHandle = *((HANDLE*)lpMem);
+ if(storedHeapHandle != hHeap)
+ _ASSERTE(!"Heap contamination detected! HeapFree was called on a heap other than the one that memory was allocated from.\n"
+ "Possible cause: you used new (executable) to allocate the memory, but didn't use DeleteExecutable() to free it.");
+ }
#endif
// DON'T REMOVE THIS SEEMINGLY USELESS CAST
//
diff --git a/src/vm/i386/cgencpu.h b/src/vm/i386/cgencpu.h
index ff76d992fc..e4a623b715 100644
--- a/src/vm/i386/cgencpu.h
+++ b/src/vm/i386/cgencpu.h
@@ -504,6 +504,7 @@ struct DECLSPEC_ALIGN(4) UMEntryThunkCode
const BYTE * m_execstub; // pointer to destination code // make sure the backpatched portion is dword aligned.
void Encode(BYTE* pTargetCode, void* pvSecretParam);
+ void Poison();
LPCBYTE GetEntryPoint() const
{
diff --git a/src/vm/i386/cgenx86.cpp b/src/vm/i386/cgenx86.cpp
index c75490babd..9b8960a6eb 100644
--- a/src/vm/i386/cgenx86.cpp
+++ b/src/vm/i386/cgenx86.cpp
@@ -19,7 +19,6 @@
#include "dllimport.h"
#include "comdelegate.h"
#include "log.h"
-#include "security.h"
#include "comdelegate.h"
#include "array.h"
#include "jitinterface.h"
@@ -1588,6 +1587,13 @@ void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
FlushInstructionCache(GetCurrentProcess(),GetEntryPoint(),sizeof(UMEntryThunkCode));
}
+void UMEntryThunkCode::Poison()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_movEAX = X86_INSTR_INT3;
+}
+
UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback)
{
LIMITED_METHOD_CONTRACT;
diff --git a/src/vm/i386/stublinkerx86.cpp b/src/vm/i386/stublinkerx86.cpp
index 1ad4636a8f..b77609822b 100644
--- a/src/vm/i386/stublinkerx86.cpp
+++ b/src/vm/i386/stublinkerx86.cpp
@@ -21,7 +21,6 @@
#include "excep.h"
#include "dllimport.h"
#include "log.h"
-#include "security.h"
#include "comdelegate.h"
#include "array.h"
#include "jitinterface.h"
@@ -6720,7 +6719,7 @@ BOOL FixupPrecode::SetTargetInterlocked(TADDR target, TADDR expected)
}
else if (pOldValue[OFFSETOF_PRECODE_TYPE_CALL_OR_JMP] == FixupPrecode::Type)
{
-#ifdef FEATURE_TIERED_COMPILATION
+#ifdef FEATURE_CODE_VERSIONING
// No change needed, jmp is already in place
#else
// Setting the target more than once is unexpected
diff --git a/src/vm/ilinstrumentation.cpp b/src/vm/ilinstrumentation.cpp
new file mode 100644
index 0000000000..a2bdbf1a60
--- /dev/null
+++ b/src/vm/ilinstrumentation.cpp
@@ -0,0 +1,90 @@
+// 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.
+// ===========================================================================
+// File: ILInstrumentation.cpp
+//
+// ===========================================================================
+
+
+#include "common.h"
+#include "ilinstrumentation.h"
+
+
+//---------------------------------------------------------------------------------------
+InstrumentedILOffsetMapping::InstrumentedILOffsetMapping()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ m_cMap = 0;
+ m_rgMap = NULL;
+ _ASSERTE(IsNull());
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Check whether there is any mapping information stored in this object.
+//
+// Notes:
+// The memory should be alive throughout the process lifetime until
+// the Module containing the instrumented method is destructed.
+//
+
+BOOL InstrumentedILOffsetMapping::IsNull() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ _ASSERTE((m_cMap == 0) == (m_rgMap == NULL));
+ return (m_cMap == 0);
+}
+
+#if !defined(DACCESS_COMPILE)
+//---------------------------------------------------------------------------------------
+//
+// Release the memory used by the array of COR_IL_MAPs.
+//
+// Notes:
+// * The memory should be alive throughout the process lifetime until the Module containing
+// the instrumented method is destructed.
+// * This struct should be read-only in DAC builds.
+//
+
+void InstrumentedILOffsetMapping::Clear()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_rgMap != NULL)
+ {
+ delete[] m_rgMap;
+ }
+
+ m_cMap = 0;
+ m_rgMap = NULL;
+}
+#endif // !DACCESS_COMPILE
+
+#if !defined(DACCESS_COMPILE)
+void InstrumentedILOffsetMapping::SetMappingInfo(SIZE_T cMap, COR_IL_MAP * rgMap)
+{
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE((cMap == 0) == (rgMap == NULL));
+ m_cMap = cMap;
+ m_rgMap = ARRAY_PTR_COR_IL_MAP(rgMap);
+}
+#endif // !DACCESS_COMPILE
+
+SIZE_T InstrumentedILOffsetMapping::GetCount() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ _ASSERTE((m_cMap == 0) == (m_rgMap == NULL));
+ return m_cMap;
+}
+
+ARRAY_PTR_COR_IL_MAP InstrumentedILOffsetMapping::GetOffsets() const
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ _ASSERTE((m_cMap == 0) == (m_rgMap == NULL));
+ return m_rgMap;
+}
diff --git a/src/vm/ilinstrumentation.h b/src/vm/ilinstrumentation.h
new file mode 100644
index 0000000000..cc486ede3f
--- /dev/null
+++ b/src/vm/ilinstrumentation.h
@@ -0,0 +1,116 @@
+// 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.
+// ===========================================================================
+// File: ILInstrumentation.h
+//
+// ===========================================================================
+
+
+
+#ifndef IL_INSTRUMENTATION_H
+#define IL_INSTRUMENTATION_H
+
+// declare an array type of COR_IL_MAP entries
+typedef ArrayDPTR(COR_IL_MAP) ARRAY_PTR_COR_IL_MAP;
+
+//---------------------------------------------------------------------------------------
+//
+// A profiler may instrument a method by changing the IL. This is typically done when the profiler receives
+// a JITCompilationStarted notification. The profiler also has the option to provide the runtime with
+// a mapping between original IL offsets and instrumented IL offsets. This struct is a simple container
+// for storing the mapping information. We store the mapping information on the Module class, where it can
+// be accessed by the debugger from out-of-process.
+//
+
+class InstrumentedILOffsetMapping
+{
+public:
+ InstrumentedILOffsetMapping();
+
+ // Check whether there is any mapping information stored in this object.
+ BOOL IsNull() const;
+
+#if !defined(DACCESS_COMPILE)
+ // Release the memory used by the array of COR_IL_MAPs.
+ void Clear();
+
+ void SetMappingInfo(SIZE_T cMap, COR_IL_MAP * rgMap);
+#endif // !DACCESS_COMPILE
+
+ SIZE_T GetCount() const;
+ ARRAY_PTR_COR_IL_MAP GetOffsets() const;
+
+private:
+ SIZE_T m_cMap; // the number of elements in m_rgMap
+ ARRAY_PTR_COR_IL_MAP m_rgMap; // an array of COR_IL_MAPs
+};
+
+//---------------------------------------------------------------------------------------
+//
+// Hash table entry for storing InstrumentedILOffsetMapping. This is keyed by the MethodDef token.
+//
+
+struct ILOffsetMappingEntry
+{
+ ILOffsetMappingEntry()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ m_methodToken = mdMethodDefNil;
+ // No need to initialize m_mapping. The default ctor of InstrumentedILOffsetMapping does the job.
+ }
+
+ ILOffsetMappingEntry(mdMethodDef token, InstrumentedILOffsetMapping mapping)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ m_methodToken = token;
+ m_mapping = mapping;
+ }
+
+ mdMethodDef m_methodToken;
+ InstrumentedILOffsetMapping m_mapping;
+};
+
+//---------------------------------------------------------------------------------------
+//
+// This class is used to create the hash table for the instrumented IL offset mapping.
+// It encapsulates the desired behaviour of the templated hash table and implements
+// the various functions needed by the hash table.
+//
+
+class ILOffsetMappingTraits : public NoRemoveSHashTraits<DefaultSHashTraits<ILOffsetMappingEntry> >
+{
+public:
+ typedef mdMethodDef key_t;
+
+ static key_t GetKey(element_t e)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return e.m_methodToken;
+ }
+ static BOOL Equals(key_t k1, key_t k2)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (k1 == k2);
+ }
+ static count_t Hash(key_t k)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (count_t)(size_t)k;
+ }
+ static const element_t Null()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ ILOffsetMappingEntry e;
+ return e;
+ }
+ static bool IsNull(const element_t &e) { LIMITED_METHOD_DAC_CONTRACT; return e.m_methodToken == mdMethodDefNil; }
+};
+
+// Hash table of profiler-provided instrumented IL offset mapping, keyed by the MethodDef token
+typedef SHash<ILOffsetMappingTraits> ILOffsetMappingTable;
+typedef DPTR(ILOffsetMappingTable) PTR_ILOffsetMappingTable;
+
+#endif // IL_INSTRUMENTATION_H
diff --git a/src/vm/interpreter.cpp b/src/vm/interpreter.cpp
index 6901c9c2cd..d18eede1f1 100644
--- a/src/vm/interpreter.cpp
+++ b/src/vm/interpreter.cpp
@@ -38,7 +38,6 @@ static CorInfoType asCorInfoType(CORINFO_CLASS_HANDLE clsHnd)
InterpreterMethodInfo::InterpreterMethodInfo(CEEInfo* comp, CORINFO_METHOD_INFO* methInfo)
: m_method(methInfo->ftn),
m_module(methInfo->scope),
- m_jittedCode(0),
m_ILCode(methInfo->ILCode),
m_ILCodeEnd(methInfo->ILCode + methInfo->ILCodeSize),
m_maxStack(methInfo->maxStack),
@@ -798,12 +797,6 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp,
// So the structure of the code will look like this (in the non-ILstub case):
//
#if defined(_X86_) || defined(_AMD64_)
- // First do "short-circuiting" if the method has JITted code, and we couldn't find/update the call site:
- // eax = &interpMethInfo
- // eax = [eax + offsetof(m_jittedCode)]
- // if (eax == zero) goto doInterpret:
- // /*else*/ jmp [eax]
- // doInterpret:
// push ebp
// mov ebp, esp
// [if there are register arguments in ecx or edx, push them]
@@ -817,41 +810,6 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp,
// TODO.
#endif
- // The IL stub case is hard. The portion of the interpreter stub that short-circuits
- // to JITted code requires an extra "scratch" volatile register, not an argument register;
- // in the IL stub case, it too is using such a register, as an extra argument, to hold the stub context.
- // On x86 and ARM, there is only one such extra volatile register, and we've got a conundrum.
- // The cases where this short-circuiting is important is when the address of an interpreter stub
- // becomes "embedded" in other code. The examples I know of are VSD stubs and delegates.
- // The first of these is not a problem for IL stubs -- methods invoked via p/Invoke (the ones that
- // [I think!] use IL stubs) are static, and cannot be invoked via VSD stubs. Delegates, on the other
- // remain a problem [I believe].
- // For the short term, we'll ignore this issue, and never do short-circuiting for IL stubs.
- // So interpreter stubs embedded in delegates will continue to interpret the IL stub, even after
- // the stub has been JITted.
- // The long-term intention is that when we JIT a method with an interpreter stub, we keep a mapping
- // from interpreter stub address to corresponding native code address. If this mapping is non-empty,
- // at GC time we would visit the locations in which interpreter stub addresses might be located, like
- // VSD stubs and delegate objects, and update them to point to new addresses. This would be a necessary
- // part of any scheme to GC interpreter stubs, and InterpreterMethodInfos.
-
- // If we *really* wanted to make short-circuiting work for the IL stub case, we would have to
- // (in the x86 case, which should be sufficiently illustrative):
- // push eax
- // <get the address of JITted code, if any, into eax>
- // if there is JITted code in eax, we'd have to
- // push 2 non-volatile registers, say esi and edi.
- // copy the JITted code address from eax into esi.
- // copy the method arguments (without the return address) down the stack, using edi
- // as a scratch register.
- // restore the original stub context value into eax from the stack
- // call (not jmp) to the JITted code address in esi
- // pop esi and edi from the stack.
- // now the stack has original args, followed by original return address. Do a "ret"
- // that returns to the return address, and also pops the original args from the stack.
- // If we did this, we'd have to give this portion of the stub proper unwind info.
- // Also, we'd have to adjust the rest of the stub to pop eax from the stack.
-
// TODO: much of the interpreter stub code should be is shareable. In the non-IL stub case,
// at least, we could have a small per-method stub that puts the address of the method-specific
// InterpreterMethodInfo into eax, and then branches to a shared part. Probably we would want to
@@ -868,24 +826,7 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp,
{
sl.Init();
#if defined(_X86_) || defined(_AMD64_)
- // First we do "short-circuiting" if the method has JITted code.
-#if INTERP_ILSTUBS
- if (!pMD->IsILStub()) // As discussed above, we don't do short-circuiting for IL stubs.
-#endif
- {
- // First read the m_jittedCode field.
- sl.X86EmitRegLoad(kEAX, UINT_PTR(interpMethInfo));
- sl.X86EmitOffsetModRM(0x8b, kEAX, kEAX, offsetof(InterpreterMethodInfo, m_jittedCode));
- // If it is still zero, then go on to do the interpretation.
- sl.X86EmitCmpRegImm32(kEAX, 0);
- CodeLabel* doInterpret = sl.NewCodeLabel();
- sl.X86EmitCondJump(doInterpret, X86CondCode::kJE);
- // Otherwise...
- sl.X86EmitJumpReg(kEAX); // tail call to JITted code.
- sl.EmitLabel(doInterpret);
- }
#if defined(_X86_)
- // Start regular interpretation
sl.X86EmitPushReg(kEBP);
sl.X86EmitMovRegReg(kEBP, static_cast<X86Reg>(kESP_Unsafe));
#endif
@@ -895,43 +836,10 @@ CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp,
ThumbReg r11 = ThumbReg(11);
ThumbReg r12 = ThumbReg(12);
-#if INTERP_ILSTUBS
- if (!pMD->IsILStub()) // As discussed above, we don't do short-circuiting for IL stubs.
-#endif
- {
- // But we also have to use r4, because ThumbEmitCondRegJump below requires a low register.
- sl.ThumbEmitMovConstant(r11, 0);
- sl.ThumbEmitMovConstant(r12, UINT_PTR(interpMethInfo));
- sl.ThumbEmitLoadRegIndirect(r12, r12, offsetof(InterpreterMethodInfo, m_jittedCode));
- sl.ThumbEmitCmpReg(r12, r11); // Set condition codes.
- // If r12 is zero, then go on to do the interpretation.
- CodeLabel* doInterpret = sl.NewCodeLabel();
- sl.ThumbEmitCondFlagJump(doInterpret, thumbCondEq.cond);
- sl.ThumbEmitJumpRegister(r12); // If non-zero, tail call to JITted code.
- sl.EmitLabel(doInterpret);
- }
-
- // Start regular interpretation
-
#elif defined(_ARM64_)
// x8 through x15 are scratch registers on ARM64.
IntReg x8 = IntReg(8);
IntReg x9 = IntReg(9);
-
-#if INTERP_ILSTUBS
- if (!pMD->IsILStub()) // As discussed above, we don't do short-circuiting for IL stubs.
-#endif
- {
- sl.EmitMovConstant(x8, UINT64(interpMethInfo));
- sl.EmitLoadStoreRegImm(StubLinkerCPU::eLOAD, x9, x8, offsetof(InterpreterMethodInfo, m_jittedCode));
- sl.EmitCmpImm(x9, 0);
- CodeLabel* doInterpret = sl.NewCodeLabel();
- sl.EmitCondFlagJump(doInterpret, CondEq.cond);
- sl.EmitJumpRegister(x9);
- sl.EmitLabel(doInterpret);
- }
-
- // Start regular interpretation
#else
#error unsupported platform
#endif
@@ -1749,8 +1657,16 @@ void Interpreter::JitMethodIfAppropriate(InterpreterMethodInfo* interpMethInfo,
md->GetMDImport(),
&status);
}
- PCODE res = md->MakeJitWorker(pDecoder, jitFlags);
- interpMethInfo->m_jittedCode = res;
+ // This used to be a synchronous jit and could be made so again if desired,
+ // but using ASP.Net MusicStore as an example scenario the performance is
+ // better doing the JIT asynchronously. Given the not-on-by-default nature of the
+ // interpreter I didn't wring my hands too much trying to determine the ideal
+ // policy.
+#ifdef FEATURE_TIERED_COMPILATION
+ GetAppDomain()->GetTieredCompilationManager()->AsyncPromoteMethodToTier1(md);
+#else
+#error FEATURE_INTERPRETER depends on FEATURE_TIERED_COMPILATION now
+#endif
}
}
}
@@ -10294,7 +10210,7 @@ void Interpreter::CallI()
MethodDesc* pMD;
if (mSig.HasThis())
{
- pMD = g_pObjectCtorMD;
+ pMD = g_pObjectFinalizerMD;
}
else
{
diff --git a/src/vm/interpreter.h b/src/vm/interpreter.h
index dc7638ca7d..fd4a68bea3 100644
--- a/src/vm/interpreter.h
+++ b/src/vm/interpreter.h
@@ -552,9 +552,6 @@ struct InterpreterMethodInfo
// The module containing the method.
CORINFO_MODULE_HANDLE m_module;
- // If the method has been JITted, it's JITted code (for indirection).
- PCODE m_jittedCode;
-
// Code pointer, size, and max stack usage.
BYTE* m_ILCode;
BYTE* m_ILCodeEnd; // One byte past the last byte of IL. IL Code Size = m_ILCodeEnd - m_ILCode.
diff --git a/src/vm/invokeutil.cpp b/src/vm/invokeutil.cpp
index 9efc84d711..4c1dd4d203 100644
--- a/src/vm/invokeutil.cpp
+++ b/src/vm/invokeutil.cpp
@@ -18,7 +18,6 @@
#include "method.hpp"
#include "threads.h"
#include "excep.h"
-#include "security.h"
#include "field.h"
#include "customattribute.h"
#include "eeconfig.h"
@@ -601,11 +600,9 @@ void InvokeUtil::ValidField(TypeHandle th, OBJECTREF* value)
if (!srcTH.CanCastTo(th))
COMPlusThrow(kArgumentException,W("Arg_ObjObj"));
}
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
return;
}
else if (MscorlibBinder::IsClass((*value)->GetMethodTable(), CLASS__INTPTR)) {
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
return;
}
diff --git a/src/vm/invokeutil.h b/src/vm/invokeutil.h
index cfa1a0e96b..ec8114f76a 100644
--- a/src/vm/invokeutil.h
+++ b/src/vm/invokeutil.h
@@ -66,14 +66,6 @@ public:
virtual MethodDesc* GetCallerMethod();
virtual Assembly* GetCallerAssembly();
virtual bool IsCalledFromInterop();
-
- // The caller will be computed lazily by the reflection system.
- virtual bool IsCallerCritical()
- {
- LIMITED_METHOD_CONTRACT;
-
- return false;
- }
AccessCheckOptions::AccessCheckType GetAccessCheckType() const
{
diff --git a/src/vm/jithelpers.cpp b/src/vm/jithelpers.cpp
index bfb2b34565..32be77823c 100644
--- a/src/vm/jithelpers.cpp
+++ b/src/vm/jithelpers.cpp
@@ -20,7 +20,6 @@
#include "excep.h"
#include "float.h" // for isnan
#include "dbginterface.h"
-#include "security.h"
#include "dllimport.h"
#include "gcheaputilities.h"
#include "comdelegate.h"
@@ -46,7 +45,6 @@
#include "genericdict.h"
#include "array.h"
#include "debuginfostore.h"
-#include "security.h"
#include "safemath.h"
#include "threadstatics.h"
@@ -2398,7 +2396,7 @@ HCIMPL2(Object*, JIT_ChkCastClass_Portable, MethodTable* pTargetMT, Object* pObj
if (pMT == pTargetMT)
return pObject;
- pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
+ pMT = MethodTable::GetParentMethodTable(pMT);
} while (pMT);
ENDFORBIDGC();
@@ -2418,14 +2416,14 @@ HCIMPL2(Object*, JIT_ChkCastClassSpecial_Portable, MethodTable* pTargetMT, Objec
PRECONDITION(pObject->GetMethodTable() != pTargetMT);
} CONTRACTL_END;
- PTR_VOID pMT = MethodTable::GetParentMethodTableOrIndirection(pObject->GetMethodTable());
+ PTR_VOID pMT = MethodTable::GetParentMethodTable(pObject->GetMethodTable());
while (pMT)
{
if (pMT == pTargetMT)
return pObject;
- pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
+ pMT = MethodTable::GetParentMethodTable(pMT);
}
ENDFORBIDGC();
@@ -2452,7 +2450,7 @@ HCIMPL2(Object*, JIT_IsInstanceOfClass_Portable, MethodTable* pTargetMT, Object*
if (pMT == pTargetMT)
return pObject;
- pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
+ pMT = MethodTable::GetParentMethodTable(pMT);
} while (pMT);
if (!pObject->GetMethodTable()->HasTypeEquivalence())
diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp
index ecabc89ba7..d960394e12 100644
--- a/src/vm/jitinterface.cpp
+++ b/src/vm/jitinterface.cpp
@@ -24,7 +24,6 @@
#include "excep.h"
#include "float.h" // for isnan
#include "dbginterface.h"
-#include "security.h"
#include "dllimport.h"
#include "gcheaputilities.h"
#include "comdelegate.h"
@@ -47,7 +46,6 @@
#include "genericdict.h"
#include "array.h"
#include "debuginfostore.h"
-#include "security.h"
#include "safemath.h"
#include "runtimehandles.h"
#include "sigbuilder.h"
@@ -68,6 +66,10 @@
#include "interpreter.h"
#endif // FEATURE_INTERPRETER
+#ifdef FEATURE_PERFMAP
+#include "perfmap.h"
+#endif
+
// The Stack Overflow probe takes place in the COOPERATIVE_TRANSITION_BEGIN() macro
//
@@ -1783,9 +1785,7 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
fieldAttribs,
NULL,
(flags & CORINFO_ACCESS_INIT_ARRAY) ? NULL : pField, // For InitializeArray, we don't need tocheck the type of the field.
- accessCheckOptions,
- FALSE /*checkTargetMethodTransparency*/,
- TRUE /*checkTargetTypeTransparency*/);
+ accessCheckOptions);
if (!canAccess)
{
@@ -1924,14 +1924,6 @@ CEEInfo::findCallSiteSig(
if (TypeFromToken(sigMethTok) == mdtMemberRef)
{
IfFailThrow(module->GetMDImport()->GetNameAndSigOfMemberRef(sigMethTok, &pSig, &cbSig, &szName));
-
- // Defs have already been checked by the loader for validity
- // However refs need to be checked.
- if (!Security::CanSkipVerification(module->GetDomainAssembly()))
- {
- // Can pass 0 for the flags, since it is only used for defs.
- IfFailThrow(validateTokenSig(sigMethTok, pSig, cbSig, 0, module->GetMDImport()));
- }
}
else if (TypeFromToken(sigMethTok) == mdtMethodDef)
{
@@ -3093,6 +3085,7 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr
pResult->signature = NULL;
pResult->indirectFirstOffset = 0;
+ pResult->indirectSecondOffset = 0;
// Unless we decide otherwise, just do the lookup via a helper function
pResult->indirections = CORINFO_USEHELPER;
@@ -3139,9 +3132,6 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr
#ifdef FEATURE_READYTORUN_COMPILER
if (IsReadyToRunCompilation())
{
-#if defined(_TARGET_ARM_)
- ThrowHR(E_NOTIMPL); /* TODO - NYI */
-#endif
pResultLookup->lookupKind.runtimeLookupArgs = NULL;
switch (entryKind)
@@ -3307,6 +3297,12 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr
IfFailThrow(sigptr.GetData(&data));
pResult->offsets[2] = sizeof(TypeHandle) * data;
+ if (MethodTable::IsPerInstInfoRelative())
+ {
+ pResult->indirectFirstOffset = 1;
+ pResult->indirectSecondOffset = 1;
+ }
+
return;
}
else if (type == ELEMENT_TYPE_GENERICINST &&
@@ -3554,6 +3550,12 @@ NoSpecialCase:
// Next indirect through the dictionary appropriate to this instantiated type
pResult->offsets[1] = sizeof(TypeHandle*) * (pContextMT->GetNumDicts() - 1);
+
+ if (MethodTable::IsPerInstInfoRelative())
+ {
+ pResult->indirectFirstOffset = 1;
+ pResult->indirectSecondOffset = 1;
+ }
}
}
}
@@ -5554,9 +5556,7 @@ void CEEInfo::getCallInfo(
pCalleeForSecurity->GetAttrs(),
pCalleeForSecurity,
NULL,
- accessCheckOptions,
- FALSE,
- TRUE
+ accessCheckOptions
);
// If we were allowed access to the exact method, but it is on a type that has a type parameter
@@ -5576,11 +5576,10 @@ void CEEInfo::getCallInfo(
// No accees check is need for Var, MVar, or FnPtr.
if (pTypeParamMT != NULL)
- canAccessMethod = ClassLoader::CanAccessClassForExtraChecks(&accessContext,
- pTypeParamMT,
- typeParam.GetAssembly(),
- accessCheckOptions,
- TRUE);
+ canAccessMethod = ClassLoader::CanAccessClass(&accessContext,
+ pTypeParamMT,
+ typeParam.GetAssembly(),
+ accessCheckOptions);
}
pResult->accessAllowed = canAccessMethod ? CORINFO_ACCESS_ALLOWED : CORINFO_ACCESS_ILLEGAL;
@@ -6457,6 +6456,48 @@ const char* CEEInfo::getMethodName (CORINFO_METHOD_HANDLE ftnHnd, const char** s
return result;
}
+const char* CEEInfo::getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftnHnd, const char** className, const char** namespaceName)
+{
+ CONTRACTL {
+ SO_TOLERANT;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ } CONTRACTL_END;
+
+ const char* result = NULL;
+ const char* classResult = NULL;
+ const char* namespaceResult = NULL;
+
+ JIT_TO_EE_TRANSITION();
+
+ MethodDesc *ftn = GetMethod(ftnHnd);
+ mdMethodDef token = ftn->GetMemberDef();
+
+ if (!IsNilToken(token))
+ {
+ if (!FAILED(ftn->GetMDImport()->GetNameOfMethodDef(token, &result)))
+ {
+ MethodTable* pMT = ftn->GetMethodTable();
+ classResult = pMT->GetFullyQualifiedNameInfo(&namespaceResult);
+ }
+ }
+
+ if (className != NULL)
+ {
+ *className = classResult;
+ }
+
+ if (namespaceName != NULL)
+ {
+ *namespaceName = namespaceResult;
+ }
+
+ EE_TO_JIT_TRANSITION();
+
+ return result;
+}
+
/*********************************************************************/
DWORD CEEInfo::getMethodAttribs (CORINFO_METHOD_HANDLE ftn)
{
@@ -6494,13 +6535,10 @@ DWORD CEEInfo::getMethodAttribsInternal (CORINFO_METHOD_HANDLE ftn)
if (pMD->IsLCGMethod())
{
-#ifndef CROSSGEN_COMPILE
-#endif // !CROSSGEN_COMPILE
-
return CORINFO_FLG_STATIC | CORINFO_FLG_DONT_INLINE | CORINFO_FLG_NOSECURITYWRAP;
}
- DWORD result = 0;
+ DWORD result = CORINFO_FLG_NOSECURITYWRAP;
// <REVISIT_TODO>@todo: can we git rid of CORINFO_FLG_ stuff and just include cor.h?</REVISIT_TODO>
@@ -6514,6 +6552,8 @@ DWORD CEEInfo::getMethodAttribsInternal (CORINFO_METHOD_HANDLE ftn)
result |= CORINFO_FLG_SYNCH;
if (pMD->IsFCallOrIntrinsic())
result |= CORINFO_FLG_NOGCCHECK | CORINFO_FLG_INTRINSIC;
+ if (pMD->IsJitIntrinsic())
+ result |= CORINFO_FLG_JIT_INTRINSIC;
if (IsMdVirtual(attribs))
result |= CORINFO_FLG_VIRTUAL;
if (IsMdAbstract(attribs))
@@ -6554,11 +6594,6 @@ DWORD CEEInfo::getMethodAttribsInternal (CORINFO_METHOD_HANDLE ftn)
result |= CORINFO_FLG_PINVOKE;
}
- if (!pMD->IsInterceptedForDeclSecurity())
- {
- result |= CORINFO_FLG_NOSECURITYWRAP;
- }
-
if (IsMdRequireSecObject(attribs))
{
// Assume all methods marked as DynamicSecurity are
@@ -6640,15 +6675,6 @@ void CEEInfo::setMethodAttribs (
}
}
- // Both CORINFO_FLG_UNVERIFIABLE and CORINFO_FLG_VERIFIABLE cannot be set
- _ASSERTE(!(attribs & CORINFO_FLG_UNVERIFIABLE) ||
- !(attribs & CORINFO_FLG_VERIFIABLE ));
-
- if (attribs & CORINFO_FLG_VERIFIABLE)
- ftn->SetIsVerified(TRUE);
- else if (attribs & CORINFO_FLG_UNVERIFIABLE)
- ftn->SetIsVerified(FALSE);
-
EE_TO_JIT_TRANSITION();
}
@@ -6870,7 +6896,8 @@ bool getILIntrinsicImplementationForUnsafe(MethodDesc * ftn,
methInfo->options = (CorInfoOptions)0;
return true;
}
- else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_AS)->GetMemberDef())
+ else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_AS)->GetMemberDef() ||
+ tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__OBJECT_AS)->GetMemberDef())
{
// Return the argument that was passed in.
static const BYTE ilcode[] = { CEE_LDARG_0, CEE_RET };
@@ -6881,7 +6908,8 @@ bool getILIntrinsicImplementationForUnsafe(MethodDesc * ftn,
methInfo->options = (CorInfoOptions)0;
return true;
}
- else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_ADD)->GetMemberDef())
+ else if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__BYREF_ADD)->GetMemberDef() ||
+ tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__PTR_ADD)->GetMemberDef())
{
mdToken tokGenericArg = FindGenericMethodArgTypeSpec(MscorlibBinder::GetModule()->GetMDImport());
@@ -7204,31 +7232,41 @@ getMethodInfoHelper(
bool fILIntrinsic = false;
MethodTable * pMT = ftn->GetMethodTable();
-
- if (MscorlibBinder::IsClass(pMT, CLASS__JIT_HELPERS))
- {
- fILIntrinsic = getILIntrinsicImplementation(ftn, methInfo);
- }
- else if (MscorlibBinder::IsClass(pMT, CLASS__UNSAFE))
- {
- fILIntrinsic = getILIntrinsicImplementationForUnsafe(ftn, methInfo);
- }
- else if (MscorlibBinder::IsClass(pMT, CLASS__INTERLOCKED))
- {
- fILIntrinsic = getILIntrinsicImplementationForInterlocked(ftn, methInfo);
- }
- else if (MscorlibBinder::IsClass(pMT, CLASS__VOLATILE))
- {
- fILIntrinsic = getILIntrinsicImplementationForVolatile(ftn, methInfo);
- }
- else if (MscorlibBinder::IsClass(pMT, CLASS__RUNTIME_HELPERS))
+
+ if (pMT->GetModule()->IsSystem())
{
- fILIntrinsic = getILIntrinsicImplementationForRuntimeHelpers(ftn, methInfo);
+ if (MscorlibBinder::IsClass(pMT, CLASS__JIT_HELPERS))
+ {
+ fILIntrinsic = getILIntrinsicImplementation(ftn, methInfo);
+ }
+ else if (MscorlibBinder::IsClass(pMT, CLASS__UNSAFE))
+ {
+ fILIntrinsic = getILIntrinsicImplementationForUnsafe(ftn, methInfo);
+ }
+ else if (MscorlibBinder::IsClass(pMT, CLASS__INTERLOCKED))
+ {
+ fILIntrinsic = getILIntrinsicImplementationForInterlocked(ftn, methInfo);
+ }
+ else if (MscorlibBinder::IsClass(pMT, CLASS__VOLATILE))
+ {
+ fILIntrinsic = getILIntrinsicImplementationForVolatile(ftn, methInfo);
+ }
+ else if (MscorlibBinder::IsClass(pMT, CLASS__RUNTIME_HELPERS))
+ {
+ fILIntrinsic = getILIntrinsicImplementationForRuntimeHelpers(ftn, methInfo);
+ }
}
if (!fILIntrinsic)
{
getMethodInfoILMethodHeaderHelper(header, methInfo);
+
+ // Workaround for https://github.com/dotnet/coreclr/issues/1279
+ // Set init locals bit to zero for system module unless profiler may have overrided it. Remove once we have
+ // better solution for this issue.
+ if (pMT->GetModule()->IsSystem() && !(CORProfilerDisableAllNGenImages() || CORProfilerUseProfileImages()))
+ methInfo->options = (CorInfoOptions)0;
+
pLocalSig = header->LocalVarSig;
cbLocalSig = header->cbLocalVarSig;
}
@@ -7384,12 +7422,6 @@ CEEInfo::getMethodInfo(
else
{
/* Get the IL header */
- /* <REVISIT_TODO>TODO: canInline already did validation, however, we do it again
- here because NGEN uses this function without calling canInline
- It would be nice to avoid this redundancy </REVISIT_TODO>*/
- Module* pModule = ftn->GetModule();
-
- bool verify = !Security::CanSkipVerification(ftn);
if (ftn->IsDynamicMethod())
{
@@ -7397,28 +7429,7 @@ CEEInfo::getMethodInfo(
}
else
{
- COR_ILMETHOD_DECODER::DecoderStatus status = COR_ILMETHOD_DECODER::SUCCESS;
- COR_ILMETHOD_DECODER header(ftn->GetILHeader(TRUE), ftn->GetMDImport(), verify ? &status : NULL);
-
- // If we get a verification error then we try to demand SkipVerification for the module
- if (status == COR_ILMETHOD_DECODER::VERIFICATION_ERROR &&
- Security::CanSkipVerification(pModule->GetDomainAssembly()))
- {
- status = COR_ILMETHOD_DECODER::SUCCESS;
- }
-
- if (status != COR_ILMETHOD_DECODER::SUCCESS)
- {
- if (status == COR_ILMETHOD_DECODER::VERIFICATION_ERROR)
- {
- // Throw a verification HR
- COMPlusThrowHR(COR_E_VERIFICATION);
- }
- else
- {
- COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL);
- }
- }
+ COR_ILMETHOD_DECODER header(ftn->GetILHeader(TRUE), ftn->GetMDImport(), NULL);
getMethodInfoHelper(ftn, ftnHnd, &header, methInfo);
}
@@ -7545,25 +7556,6 @@ CorInfoInline CEEInfo::canInline (CORINFO_METHOD_HANDLE hCaller,
Module * pOrigCallerModule;
pOrigCallerModule = pOrigCaller->GetLoaderModule();
- // Prevent recursive compiling/inlining/verifying
- if (pOrigCaller != pCallee)
- {
- // The Inliner may not do code verification.
- // So never inline anything that is unverifiable / bad code.
- if (!Security::CanSkipVerification(pCallee))
- {
- // Inlinee needs to be verifiable
- if (!pCallee->IsVerifiable())
- {
- result = INLINE_NEVER;
- szFailReason = "Inlinee is not verifiable";
- goto exit;
- }
- }
- }
-
- // We check this here as the call to MethodDesc::IsVerifiable()
- // may set CORINFO_FLG_DONT_INLINE.
if (pCallee->IsNotInline())
{
result = INLINE_NEVER;
@@ -7672,64 +7664,10 @@ CorInfoInline CEEInfo::canInline (CORINFO_METHOD_HANDLE hCaller,
{
// #rejit
//
- // See if rejit-specific flags for the caller disable inlining
- if ((ReJitManager::GetCurrentReJitFlags(pCaller) &
- COR_PRF_CODEGEN_DISABLE_INLINING) != 0)
- {
- result = INLINE_FAIL;
- szFailReason = "ReJIT request disabled inlining from caller";
- goto exit;
- }
-
- // If the profiler has set a mask preventing inlining, always return
- // false to the jit.
- if (CORProfilerDisableInlining())
- {
- result = INLINE_FAIL;
- szFailReason = "Profiler disabled inlining globally";
- goto exit;
- }
-
- // If the profiler wishes to be notified of JIT events and the result from
- // the above tests will cause a function to be inlined, we need to tell the
- // profiler that this inlining is going to take place, and give them a
- // chance to prevent it.
- {
- BEGIN_PIN_PROFILER(CORProfilerTrackJITInfo());
- if (pCaller->IsILStub() || pCallee->IsILStub())
- {
- // do nothing
- }
- else
- {
- BOOL fShouldInline;
-
- HRESULT hr = g_profControlBlock.pProfInterface->JITInlining(
- (FunctionID)pCaller,
- (FunctionID)pCallee,
- &fShouldInline);
-
- if (SUCCEEDED(hr) && !fShouldInline)
- {
- result = INLINE_FAIL;
- szFailReason = "Profiler disabled inlining locally";
- goto exit;
- }
- }
- END_PIN_PROFILER();
- }
- }
-#endif // PROFILING_SUPPORTED
-
-
-#ifdef PROFILING_SUPPORTED
- if (CORProfilerPresent())
- {
- // #rejit
- //
- // See if rejit-specific flags for the caller disable inlining
- if ((ReJitManager::GetCurrentReJitFlags(pCaller) &
- COR_PRF_CODEGEN_DISABLE_INLINING) != 0)
+ // Currently the rejit path is the only path which sets this.
+ // If we get more reasons to set this then we may need to change
+ // the failure reason message or disambiguate them.
+ if (!m_allowInlining)
{
result = INLINE_FAIL;
szFailReason = "ReJIT request disabled inlining from caller";
@@ -8018,8 +7956,7 @@ CorInfoInstantiationVerification
goto exit;
}
- result = pMethod->IsVerifiable() ? INSTVER_GENERIC_PASSED_VERIFICATION
- : INSTVER_GENERIC_FAILED_VERIFICATION;
+ result = INSTVER_GENERIC_PASSED_VERIFICATION;
exit: ;
@@ -8074,16 +8011,6 @@ bool CEEInfo::canTailCall (CORINFO_METHOD_HANDLE hCaller,
goto exit;
}
- // TailCalls will throw off security stackwalking logic when there is a declarative Assert
- // Note that this check will also include declarative demands. It's OK to do a tailcall in
- // those cases, but we currently don't have a way to check only for declarative Asserts.
- if (pCaller->IsInterceptedForDeclSecurity())
- {
- result = false;
- szFailReason = "Caller has declarative security";
- goto exit;
- }
-
if (!fIsTailPrefix)
{
mdMethodDef callerToken = pCaller->GetMemberDef();
@@ -8581,7 +8508,8 @@ CONTRACTL {
/*********************************************************************/
void CEEInfo::getMethodVTableOffset (CORINFO_METHOD_HANDLE methodHnd,
unsigned * pOffsetOfIndirection,
- unsigned * pOffsetAfterIndirection)
+ unsigned * pOffsetAfterIndirection,
+ bool * isRelative)
{
CONTRACTL {
SO_TOLERANT;
@@ -8602,8 +8530,9 @@ void CEEInfo::getMethodVTableOffset (CORINFO_METHOD_HANDLE methodHnd,
// better be in the vtable
_ASSERTE(method->GetSlot() < method->GetMethodTable()->GetNumVirtuals());
- *pOffsetOfIndirection = MethodTable::GetVtableOffset() + MethodTable::GetIndexOfVtableIndirection(method->GetSlot()) * sizeof(PTR_PCODE);
+ *pOffsetOfIndirection = MethodTable::GetVtableOffset() + MethodTable::GetIndexOfVtableIndirection(method->GetSlot()) * sizeof(MethodTable::VTableIndir_t);
*pOffsetAfterIndirection = MethodTable::GetIndexAfterVtableIndirection(method->GetSlot()) * sizeof(PCODE);
+ *isRelative = MethodTable::VTableIndir_t::isRelative ? 1 : 0;
EE_TO_JIT_TRANSITION_LEAF();
}
@@ -9391,7 +9320,6 @@ CorInfoType CEEInfo::getHFAType(CORINFO_CLASS_HANDLE hClass)
CorInfoType result = CORINFO_TYPE_UNDEF;
-#ifdef FEATURE_HFA
JIT_TO_EE_TRANSITION();
TypeHandle VMClsHnd(hClass);
@@ -9399,7 +9327,6 @@ CorInfoType CEEInfo::getHFAType(CORINFO_CLASS_HANDLE hClass)
result = asCorInfoType(VMClsHnd.GetHFAType());
EE_TO_JIT_TRANSITION();
-#endif
return result;
}
@@ -11819,6 +11746,7 @@ CorJitResult invokeCompileMethodHelper(EEJitManager *jitMgr,
#ifdef FEATURE_INTERPRETER
static ConfigDWORD s_InterpreterFallback;
+ bool isInterpreterStub = false;
bool interpreterFallback = (s_InterpreterFallback.val(CLRConfig::INTERNAL_InterpreterFallback) != 0);
if (interpreterFallback == false)
@@ -11827,7 +11755,10 @@ CorJitResult invokeCompileMethodHelper(EEJitManager *jitMgr,
// (We assume that importation is completely architecture-independent, or at least nearly so.)
if (FAILED(ret) && !jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) && !jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_MAKEFINALCODE))
{
- ret = Interpreter::GenerateInterpreterStub(comp, info, nativeEntry, nativeSizeOfCode);
+ if (SUCCEEDED(ret = Interpreter::GenerateInterpreterStub(comp, info, nativeEntry, nativeSizeOfCode)))
+ {
+ isInterpreterStub = true;
+ }
}
}
@@ -11847,7 +11778,10 @@ CorJitResult invokeCompileMethodHelper(EEJitManager *jitMgr,
// (We assume that importation is completely architecture-independent, or at least nearly so.)
if (FAILED(ret) && !jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) && !jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_MAKEFINALCODE))
{
- ret = Interpreter::GenerateInterpreterStub(comp, info, nativeEntry, nativeSizeOfCode);
+ if (SUCCEEDED(ret = Interpreter::GenerateInterpreterStub(comp, info, nativeEntry, nativeSizeOfCode)))
+ {
+ isInterpreterStub = true;
+ }
}
}
#else
@@ -11882,7 +11816,13 @@ CorJitResult invokeCompileMethodHelper(EEJitManager *jitMgr,
#if defined(FEATURE_GDBJIT)
- if (SUCCEEDED(ret) && *nativeEntry != NULL)
+ bool isJittedEntry = SUCCEEDED(ret) && *nativeEntry != NULL;
+
+#ifdef FEATURE_INTERPRETER
+ isJittedEntry &= !isInterpreterStub;
+#endif // FEATURE_INTERPRETER
+
+ if (isJittedEntry)
{
CodeHeader* pCH = ((CodeHeader*)((PCODE)*nativeEntry & ~1)) - 1;
pCH->SetCalledMethods((PTR_VOID)comp->GetCalledMethods());
@@ -11926,13 +11866,6 @@ CorJitResult invokeCompileMethod(EEJitManager *jitMgr,
return ret;
}
-CORJIT_FLAGS GetCompileFlagsIfGenericInstantiation(
- CORINFO_METHOD_HANDLE method,
- CORJIT_FLAGS compileFlags,
- ICorJitInfo * pCorJitInfo,
- BOOL * raiseVerificationException,
- BOOL * unverifiableGenericCode);
-
CorJitResult CallCompileMethodWithSEHWrapper(EEJitManager *jitMgr,
CEEInfo *comp,
struct CORINFO_METHOD_INFO *info,
@@ -12182,22 +12115,10 @@ CORJIT_FLAGS GetCompileFlags(MethodDesc * ftn, CORJIT_FLAGS flags, CORINFO_METHO
}
}
- //
- // Verification flags
- //
-
-#ifdef _DEBUG
- if (g_pConfig->IsJitVerificationDisabled())
- flags.Set(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION);
-#endif // _DEBUG
-
- if (!flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) && Security::CanSkipVerification(ftn))
- flags.Set(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION);
+ flags.Set(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION);
if (ftn->IsILStub())
{
- flags.Set(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION);
-
// no debug info available for IL stubs
flags.Clear(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO);
}
@@ -12205,148 +12126,6 @@ CORJIT_FLAGS GetCompileFlags(MethodDesc * ftn, CORJIT_FLAGS flags, CORINFO_METHO
return flags;
}
-#if defined(_WIN64)
-//The implementation of Jit64 prevents it from both inlining and verifying at the same time. This causes a
-//perf problem for code that adopts Transparency. This code attempts to enable inlining in spite of that
-//limitation in that scenario.
-//
-//This only works for real methods. If the method isn't IsIL, then IsVerifiable will AV. That would be a
-//bad thing (TM).
-BOOL IsTransparentMethodSafeToSkipVerification(CORJIT_FLAGS flags, MethodDesc * ftn)
-{
- STANDARD_VM_CONTRACT;
-
- BOOL ret = FALSE;
- if (!flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY) && !flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION)
- && Security::IsMethodTransparent(ftn) &&
- ((ftn->IsIL() && !ftn->IsUnboxingStub()) ||
- (ftn->IsDynamicMethod() && !ftn->IsILStub())))
- {
- EX_TRY
- {
- //Verify the method
- ret = ftn->IsVerifiable();
- }
- EX_CATCH
- {
- //If the jit throws an exception, do not let it leak out of here. For example, we can sometimes
- //get an IPE that we could recover from in the Jit (i.e. invalid local in a method with skip
- //verification).
- }
- EX_END_CATCH(RethrowTerminalExceptions)
- }
- return ret;
-}
-#else
-#define IsTransparentMethodSafeToSkipVerification(flags,ftn) (FALSE)
-#endif //_WIN64
-
-/*********************************************************************/
-// We verify generic code once and for all using the typical open type,
-// and then no instantiations need to be verified. If verification
-// failed, then we need to throw an exception whenever we try
-// to compile a real instantiation
-
-CORJIT_FLAGS GetCompileFlagsIfGenericInstantiation(
- CORINFO_METHOD_HANDLE method,
- CORJIT_FLAGS compileFlags,
- ICorJitInfo * pCorJitInfo,
- BOOL * raiseVerificationException,
- BOOL * unverifiableGenericCode)
-{
- STANDARD_VM_CONTRACT;
-
- *raiseVerificationException = FALSE;
- *unverifiableGenericCode = FALSE;
-
- // If we have already decided to skip verification, keep on going.
- if (compileFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION))
- return compileFlags;
-
- CorInfoInstantiationVerification ver = pCorJitInfo->isInstantiationOfVerifiedGeneric(method);
-
- switch(ver)
- {
- case INSTVER_NOT_INSTANTIATION:
- // Non-generic, or open instantiation of a generic type/method
- if (IsTransparentMethodSafeToSkipVerification(compileFlags, (MethodDesc*)method))
- compileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION);
- return compileFlags;
-
- case INSTVER_GENERIC_PASSED_VERIFICATION:
- // If the typical instantiation is verifiable, there is no need
- // to verify the concrete instantiations
- compileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION);
- return compileFlags;
-
- case INSTVER_GENERIC_FAILED_VERIFICATION:
-
- *unverifiableGenericCode = TRUE;
-
- // The generic method is not verifiable.
- // Check if it has SkipVerification permission
- MethodDesc * pGenMethod = GetMethod(method)->LoadTypicalMethodDefinition();
-
- CORINFO_METHOD_HANDLE genMethodHandle = CORINFO_METHOD_HANDLE(pGenMethod);
-
- CorInfoCanSkipVerificationResult canSkipVer;
- canSkipVer = pCorJitInfo->canSkipMethodVerification(genMethodHandle);
-
- switch(canSkipVer)
- {
-
-#ifdef FEATURE_PREJIT
- case CORINFO_VERIFICATION_DONT_JIT:
- {
- // Transparent code could be partial trust, but we don't know at NGEN time.
- // This is the flag that NGEN passes to the JIT to tell it to give-up if it
- // hits unverifiable code. Since we've already hit unverifiable code,
- // there's no point in starting the JIT, just to have it give up, so we
- // give up here.
- _ASSERTE(compileFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_PREJIT));
- *raiseVerificationException = TRUE;
- return CORJIT_FLAGS(); // This value will not be used
- }
-#else // FEATURE_PREJIT
- // Need to have this case here to keep the MAC build happy
- case CORINFO_VERIFICATION_DONT_JIT:
- {
- _ASSERTE(!"We should never get here");
- return compileFlags;
- }
-#endif // FEATURE_PREJIT
-
- case CORINFO_VERIFICATION_CANNOT_SKIP:
- {
- // For unverifiable generic code without SkipVerification permission,
- // we cannot ask the compiler to emit CORINFO_HELP_VERIFICATION in
- // unverifiable branches as the compiler cannot determine the unverifiable
- // branches while compiling the concrete instantiation. Instead,
- // just throw a VerificationException right away.
- *raiseVerificationException = TRUE;
- return CORJIT_FLAGS(); // This value will not be used
- }
-
- case CORINFO_VERIFICATION_CAN_SKIP:
- {
- compileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION);
- return compileFlags;
- }
-
- case CORINFO_VERIFICATION_RUNTIME_CHECK:
- {
- // Compile the method without CORJIT_FLAG_SKIP_VERIFICATION.
- // The compiler will know to add a call to
- // CORINFO_HELP_VERIFICATION_RUNTIME_CHECK, and then to skip verification.
- return compileFlags;
- }
- }
- }
-
- _ASSERTE(!"We should never get here");
- return compileFlags;
-}
-
// ********************************************************************
// Throw the right type of exception for the given JIT result
@@ -12554,7 +12333,8 @@ PCODE UnsafeJitFunction(MethodDesc* ftn, COR_ILMETHOD_DECODER* ILHeader, CORJIT_
for (;;)
{
#ifndef CROSSGEN_COMPILE
- CEEJitInfo jitInfo(ftn, ILHeader, jitMgr, flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY));
+ CEEJitInfo jitInfo(ftn, ILHeader, jitMgr, flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY),
+ !flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_NO_INLINING));
#else
// This path should be only ever used for verification in crossgen and so we should not need EEJitManager
_ASSERTE(flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY));
@@ -12604,26 +12384,12 @@ PCODE UnsafeJitFunction(MethodDesc* ftn, COR_ILMETHOD_DECODER* ILHeader, CORJIT_
pMethodForSecurity->GetAttrs(),
pMethodForSecurity,
NULL,
- accessCheckOptions,
- TRUE /*Check method transparency*/,
- TRUE /*Check type transparency*/))
+ accessCheckOptions))
{
EX_THROW(EEMethodException, (pMethodForSecurity));
}
}
- BOOL raiseVerificationException, unverifiableGenericCode;
-
- flags = GetCompileFlagsIfGenericInstantiation(
- ftnHnd,
- flags,
- &jitInfo,
- &raiseVerificationException,
- &unverifiableGenericCode);
-
- if (raiseVerificationException)
- COMPlusThrow(kVerificationException);
-
CorJitResult res;
PBYTE nativeEntry;
ULONG sizeOfCode;
@@ -12720,11 +12486,6 @@ PCODE UnsafeJitFunction(MethodDesc* ftn, COR_ILMETHOD_DECODER* ILHeader, CORJIT_
if (flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY))
{
- // The method must been processed by the verifier. Note that it may
- // either have been marked as verifiable or unverifiable.
- // ie. IsVerified() does not imply IsVerifiable()
- _ASSERTE(ftn->IsVerified());
-
// We are done
break;
}
@@ -12829,6 +12590,10 @@ void Module::LoadHelperTable()
BYTE * curEntry = table;
BYTE * tableEnd = table + tableSize;
+#ifdef FEATURE_PERFMAP
+ PerfMap::LogStubs(__FUNCTION__, GetSimpleName(), (PCODE)table, tableSize);
+#endif
+
#ifdef LOGGING
int iEntryNumber = 0;
#endif // LOGGING
diff --git a/src/vm/jitinterface.h b/src/vm/jitinterface.h
index 1dccdb24e9..93470ecbac 100644
--- a/src/vm/jitinterface.h
+++ b/src/vm/jitinterface.h
@@ -649,6 +649,7 @@ public:
// ICorMethodInfo stuff
const char* getMethodName (CORINFO_METHOD_HANDLE ftnHnd, const char** scopeName);
+ const char* getMethodNameFromMetadata (CORINFO_METHOD_HANDLE ftnHnd, const char** className, const char** namespaceName);
unsigned getMethodHash (CORINFO_METHOD_HANDLE ftnHnd);
DWORD getMethodAttribs (CORINFO_METHOD_HANDLE ftnHnd);
@@ -728,8 +729,8 @@ public:
void getMethodVTableOffset (
CORINFO_METHOD_HANDLE methodHnd,
unsigned * pOffsetOfIndirection,
- unsigned * pOffsetAfterIndirection
- );
+ unsigned * pOffsetAfterIndirection,
+ bool * isRelative);
CORINFO_METHOD_HANDLE resolveVirtualMethod(
CORINFO_METHOD_HANDLE virtualMethod,
@@ -1053,16 +1054,17 @@ public:
DWORD getExpectedTargetArchitecture();
- CEEInfo(MethodDesc * fd = NULL, bool fVerifyOnly = false) :
+ CEEInfo(MethodDesc * fd = NULL, bool fVerifyOnly = false, bool fAllowInlining = true) :
m_pOverride(NULL),
m_pMethodBeingCompiled(fd),
m_fVerifyOnly(fVerifyOnly),
m_pThread(GetThread()),
m_hMethodForSecurity_Key(NULL),
- m_pMethodForSecurity_Value(NULL)
+ m_pMethodForSecurity_Value(NULL),
#if defined(FEATURE_GDBJIT)
- , m_pCalledMethods(NULL)
+ m_pCalledMethods(NULL),
#endif
+ m_allowInlining(fAllowInlining)
{
LIMITED_METHOD_CONTRACT;
}
@@ -1155,6 +1157,8 @@ protected:
CalledMethod * m_pCalledMethods;
#endif
+ bool m_allowInlining;
+
// Tracking of module activation dependencies. We have two flavors:
// - Fast one that gathers generic arguments from EE handles, but does not work inside generic context.
// - Slow one that operates on typespec and methodspecs from metadata.
@@ -1331,8 +1335,8 @@ public:
#endif
CEEJitInfo(MethodDesc* fd, COR_ILMETHOD_DECODER* header,
- EEJitManager* jm, bool fVerifyOnly)
- : CEEInfo(fd, fVerifyOnly),
+ EEJitManager* jm, bool fVerifyOnly, bool allowInlining = true)
+ : CEEInfo(fd, fVerifyOnly, allowInlining),
m_jitManager(jm),
m_CodeHeader(NULL),
m_ILHeader(header),
@@ -1465,7 +1469,6 @@ protected :
void* m_pvGphProfilerHandle;
} m_gphCache;
-
};
#endif // CROSSGEN_COMPILE
diff --git a/src/vm/listlock.cpp b/src/vm/listlock.cpp
deleted file mode 100644
index 450e85aef5..0000000000
--- a/src/vm/listlock.cpp
+++ /dev/null
@@ -1,96 +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.
-// ===========================================================================
-// File: ListLock.cpp
-//
-
-//
-// ===========================================================================
-// This file decribes the list lock and deadlock aware list lock.
-// ===========================================================================
-
-
-#include "common.h"
-#include "listlock.h"
-#include "listlock.inl"
-
-ListLockEntry::ListLockEntry(ListLock *pList, void *pData, const char *description)
- : m_deadlock(description),
- m_pList(pList),
- m_pData(pData),
- m_Crst(CrstListLock,
- (CrstFlags)(CRST_REENTRANCY | (pList->IsHostBreakable()?CRST_HOST_BREAKABLE:0))),
- m_pszDescription(description),
- m_pNext(NULL),
- m_dwRefCount(1),
- m_hrResultCode(S_FALSE),
- m_hInitException(NULL),
- m_pLoaderAllocator(NULL)
-#ifdef FEATURE_CORRUPTING_EXCEPTIONS
- ,
- m_CorruptionSeverity(NotCorrupting)
-#endif // FEATURE_CORRUPTING_EXCEPTIONS
-{
- WRAPPER_NO_CONTRACT;
-}
-
-ListLockEntry *ListLockEntry::Find(ListLock* pLock, LPVOID pPointer, const char *description)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(pLock->HasLock());
-
- ListLockEntry *pEntry = pLock->Find(pPointer);
- if (pEntry==NULL)
- {
- pEntry = new ListLockEntry(pLock, pPointer, description);
- pLock->AddElement(pEntry);
- }
- else
- pEntry->AddRef();
-
- return pEntry;
-};
-
-void ListLockEntry::AddRef()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- PRECONDITION(CheckPointer(this));
- }
- CONTRACTL_END;
-
- FastInterlockIncrement((LONG*)&m_dwRefCount);
-}
-
-void ListLockEntry::Release()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- MODE_ANY;
- PRECONDITION(CheckPointer(this));
- }
- CONTRACTL_END;
-
- ListLockHolder lock(m_pList);
-
- if (FastInterlockDecrement((LONG*)&m_dwRefCount) == 0)
- {
- // Remove from list
- m_pList->Unlink(this);
- delete this;
- }
-};
-
diff --git a/src/vm/listlock.h b/src/vm/listlock.h
index e16741a7d7..db953c8b55 100644
--- a/src/vm/listlock.h
+++ b/src/vm/listlock.h
@@ -17,7 +17,8 @@
#include "threads.h"
#include "crst.h"
-class ListLock;
+template < typename ELEMENT >
+class ListLockBase;
// This structure is used for running class init methods or JITing methods
// (m_pData points to a FunctionDesc). This class cannot have a destructor since it is used
// in function that also have EX_TRY's and the VC compiler doesn't allow classes with destructors
@@ -25,9 +26,14 @@ class ListLock;
// <TODO>@FUTURE Keep a pool of these (e.g. an array), so we don't have to allocate on the fly</TODO>
// m_hInitException contains a handle to the exception thrown by the class init. This
// allows us to throw this information to the caller on subsequent class init attempts.
-class ListLockEntry
+template < typename ELEMENT >
+class ListLockEntryBase
{
- friend class ListLock;
+ friend class ListLockBase<ELEMENT>;
+ typedef ListLockEntryBase<ELEMENT> Entry_t;
+ typedef ListLockBase<ELEMENT> List_t;
+ typedef typename List_t::LockHolder ListLockHolder;
+
public:
#ifdef _DEBUG
@@ -40,11 +46,11 @@ public:
#endif // DEBUG
DeadlockAwareLock m_deadlock;
- ListLock * m_pList;
- void * m_pData;
+ List_t * m_pList;
+ ELEMENT m_data;
Crst m_Crst;
const char * m_pszDescription;
- ListLockEntry * m_pNext;
+ Entry_t * m_pNext;
DWORD m_dwRefCount;
HRESULT m_hrResultCode;
LOADERHANDLE m_hInitException;
@@ -54,9 +60,27 @@ public:
CorruptionSeverity m_CorruptionSeverity;
#endif // FEATURE_CORRUPTING_EXCEPTIONS
- ListLockEntry(ListLock *pList, void *pData, const char *description = NULL);
+ ListLockEntryBase(List_t *pList, ELEMENT data, const char *description = NULL)
+ : m_deadlock(description),
+ m_pList(pList),
+ m_data(data),
+ m_Crst(CrstListLock,
+ (CrstFlags)(CRST_REENTRANCY | (pList->IsHostBreakable() ? CRST_HOST_BREAKABLE : 0))),
+ m_pszDescription(description),
+ m_pNext(NULL),
+ m_dwRefCount(1),
+ m_hrResultCode(S_FALSE),
+ m_hInitException(NULL),
+ m_pLoaderAllocator(dac_cast<PTR_LoaderAllocator>(nullptr))
+#ifdef FEATURE_CORRUPTING_EXCEPTIONS
+ ,
+ m_CorruptionSeverity(NotCorrupting)
+#endif // FEATURE_CORRUPTING_EXCEPTIONS
+ {
+ WRAPPER_NO_CONTRACT;
+ }
- virtual ~ListLockEntry()
+ virtual ~ListLockEntryBase()
{
}
@@ -102,10 +126,65 @@ public:
m_Crst.Leave();
}
- static ListLockEntry *Find(ListLock* pLock, LPVOID pPointer, const char *description = NULL) DAC_EMPTY_RET(NULL);
+ static Entry_t *Find(List_t* pLock, ELEMENT data, const char *description = NULL)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(pLock->HasLock());
+
+ Entry_t *pEntry = pLock->Find(data);
+ if (pEntry == NULL)
+ {
+ pEntry = new Entry_t(pLock, data, description);
+ pLock->AddElement(pEntry);
+ }
+ else
+ pEntry->AddRef();
+
+ return pEntry;
+ };
+
+
+ void AddRef()
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(this));
+ }
+ CONTRACTL_END;
+
+ FastInterlockIncrement((LONG*)&m_dwRefCount);
+ }
+
+ void Release()
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(this));
+ }
+ CONTRACTL_END;
- void AddRef() DAC_EMPTY_ERR();
- void Release() DAC_EMPTY_ERR();
+ ListLockHolder lock(m_pList);
+
+ if (FastInterlockDecrement((LONG*)&m_dwRefCount) == 0)
+ {
+ // Remove from list
+ m_pList->Unlink(this);
+ delete this;
+ }
+ };
#ifdef _DEBUG
BOOL HasLock()
@@ -117,14 +196,14 @@ public:
// LockHolder holds the lock of the element, not the element itself
- DEBUG_NOINLINE static void LockHolderEnter(ListLockEntry *pThis) PUB
+ DEBUG_NOINLINE static void LockHolderEnter(Entry_t *pThis) PUB
{
WRAPPER_NO_CONTRACT;
ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
pThis->Enter();
}
- DEBUG_NOINLINE static void LockHolderLeave(ListLockEntry *pThis) PUB
+ DEBUG_NOINLINE static void LockHolderLeave(Entry_t *pThis) PUB
{
WRAPPER_NO_CONTRACT;
ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
@@ -139,7 +218,7 @@ public:
m_deadlock.EndEnterLock();
}
- typedef Wrapper<ListLockEntry *, ListLockEntry::LockHolderEnter, ListLockEntry::LockHolderLeave> LockHolderBase;
+ typedef Wrapper<Entry_t *, LockHolderEnter, LockHolderLeave> LockHolderBase;
class LockHolder : public LockHolderBase
{
@@ -150,32 +229,36 @@ public:
{
}
- LockHolder(ListLockEntry *value, BOOL take = TRUE)
+ LockHolder(Entry_t *value, BOOL take = TRUE)
: LockHolderBase(value, take)
{
}
BOOL DeadlockAwareAcquire()
{
- if (!m_acquired && m_value != NULL)
+ if (!this->m_acquired && this->m_value != NULL)
{
- if (!m_value->m_deadlock.TryBeginEnterLock())
+ if (!this->m_value->m_deadlock.TryBeginEnterLock())
return FALSE;
- m_value->FinishDeadlockAwareEnter();
- m_acquired = TRUE;
+ this->m_value->FinishDeadlockAwareEnter();
+ this->m_acquired = TRUE;
}
return TRUE;
}
};
};
-class ListLock
+template < typename ELEMENT >
+class ListLockBase
{
+ typedef ListLockBase<ELEMENT> List_t;
+ typedef ListLockEntryBase<ELEMENT> Entry_t;
+
protected:
CrstStatic m_Crst;
BOOL m_fInited;
BOOL m_fHostBreakable; // Lock can be broken by a host for deadlock detection
- ListLockEntry * m_pHead;
+ Entry_t * m_pHead;
public:
@@ -219,7 +302,7 @@ class ListLock
return m_fHostBreakable;
}
- void AddElement(ListLockEntry* pElement)
+ void AddElement(Entry_t* pElement)
{
WRAPPER_NO_CONTRACT;
pElement->m_pNext = m_pHead;
@@ -257,10 +340,39 @@ class ListLock
// Must own the lock before calling this or is ok if the debugger has
// all threads stopped
- ListLockEntry *Find(void *pData);
+ inline Entry_t *Find(ELEMENT data)
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+#ifdef DEBUGGING_SUPPORTED
+ PRECONDITION(m_Crst.OwnedByCurrentThread() ||
+ CORDebuggerAttached()
+ // This condition should be true, but it is awkward to assert it because adding dbginterface.h creates lots of cycles in the includes
+ // It didn't seem valuable enough to refactor out a wrapper just to preserve it
+ /* && g_pDebugInterface->IsStopped() */);
+#else
+ PRECONDITION(m_Crst.OwnedByCurrentThread());
+#endif // DEBUGGING_SUPPORTED
+
+ }
+ CONTRACTL_END;
+
+ Entry_t *pSearch;
+
+ for (pSearch = m_pHead; pSearch != NULL; pSearch = pSearch->m_pNext)
+ {
+ if (pSearch->m_data == data)
+ return pSearch;
+ }
+
+ return NULL;
+ }
// Must own the lock before calling this!
- ListLockEntry* Pop(BOOL unloading = FALSE)
+ Entry_t* Pop(BOOL unloading = FALSE)
{
LIMITED_METHOD_CONTRACT;
#ifdef _DEBUG
@@ -269,13 +381,13 @@ class ListLock
#endif
if(m_pHead == NULL) return NULL;
- ListLockEntry* pEntry = m_pHead;
+ Entry_t* pEntry = m_pHead;
m_pHead = m_pHead->m_pNext;
return pEntry;
}
// Must own the lock before calling this!
- ListLockEntry* Peek()
+ Entry_t* Peek()
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(m_Crst.OwnedByCurrentThread());
@@ -283,12 +395,12 @@ class ListLock
}
// Must own the lock before calling this!
- BOOL Unlink(ListLockEntry *pItem)
+ BOOL Unlink(Entry_t *pItem)
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(m_Crst.OwnedByCurrentThread());
- ListLockEntry *pSearch;
- ListLockEntry *pPrev;
+ Entry_t *pSearch;
+ Entry_t *pPrev;
pPrev = NULL;
@@ -320,21 +432,21 @@ class ListLock
}
#endif
- DEBUG_NOINLINE static void HolderEnter(ListLock *pThis)
+ DEBUG_NOINLINE static void HolderEnter(List_t *pThis)
{
WRAPPER_NO_CONTRACT;
ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
pThis->Enter();
}
- DEBUG_NOINLINE static void HolderLeave(ListLock *pThis)
+ DEBUG_NOINLINE static void HolderLeave(List_t *pThis)
{
WRAPPER_NO_CONTRACT;
ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
pThis->Leave();
}
- typedef Wrapper<ListLock*, ListLock::HolderEnter, ListLock::HolderLeave> LockHolder;
+ typedef Wrapper<List_t*, List_t::HolderEnter, List_t::HolderLeave> LockHolder;
};
class WaitingThreadListElement
@@ -344,6 +456,9 @@ public:
WaitingThreadListElement * m_pNext;
};
+typedef class ListLockBase<void*> ListLock;
+typedef class ListLockEntryBase<void*> ListLockEntry;
+
// Holds the lock of the ListLock
typedef ListLock::LockHolder ListLockHolder;
diff --git a/src/vm/listlock.inl b/src/vm/listlock.inl
deleted file mode 100644
index 17e383edd7..0000000000
--- a/src/vm/listlock.inl
+++ /dev/null
@@ -1,51 +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.
-// ===========================================================================
-//
-
-//
-// File: ListLock.inl
-//
-// ===========================================================================
-// This file decribes the list lock and deadlock aware list lock functions
-// that are inlined but can't go in the header.
-// ===========================================================================
-#ifndef LISTLOCK_INL
-#define LISTLOCK_INL
-
-#include "listlock.h"
-#include "dbginterface.h"
-// Must own the lock before calling this or is ok if the debugger has
-// all threads stopped
-
-inline ListLockEntry *ListLock::Find(void *pData)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- PRECONDITION(CheckPointer(this));
-#ifdef DEBUGGING_SUPPORTED
- PRECONDITION(m_Crst.OwnedByCurrentThread() ||
- CORDebuggerAttached() && g_pDebugInterface->IsStopped());
-#else
- PRECONDITION(m_Crst.OwnedByCurrentThread());
-#endif // DEBUGGING_SUPPORTED
-
- }
- CONTRACTL_END;
-
- ListLockEntry *pSearch;
-
- for (pSearch = m_pHead; pSearch != NULL; pSearch = pSearch->m_pNext)
- {
- if (pSearch->m_pData == pData)
- return pSearch;
- }
-
- return NULL;
-}
-
-
-#endif // LISTLOCK_I
diff --git a/src/vm/loaderallocator.cpp b/src/vm/loaderallocator.cpp
index 1a05bf2c05..ff54277efd 100644
--- a/src/vm/loaderallocator.cpp
+++ b/src/vm/loaderallocator.cpp
@@ -1005,7 +1005,9 @@ void LoaderAllocator::Init(BaseDomain *pDomain, BYTE *pExecutableHeapMemory)
dwExecutableHeapReserveSize,
LOADERHEAP_PROFILE_COUNTER,
NULL,
- TRUE /* Make heap executable */);
+ TRUE /* Make heap executable */,
+ FALSE /* Disable zero-initialization (needed by UMEntryThunkCode::Poison) */
+ );
initReservedMem += dwExecutableHeapReserveSize;
}
diff --git a/src/vm/loaderallocator.hpp b/src/vm/loaderallocator.hpp
index 72fa59857d..b057283136 100644
--- a/src/vm/loaderallocator.hpp
+++ b/src/vm/loaderallocator.hpp
@@ -62,7 +62,9 @@ public:
class StringLiteralMap;
class VirtualCallStubManager;
-class ListLockEntry;
+template <typename ELEMENT>
+class ListLockEntryBase;
+typedef ListLockEntryBase<void*> ListLockEntry;
class LoaderAllocator
{
diff --git a/src/vm/marshalnative.cpp b/src/vm/marshalnative.cpp
index 34d7a861b5..a552ef33f5 100644
--- a/src/vm/marshalnative.cpp
+++ b/src/vm/marshalnative.cpp
@@ -28,7 +28,6 @@
#include "fieldmarshaler.h"
#include "cgensys.h"
#include "gcheaputilities.h"
-#include "security.h"
#include "dbginterface.h"
#include "marshalnative.h"
#include "fcall.h"
diff --git a/src/vm/memberload.cpp b/src/vm/memberload.cpp
index aa5667dd21..86be548cd2 100644
--- a/src/vm/memberload.cpp
+++ b/src/vm/memberload.cpp
@@ -30,7 +30,6 @@
#include "fieldmarshaler.h"
#include "cgensys.h"
#include "gcheaputilities.h"
-#include "security.h"
#include "dbginterface.h"
#include "comdelegate.h"
#include "sigformat.h"
@@ -45,7 +44,6 @@
#include "virtualcallstub.h"
#include "eeconfig.h"
#include "contractimpl.h"
-#include "listlock.inl"
#include "generics.h"
#include "instmethhash.h"
#include "typestring.h"
diff --git a/src/vm/metasig.h b/src/vm/metasig.h
index c2dc42fb9d..bbd326ebe3 100644
--- a/src/vm/metasig.h
+++ b/src/vm/metasig.h
@@ -56,6 +56,7 @@
// T -- TypedReference -- TypedReference
// G -- -- Generic type variable
// M -- -- Generic method variable
+// GI -- -- Generic type instantiation
//
//#DEFINE_METASIG
@@ -128,6 +129,8 @@
#define G(n) METASIG_ATOM(ELEMENT_TYPE_VAR) METASIG_ATOM(n)
#define M(n) METASIG_ATOM(ELEMENT_TYPE_MVAR) METASIG_ATOM(n)
+#define GI(type, n, x) METASIG_ATOM(ELEMENT_TYPE_GENERICINST) type METASIG_ATOM(n) x
+
// The references to other types have special definition in some cases
#ifndef C
#define C(x) METASIG_ATOM(ELEMENT_TYPE_CLASS) METASIG_ATOM(CLASS__ ## x % 0x100) METASIG_ATOM(CLASS__ ## x / 0x100)
@@ -145,6 +148,8 @@
#define G(n) METASIG_ATOM(ELEMENT_TYPE_VAR)
#define M(n) METASIG_ATOM(ELEMENT_TYPE_MVAR)
+#define GI(type, n, x) METASIG_ATOM(ELEMENT_TYPE_GENERICINST)
+
// The references to other types have special definition in some cases
#ifndef C
#define C(x) METASIG_ATOM(ELEMENT_TYPE_CLASS)
@@ -285,6 +290,11 @@ DEFINE_METASIG(GM(RefByte_T_RetVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, r(b) M(0)
DEFINE_METASIG(GM(PtrVoid_RetT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, P(v), M(0)))
DEFINE_METASIG(GM(PtrVoid_T_RetVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, P(v) M(0), v))
+DEFINE_METASIG(GM(RefTFrom_RetRefTTo, IMAGE_CEE_CS_CALLCONV_DEFAULT, 2, r(M(0)), r(M(1))))
+DEFINE_METASIG(GM(Obj_RetT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, j, M(0)))
+DEFINE_METASIG(GM(RefT_Int_RetRefT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, r(M(0)) i, r(M(0))))
+DEFINE_METASIG(GM(PtrVoid_Int_RetPtrVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, P(v) i, P(v)))
+
DEFINE_METASIG_T(SM(SafeHandle_RefBool_RetIntPtr, C(SAFE_HANDLE) r(F), I ))
DEFINE_METASIG_T(SM(SafeHandle_RetVoid, C(SAFE_HANDLE), v ))
@@ -382,6 +392,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(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))
DEFINE_METASIG_T(IM(PtrSByt_Int_Int_Encoding_RetVoid, P(B) i i C(ENCODING), v))
@@ -393,6 +404,7 @@ DEFINE_METASIG(IM(ArrChar_Int_Int_RetStr, a(u) i i, s))
DEFINE_METASIG(IM(Char_Int_RetStr, u i, s))
DEFINE_METASIG(IM(PtrChar_RetStr, P(u), s))
DEFINE_METASIG(IM(PtrChar_Int_Int_RetStr, P(u) i i, s))
+DEFINE_METASIG_T(IM(ReadOnlySpanOfChar_RetStr, GI(g(READONLY_SPAN), 1, u), s))
DEFINE_METASIG(IM(Obj_Int_RetIntPtr, j i, I))
DEFINE_METASIG(IM(Char_Char_RetStr, u u, s))
@@ -405,6 +417,8 @@ DEFINE_METASIG(IM(Int_RefIntPtr_RefIntPtr_RefIntPtr_RetVoid, i r(I) r(I) r(I), v
DEFINE_METASIG(IM(Int_RetStr, i, s))
DEFINE_METASIG(IM(Int_RetVoid, i, v))
DEFINE_METASIG(IM(Int_RetBool, i, F))
+DEFINE_METASIG(IM(Int_Int_RetVoid, i i, v))
+DEFINE_METASIG(IM(Int_Int_Int_RetVoid, i i i, v))
DEFINE_METASIG(IM(Int_Int_Int_Int_RetVoid, i i i i, v))
DEFINE_METASIG_T(IM(Obj_EventArgs_RetVoid, j C(EVENT_ARGS), v))
DEFINE_METASIG_T(IM(Obj_UnhandledExceptionEventArgs_RetVoid, j C(UNHANDLED_EVENTARGS), v))
@@ -608,6 +622,7 @@ DEFINE_METASIG_T(IM(IAsyncResult_RetVoid, C(IASYNCRESULT), v))
#undef T
#undef G
#undef M
+#undef GI
#undef _
diff --git a/src/vm/method.cpp b/src/vm/method.cpp
index 845ce799a9..8778744537 100644
--- a/src/vm/method.cpp
+++ b/src/vm/method.cpp
@@ -12,7 +12,6 @@
#include "common.h"
-#include "security.h"
#include "excep.h"
#include "dbginterface.h"
#include "ecall.h"
@@ -30,9 +29,6 @@
#include "interoputil.h"
#include "prettyprintsig.h"
#include "formattype.h"
-#ifdef FEATURE_INTERPRETER
-#include "interpreter.h"
-#endif
#ifdef FEATURE_PREJIT
#include "compile.h"
@@ -942,118 +938,6 @@ BOOL MethodDesc::IsTightlyBoundToMethodTable()
#ifndef DACCESS_COMPILE
-
-//*******************************************************************************
-HRESULT MethodDesc::Verify(COR_ILMETHOD_DECODER* ILHeader,
- BOOL fThrowException,
- BOOL fForceVerify)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_TRIGGERS;
- INJECT_FAULT(return E_OUTOFMEMORY;);
- }
- CONTRACTL_END
-
-#ifdef _VER_EE_VERIFICATION_ENABLED
- // ForceVerify will force verification if the Verifier is OFF
- if (fForceVerify)
- goto DoVerify;
-
- // Don't even try to verify if verifier is off.
- if (g_fVerifierOff)
- return S_OK;
-
- if (IsVerified())
- return S_OK;
-
- // LazyCanSkipVerification does not resolve the policy.
- // We go ahead with verification if policy is not resolved.
- // In case the verification fails, we resolve policy and
- // fail verification if the Assembly of this method does not have
- // permission to skip verification.
-
- if (Security::LazyCanSkipVerification(GetModule()->GetDomainAssembly()))
- return S_OK;
-
-#ifdef _DEBUG
- _ASSERTE(Security::IsSecurityOn());
- _ASSERTE(GetModule() != SystemDomain::SystemModule());
-#endif // _DEBUG
-
-
-DoVerify:
-
- HRESULT hr;
-
- if (fThrowException)
- hr = Verifier::VerifyMethod(this, ILHeader, NULL,
- fForceVerify ? VER_FORCE_VERIFY : VER_STOP_ON_FIRST_ERROR);
- else
- hr = Verifier::VerifyMethodNoException(this, ILHeader);
-
- if (SUCCEEDED(hr))
- SetIsVerified(TRUE);
-
- return hr;
-#else // !_VER_EE_VERIFICATION_ENABLED
- _ASSERTE(!"EE Verification is disabled, should never get here");
- return E_FAIL;
-#endif // !_VER_EE_VERIFICATION_ENABLED
-}
-
-//*******************************************************************************
-
-BOOL MethodDesc::IsVerifiable()
-{
- STANDARD_VM_CONTRACT;
-
- if (IsVerified())
- return (m_wFlags & mdcVerifiable);
-
- if (!IsTypicalMethodDefinition())
- {
- // We cannot verify concrete instantiation (eg. List<int>.Add()).
- // We have to verify the typical instantiation (eg. List<T>.Add()).
- MethodDesc * pGenMethod = LoadTypicalMethodDefinition();
- BOOL isVerifiable = pGenMethod->IsVerifiable();
-
- // Propagate the result from the typical instantiation to the
- // concrete instantiation
- SetIsVerified(isVerifiable);
-
- return isVerifiable;
- }
-
- COR_ILMETHOD_DECODER *pHeader = NULL;
- // Don't use HasILHeader() here because it returns the wrong answer
- // for methods that have DynamicIL (not to be confused with DynamicMethods)
- if (IsIL() && !IsUnboxingStub())
- {
- COR_ILMETHOD_DECODER::DecoderStatus status;
- COR_ILMETHOD_DECODER header(GetILHeader(), GetMDImport(), &status);
- if (status != COR_ILMETHOD_DECODER::SUCCESS)
- {
- COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL);
- }
- pHeader = &header;
-
-#ifdef _VER_EE_VERIFICATION_ENABLED
- static ConfigDWORD peVerify;
- if (peVerify.val(CLRConfig::EXTERNAL_PEVerify))
- {
- HRESULT hr = Verify(&header, TRUE, FALSE);
- }
-#endif // _VER_EE_VERIFICATION_ENABLED
- }
-
- UnsafeJitFunction(this, pHeader, CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_IMPORT_ONLY));
- _ASSERTE(IsVerified());
-
- return (IsVerified() && (m_wFlags & mdcVerifiable));
-}
-
//*******************************************************************************
// Update flags in a thread safe manner.
WORD MethodDesc::InterlockedUpdateFlags(WORD wMask, BOOL fSet)
@@ -1176,16 +1060,6 @@ PCODE MethodDesc::GetNativeCode()
return pCode;
}
-#ifdef FEATURE_INTERPRETER
-#ifndef DACCESS_COMPILE // TODO: Need a solution that will work under DACCESS
- PCODE pEntryPoint = GetMethodEntryPoint();
- if (Interpreter::InterpretationStubToMethodInfo(pEntryPoint) == this)
- {
- return pEntryPoint;
- }
-#endif
-#endif
-
if (!HasStableEntryPoint() || HasPrecode())
return NULL;
@@ -2440,32 +2314,6 @@ BOOL MethodDesc::IsPointingToPrestub()
return GetPrecode()->IsPointingToPrestub();
}
-#ifdef FEATURE_INTERPRETER
-//*******************************************************************************
-BOOL MethodDesc::IsReallyPointingToPrestub()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- SO_TOLERANT;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if (!HasPrecode())
- {
- PCODE pCode = GetMethodEntryPoint();
- return HasTemporaryEntryPoint() && pCode == GetTemporaryEntryPoint();
- }
-
- if (!IsRestored())
- return TRUE;
-
- return GetPrecode()->IsPointingToPrestub();
-}
-#endif
-
//*******************************************************************************
void MethodDesc::Reset()
{
@@ -2699,9 +2547,6 @@ void MethodDesc::Save(DataImage *image)
{
STANDARD_VM_CONTRACT;
- // Make sure that the transparency is cached in the NGen image
- Security::IsMethodTransparent(this);
-
// Initialize the DoesNotHaveEquivalentValuetypeParameters flag.
// If we fail to determine whether there is a type-equivalent struct parameter (eg. because there is a struct parameter
// defined in a missing dependency), then just continue. The reason we run this method is to initialize a flag that is
@@ -3501,14 +3346,7 @@ MethodDesc::Fixup(
}
}
- if (decltype(InstantiatedMethodDesc::m_pPerInstInfo)::isRelative)
- {
- image->FixupRelativePointerField(this, offsetof(InstantiatedMethodDesc, m_pPerInstInfo));
- }
- else
- {
- image->FixupPointerField(this, offsetof(InstantiatedMethodDesc, m_pPerInstInfo));
- }
+ image->FixupPlainOrRelativePointerField((InstantiatedMethodDesc*) this, &InstantiatedMethodDesc::m_pPerInstInfo);
// Generic methods are dealt with specially to avoid encoding the formal method type parameters
if (IsTypicalMethodDefinition())
@@ -3587,14 +3425,7 @@ MethodDesc::Fixup(
NDirectMethodDesc *pNMD = (NDirectMethodDesc *)this;
- if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
- {
- image->FixupRelativePointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pWriteableData));
- }
- else
- {
- image->FixupPointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pWriteableData));
- }
+ image->FixupPlainOrRelativePointerField(pNMD, &NDirectMethodDesc::ndirect, &decltype(NDirectMethodDesc::ndirect)::m_pWriteableData);
NDirectWriteableData *pWriteableData = pNMD->GetWriteableData();
NDirectImportThunkGlue *pImportThunkGlue = pNMD->GetNDirectImportThunkGlue();
@@ -4185,7 +4016,7 @@ void MethodDesc::CheckRestore(ClassLoadLevel level)
// for details on the race.
//
{
- ReJitPublishMethodHolder publishWorker(this, GetNativeCode());
+ PublishMethodHolder publishWorker(this, GetNativeCode());
pIMD->m_wFlags2 = pIMD->m_wFlags2 & ~InstantiatedMethodDesc::Unrestored;
}
@@ -4968,11 +4799,7 @@ Precode* MethodDesc::GetOrCreatePrecode()
}
//*******************************************************************************
-BOOL MethodDesc::SetNativeCodeInterlocked(PCODE addr, PCODE pExpected /*=NULL*/
-#ifdef FEATURE_INTERPRETER
- , BOOL fStable
-#endif
- )
+BOOL MethodDesc::SetNativeCodeInterlocked(PCODE addr, PCODE pExpected /*=NULL*/)
{
CONTRACTL {
THROWS;
@@ -4998,28 +4825,8 @@ BOOL MethodDesc::SetNativeCodeInterlocked(PCODE addr, PCODE pExpected /*=NULL*/
value.SetValueMaybeNull(pSlot, addr | (*dac_cast<PTR_TADDR>(pSlot) & FIXUP_LIST_MASK));
expected.SetValueMaybeNull(pSlot, pExpected | (*dac_cast<PTR_TADDR>(pSlot) & FIXUP_LIST_MASK));
-#ifdef FEATURE_INTERPRETER
- BOOL fRet = FALSE;
-
- fRet = FastInterlockCompareExchangePointer(
- EnsureWritablePages(reinterpret_cast<TADDR*>(pSlot)),
- (TADDR&)value,
- (TADDR&)expected) == (TADDR&)expected;
-
- if (!fRet)
- {
- // Can always replace NULL.
- expected.SetValueMaybeNull(pSlot, (*dac_cast<PTR_TADDR>(pSlot) & FIXUP_LIST_MASK));
- fRet = FastInterlockCompareExchangePointer(
- EnsureWritablePages(reinterpret_cast<TADDR*>(pSlot)),
- (TADDR&)value,
- (TADDR&)expected) == (TADDR&)expected;
- }
- return fRet;
-#else // FEATURE_INTERPRETER
return FastInterlockCompareExchangePointer(EnsureWritablePages(reinterpret_cast<TADDR*>(pSlot)),
(TADDR&)value, (TADDR&)expected) == (TADDR&)expected;
-#endif // FEATURE_INTERPRETER
}
if (IsDefaultInterfaceMethod() && HasPrecode())
@@ -5027,17 +4834,8 @@ BOOL MethodDesc::SetNativeCodeInterlocked(PCODE addr, PCODE pExpected /*=NULL*/
return GetPrecode()->SetTargetInterlocked(addr);
}
-#ifdef FEATURE_INTERPRETER
- PCODE pFound = FastInterlockCompareExchangePointer(GetAddrOfSlot(), addr, pExpected);
- if (fStable)
- {
- InterlockedUpdateFlags2(enum_flag2_HasStableEntryPoint, TRUE);
- }
- return (pFound == pExpected);
-#else
_ASSERTE(pExpected == NULL);
return SetStableEntryPointInterlocked(addr);
-#endif
}
//*******************************************************************************
@@ -5061,26 +4859,6 @@ BOOL MethodDesc::SetStableEntryPointInterlocked(PCODE addr)
return fResult;
}
-#ifdef FEATURE_INTERPRETER
-BOOL MethodDesc::SetEntryPointInterlocked(PCODE addr)
-{
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- } CONTRACTL_END;
-
- _ASSERTE(!HasPrecode());
-
- PCODE pExpected = GetTemporaryEntryPoint();
- PTR_PCODE pSlot = GetAddrOfSlot();
-
- BOOL fResult = FastInterlockCompareExchangePointer(pSlot, addr, pExpected) == pExpected;
-
- return fResult;
-}
-
-#endif // FEATURE_INTERPRETER
-
//*******************************************************************************
void NDirectMethodDesc::InterlockedSetNDirectFlags(WORD wFlags)
{
@@ -5245,14 +5023,6 @@ BOOL MethodDesc::HasNativeCallableAttribute()
return FALSE;
}
-//*******************************************************************************
-BOOL MethodDesc::HasSuppressUnmanagedCodeAccessAttr()
-{
- LIMITED_METHOD_CONTRACT;
-
- return TRUE;
-}
-
#ifdef FEATURE_COMINTEROP
//*******************************************************************************
void ComPlusCallMethodDesc::InitComEventCallInfo()
diff --git a/src/vm/method.hpp b/src/vm/method.hpp
index 336260cae3..08318ec5b5 100644
--- a/src/vm/method.hpp
+++ b/src/vm/method.hpp
@@ -25,6 +25,7 @@
#include <stddef.h>
#include "eeconfig.h"
#include "precode.h"
+#include "codeversion.h"
#ifndef FEATURE_PREJIT
#include "fixuppointer.h"
@@ -42,6 +43,8 @@ class Dictionary;
class GCCoverageInfo;
class DynamicMethodDesc;
class ReJitManager;
+class CodeVersionManager;
+class PrepareCodeConfig;
typedef DPTR(FCallMethodDesc) PTR_FCallMethodDesc;
typedef DPTR(ArrayMethodDesc) PTR_ArrayMethodDesc;
@@ -143,29 +146,10 @@ enum MethodDescClassification
// Method is static
mdcStatic = 0x0020,
- // Temporary Security Interception.
- // Methods can now be intercepted by security. An intercepted method behaves
- // like it was an interpreted method. The Prestub at the top of the method desc
- // is replaced by an interception stub. Therefore, no back patching will occur.
- // We picked this approach to minimize the number variations given IL and native
- // code with edit and continue. E&C will need to find the real intercepted method
- // and if it is intercepted change the real stub. If E&C is enabled then there
- // is no back patching and needs to fix the pre-stub.
- mdcIntercepted = 0x0040,
-
- // Method requires linktime security checks.
- mdcRequiresLinktimeCheck = 0x0080,
-
- // Method requires inheritance security checks.
- // If this bit is set, then this method demands inheritance permissions
- // or a method that this method overrides demands inheritance permissions
- // or both.
- mdcRequiresInheritanceCheck = 0x0100,
-
- // The method that this method overrides requires an inheritance security check.
- // This bit is used as an optimization to avoid looking up overridden methods
- // during the inheritance check.
- mdcParentRequiresInheritanceCheck = 0x0200,
+ // unused = 0x0040,
+ // unused = 0x0080,
+ // unused = 0x0100,
+ // unused = 0x0200,
// Duplicate method. When a method needs to be placed in multiple slots in the
// method table, because it could not be packed into one slot. For eg, a method
@@ -268,10 +252,6 @@ public:
BOOL SetStableEntryPointInterlocked(PCODE addr);
-#ifdef FEATURE_INTERPRETER
- BOOL SetEntryPointInterlocked(PCODE addr);
-#endif // FEATURE_INTERPRETER
-
BOOL HasTemporaryEntryPoint();
PCODE GetTemporaryEntryPoint();
@@ -507,7 +487,12 @@ public:
BaseDomain *GetDomain();
- ReJitManager * GetReJitManager();
+#ifdef FEATURE_CODE_VERSIONING
+ CodeVersionManager* GetCodeVersionManager();
+#endif
+#ifdef FEATURE_TIERED_COMPILATION
+ CallCounter* GetCallCounter();
+#endif
PTR_LoaderAllocator GetLoaderAllocator();
@@ -669,7 +654,6 @@ public:
}
void ComputeSuppressUnmanagedCodeAccessAttr(IMDInternalImport *pImport);
- BOOL HasSuppressUnmanagedCodeAccessAttr();
BOOL HasNativeCallableAttribute();
#ifdef FEATURE_COMINTEROP
@@ -697,32 +681,6 @@ public:
// Update flags in a thread safe manner.
WORD InterlockedUpdateFlags(WORD wMask, BOOL fSet);
- inline DWORD IsInterceptedForDeclSecurity()
- {
- LIMITED_METHOD_CONTRACT;
- STATIC_CONTRACT_SO_TOLERANT;
- return m_wFlags & mdcIntercepted;
- }
-
- inline void SetInterceptedForDeclSecurity()
- {
- LIMITED_METHOD_CONTRACT;
- m_wFlags |= mdcIntercepted;
- }
-
- inline DWORD IsInterceptedForDeclSecurityCASDemandsOnly()
- {
- LIMITED_METHOD_CONTRACT;
- STATIC_CONTRACT_SO_TOLERANT;
- return m_bFlags2 & enum_flag2_CASDemandsOnly;
- }
-
- inline void SetInterceptedForDeclSecurityCASDemandsOnly()
- {
- LIMITED_METHOD_CONTRACT;
- m_bFlags2 |= enum_flag2_CASDemandsOnly;
- }
-
// If the method is in an Edit and Contine (EnC) module, then
// we DON'T want to backpatch this, ever. We MUST always call
// through the precode so that we can update the method.
@@ -746,7 +704,6 @@ public:
InterlockedUpdateFlags(mdcNotInline, set);
}
-
BOOL IsIntrospectionOnly();
#ifndef DACCESS_COMPILE
VOID EnsureActive();
@@ -811,50 +768,11 @@ public:
BOOL IsQCall();
//================================================================
- // Has the method been verified?
- // This does not mean that the IL is verifiable, just that we have
- // determined if the IL is verfiable or unverifiable.
- // (Is this is dead code since the JIT now does verification?)
-
- inline BOOL IsVerified()
- {
- LIMITED_METHOD_CONTRACT;
- return m_wFlags & mdcVerifiedState;
- }
-
- inline void SetIsVerified(BOOL isVerifiable)
- {
- WRAPPER_NO_CONTRACT;
-
- WORD flags = isVerifiable ? (WORD(mdcVerifiedState) | WORD(mdcVerifiable))
- : (WORD(mdcVerifiedState));
- InterlockedUpdateFlags(flags, TRUE);
- }
-
- inline void ResetIsVerified()
- {
- WRAPPER_NO_CONTRACT;
- InterlockedUpdateFlags(mdcVerifiedState | mdcVerifiable, FALSE);
- }
-
- BOOL IsVerifiable();
-
- // fThrowException is used to prevent Verifier from
- // throwin an exception on error
- // fForceVerify is to be used by tools that need to
- // force verifier to verify code even if the code is fully trusted.
- HRESULT Verify(COR_ILMETHOD_DECODER* ILHeader,
- BOOL fThrowException,
- BOOL fForceVerify);
-
-
- //================================================================
//
inline void ClearFlagsOnUpdate()
{
WRAPPER_NO_CONTRACT;
- ResetIsVerified();
SetNotInline(FALSE);
}
@@ -1229,45 +1147,6 @@ protected:
}
public:
- //==================================================================
- // Security...
-
- inline DWORD RequiresLinktimeCheck()
- {
- LIMITED_METHOD_CONTRACT;
- return m_wFlags & mdcRequiresLinktimeCheck;
- }
-
- inline DWORD RequiresInheritanceCheck()
- {
- LIMITED_METHOD_CONTRACT;
- return m_wFlags & mdcRequiresInheritanceCheck;
- }
-
- inline DWORD ParentRequiresInheritanceCheck()
- {
- LIMITED_METHOD_CONTRACT;
- return m_wFlags & mdcParentRequiresInheritanceCheck;
- }
-
- void SetRequiresLinktimeCheck()
- {
- LIMITED_METHOD_CONTRACT;
- m_wFlags |= mdcRequiresLinktimeCheck;
- }
-
- void SetRequiresInheritanceCheck()
- {
- LIMITED_METHOD_CONTRACT;
- m_wFlags |= mdcRequiresInheritanceCheck;
- }
-
- void SetParentRequiresInheritanceCheck()
- {
- LIMITED_METHOD_CONTRACT;
- m_wFlags |= mdcParentRequiresInheritanceCheck;
- }
-
mdMethodDef GetMemberDef() const;
mdMethodDef GetMemberDef_NoLogging() const;
@@ -1286,12 +1165,73 @@ public:
void SetChunkIndex(MethodDescChunk *pChunk);
BOOL IsPointingToPrestub();
-#ifdef FEATURE_INTERPRETER
- BOOL IsReallyPointingToPrestub();
-#endif // FEATURE_INTERPRETER
public:
+ // TRUE iff it is possible to change the code this method will run using
+ // the CodeVersionManager.
+ // Note: EnC currently returns FALSE here because it uses its own seperate
+ // scheme to manage versionability. We will likely want to converge them
+ // at some point.
+ BOOL IsVersionable()
+ {
+#ifndef FEATURE_CODE_VERSIONING
+ return FALSE;
+#else
+ return IsVersionableWithPrecode() || IsVersionableWithJumpStamp();
+#endif
+ }
+
+ // If true, these methods version using the CodeVersionManager and
+ // switch between different code versions by updating the target of the precode.
+ // Note: EnC returns FALSE - even though it uses precode updates it does not
+ // use the CodeVersionManager right now
+ BOOL IsVersionableWithPrecode()
+ {
+#ifdef FEATURE_CODE_VERSIONING
+ return
+ // policy: which things do we want to version with a precode if possible
+ IsEligibleForTieredCompilation() &&
+
+ // functional requirements:
+ !IsZapped() && // NGEN directly invokes the pre-generated native code.
+ // without necessarily going through the prestub or
+ // precode
+ HasNativeCodeSlot(); // the stable entry point will need to point at our
+ // precode and not directly contain the native code.
+#else
+ return FALSE;
+#endif
+ }
+
+ // If true, these methods version using the CodeVersionManager and switch between
+ // different code versions by overwriting the first bytes of the method's initial
+ // native code with a jmp instruction.
+ BOOL IsVersionableWithJumpStamp()
+ {
+#if defined(FEATURE_CODE_VERSIONING) && defined(FEATURE_JUMPSTAMP)
+ return
+ // for native image code this is policy, but for jitted code it is a functional requirement
+ // to ensure the prolog is sufficiently large
+ ReJitManager::IsReJITEnabled() &&
+
+ // functional requirement - the runtime doesn't expect both options to be possible
+ !IsVersionableWithPrecode() &&
+
+ // functional requirement - we must be able to evacuate the prolog and the prolog must be big
+ // enough, both of which are only designed to work on jitted code
+ (IsIL() || IsNoMetadata()) &&
+ !IsUnboxingStub() &&
+ !IsInstantiatingStub() &&
+
+ // functional requirement - code version manager can't handle what would happen if the code
+ // was collected
+ !GetLoaderAllocator()->IsCollectible();
+#else
+ return FALSE;
+#endif
+ }
+
#ifdef FEATURE_TIERED_COMPILATION
// Is this method allowed to be recompiled and the entrypoint redirected so that we
// can optimize its performance? Eligibility is invariant for the lifetime of a method.
@@ -1301,20 +1241,31 @@ public:
// This policy will need to change some more before tiered compilation feature
// can be properly supported across a broad range of scenarios. For instance it
- // wouldn't interact correctly debugging or profiling at the moment because we
- // enable it too aggresively and it conflicts with the operations of those features.
+ // wouldn't interact correctly with debugging at the moment because we enable
+ // it too aggresively and it conflicts with the operations of those features.
- //Keep in-sync with MethodTableBuilder::NeedsNativeCodeSlot(bmtMDMethod * pMDMethod)
- //In the future we might want mutable vtable slots too, but that would require
- //more work around the runtime to prevent those mutable pointers from leaking
+ // Keep in-sync with MethodTableBuilder::NeedsNativeCodeSlot(bmtMDMethod * pMDMethod)
+ // to ensure native slots are available where needed.
return g_pConfig->TieredCompilation() &&
- !GetModule()->HasNativeOrReadyToRunImage() &&
+ !IsZapped() &&
!IsEnCMethod() &&
- HasNativeCodeSlot();
+ HasNativeCodeSlot() &&
+ !IsUnboxingStub() &&
+ !IsInstantiatingStub();
+
+ // We should add an exclusion for modules with debuggable code gen flags
}
#endif
+ // Returns a code version that represents the first (default)
+ // code body that this method would have.
+ NativeCodeVersion GetInitialCodeVersion()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return NativeCodeVersion(dac_cast<PTR_MethodDesc>(this));
+ }
+
// Does this method force the NativeCodeSlot to stay fixed after it
// is first initialized to native code? Consumers of the native code
// pointer need to be very careful about if and when they cache it
@@ -1326,6 +1277,12 @@ public:
BOOL IsNativeCodeStableAfterInit()
{
LIMITED_METHOD_DAC_CONTRACT;
+
+#if defined(FEATURE_JIT_PITCHING)
+ if (IsPitchable())
+ return false;
+#endif
+
return
#ifdef FEATURE_TIERED_COMPILATION
!IsEligibleForTieredCompilation() &&
@@ -1371,11 +1328,7 @@ public:
return GetNativeCode() != NULL;
}
-#ifdef FEATURE_INTERPRETER
- BOOL SetNativeCodeInterlocked(PCODE addr, PCODE pExpected, BOOL fStable);
-#else // FEATURE_INTERPRETER
BOOL SetNativeCodeInterlocked(PCODE addr, PCODE pExpected = NULL);
-#endif // FEATURE_INTERPRETER
TADDR GetAddrOfNativeCodeSlot();
@@ -1442,6 +1395,11 @@ public:
// - ngened code if IsPreImplemented()
PCODE GetNativeCode();
+#if defined(FEATURE_JIT_PITCHING)
+ bool IsPitchable();
+ void PitchNativeCode();
+#endif
+
//================================================================
// FindOrCreateAssociatedMethodDesc
//
@@ -1685,69 +1643,11 @@ public:
PCODE DoPrestub(MethodTable *pDispatchingMT);
- PCODE MakeJitWorker(COR_ILMETHOD_DECODER* ILHeader, CORJIT_FLAGS flags);
-
VOID GetMethodInfo(SString &namespaceOrClassName, SString &methodName, SString &methodSignature);
VOID GetMethodInfoWithNewSig(SString &namespaceOrClassName, SString &methodName, SString &methodSignature);
VOID GetMethodInfoNoSig(SString &namespaceOrClassName, SString &methodName);
VOID GetFullMethodInfo(SString& fullMethodSigName);
- BOOL IsCritical()
- {
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(HasCriticalTransparentInfo());
- return (m_bFlags2 & enum_flag2_Transparency_Mask) != enum_flag2_Transparency_Transparent;
- }
-
- BOOL IsTreatAsSafe()
- {
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(HasCriticalTransparentInfo());
- return (m_bFlags2 & enum_flag2_Transparency_Mask) == enum_flag2_Transparency_TreatAsSafe;
- }
-
- BOOL IsTransparent()
- {
- WRAPPER_NO_CONTRACT;
- _ASSERTE(HasCriticalTransparentInfo());
- return !IsCritical();
- }
-
- BOOL HasCriticalTransparentInfo()
- {
- LIMITED_METHOD_CONTRACT;
- return (m_bFlags2 & enum_flag2_Transparency_Mask) != enum_flag2_Transparency_Unknown;
- }
-
- void SetCriticalTransparentInfo(BOOL fIsCritical, BOOL fIsTreatAsSafe)
- {
- WRAPPER_NO_CONTRACT;
-
- // TreatAsSafe has to imply critical
- _ASSERTE(fIsCritical || !fIsTreatAsSafe);
-
- EnsureWritablePages(this);
- InterlockedUpdateFlags2(
- static_cast<BYTE>(fIsTreatAsSafe ? enum_flag2_Transparency_TreatAsSafe :
- fIsCritical ? enum_flag2_Transparency_Critical :
- enum_flag2_Transparency_Transparent),
- TRUE);
-
- _ASSERTE(HasCriticalTransparentInfo());
- }
-
- BOOL RequiresLinkTimeCheckHostProtectionOnly()
- {
- LIMITED_METHOD_CONTRACT;
- return (m_bFlags2 & enum_flag2_HostProtectionLinkCheckOnly) != 0;
- }
-
- void SetRequiresLinkTimeCheckHostProtectionOnly()
- {
- LIMITED_METHOD_CONTRACT;
- m_bFlags2 |= enum_flag2_HostProtectionLinkCheckOnly;
- }
-
BOOL HasTypeEquivalentStructParameters()
#ifndef FEATURE_TYPEEQUIVALENCE
{
@@ -1797,21 +1697,11 @@ protected:
enum_flag2_IsUnboxingStub = 0x04,
enum_flag2_HasNativeCodeSlot = 0x08, // Has slot for native code
- enum_flag2_Transparency_Mask = 0x30,
- enum_flag2_Transparency_Unknown = 0x00, // The transparency has not been computed yet
- enum_flag2_Transparency_Transparent = 0x10, // Method is transparent
- enum_flag2_Transparency_Critical = 0x20, // Method is critical
- enum_flag2_Transparency_TreatAsSafe = 0x30, // Method is treat as safe. Also implied critical.
-
- // CAS Demands: Demands for Permissions that are CAS Permissions. CAS Perms are those
- // that derive from CodeAccessPermission and need a stackwalk to evaluate demands
- // Non-CAS perms are those that don't need a stackwalk and don't derive from CodeAccessPermission. The implementor
- // specifies the behavior on a demand. Examples: CAS: FileIOPermission. Non-CAS: PrincipalPermission.
- // This bit gets set if the demands are BCL CAS demands only. Even if there are non-BCL CAS demands, we don't set this
- // bit.
- enum_flag2_CASDemandsOnly = 0x40,
-
- enum_flag2_HostProtectionLinkCheckOnly = 0x80, // Method has LinkTime check due to HP only.
+ enum_flag2_IsJitIntrinsic = 0x10, // Jit may expand method as an intrinsic
+
+ // unused = 0x20,
+ // unused = 0x40,
+ // unused = 0x80,
};
BYTE m_bFlags2;
@@ -1861,6 +1751,18 @@ public:
m_bFlags2 |= enum_flag2_HasNativeCodeSlot;
}
+ inline BOOL IsJitIntrinsic()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (m_bFlags2 & enum_flag2_IsJitIntrinsic) != 0;
+ }
+
+ inline void SetIsJitIntrinsic()
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_bFlags2 |= enum_flag2_IsJitIntrinsic;
+ }
+
static const SIZE_T s_ClassificationSizeTable[];
static SIZE_T GetBaseSize(DWORD classification)
@@ -1949,8 +1851,72 @@ public:
REFLECTMETHODREF GetStubMethodInfo();
PrecodeType GetPrecodeType();
+
+
+ // ---------------------------------------------------------------------------------
+ // IL based Code generation pipeline
+ // ---------------------------------------------------------------------------------
+
+#ifndef DACCESS_COMPILE
+public:
+ PCODE PrepareInitialCode();
+ PCODE PrepareCode(NativeCodeVersion codeVersion);
+ PCODE PrepareCode(PrepareCodeConfig* pConfig);
+
+private:
+ PCODE PrepareILBasedCode(PrepareCodeConfig* pConfig);
+ PCODE GetPrecompiledCode(PrepareCodeConfig* pConfig);
+ PCODE GetPrecompiledNgenCode();
+ PCODE GetPrecompiledR2RCode();
+ PCODE GetMulticoreJitCode();
+ COR_ILMETHOD_DECODER* GetAndVerifyILHeader(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pIlDecoderMemory);
+ COR_ILMETHOD_DECODER* GetAndVerifyMetadataILHeader(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pIlDecoderMemory);
+ COR_ILMETHOD_DECODER* GetAndVerifyNoMetadataILHeader();
+ PCODE JitCompileCode(PrepareCodeConfig* pConfig);
+ PCODE JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, JitListLockEntry* pEntry);
+ PCODE JitCompileCodeLocked(PrepareCodeConfig* pConfig, JitListLockEntry* pLockEntry, ULONG* pSizeOfCode, CORJIT_FLAGS* pFlags);
+#endif // DACCESS_COMPILE
};
+#ifndef DACCESS_COMPILE
+class PrepareCodeConfig
+{
+public:
+ PrepareCodeConfig();
+ PrepareCodeConfig(NativeCodeVersion nativeCodeVersion, BOOL needsMulticoreJitNotification, BOOL mayUsePrecompiledCode);
+ MethodDesc* GetMethodDesc();
+ NativeCodeVersion GetCodeVersion();
+ BOOL NeedsMulticoreJitNotification();
+ BOOL MayUsePrecompiledCode();
+ virtual PCODE IsJitCancellationRequested();
+ virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse);
+ virtual COR_ILMETHOD* GetILHeader();
+ virtual CORJIT_FLAGS GetJitCompilationFlags();
+
+protected:
+ MethodDesc* m_pMethodDesc;
+ NativeCodeVersion m_nativeCodeVersion;
+ BOOL m_needsMulticoreJitNotification;
+ BOOL m_mayUsePrecompiledCode;
+};
+
+#ifdef FEATURE_CODE_VERSIONING
+class VersionedPrepareCodeConfig : public PrepareCodeConfig
+{
+public:
+ VersionedPrepareCodeConfig();
+ VersionedPrepareCodeConfig(NativeCodeVersion codeVersion);
+ HRESULT FinishConfiguration();
+ virtual PCODE IsJitCancellationRequested();
+ virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse);
+ virtual COR_ILMETHOD* GetILHeader();
+ virtual CORJIT_FLAGS GetJitCompilationFlags();
+private:
+ ILCodeVersion m_ilCodeVersion;
+};
+#endif // FEATURE_CODE_VERSIONING
+#endif // DACCESS_COMPILE
+
/******************************************************************/
// A code:MethodDescChunk is a container that holds one or more code:MethodDesc. Logically it is just
diff --git a/src/vm/method.inl b/src/vm/method.inl
index cdd137b84b..dd14900c12 100644
--- a/src/vm/method.inl
+++ b/src/vm/method.inl
@@ -203,11 +203,21 @@ inline BOOL HasTypeEquivalentStructParameters()
}
#endif // FEATURE_TYPEEQUIVALENCE
-inline ReJitManager * MethodDesc::GetReJitManager()
+#ifdef FEATURE_CODE_VERSIONING
+inline CodeVersionManager * MethodDesc::GetCodeVersionManager()
{
LIMITED_METHOD_CONTRACT;
- return GetModule()->GetReJitManager();
+ return GetModule()->GetCodeVersionManager();
}
+#endif
+
+#ifdef FEATURE_TIERED_COMPILATION
+inline CallCounter * MethodDesc::GetCallCounter()
+{
+ LIMITED_METHOD_CONTRACT;
+ return GetModule()->GetCallCounter();
+}
+#endif
#endif // _METHOD_INL_
diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp
index face764e0f..0adba6ac4e 100644
--- a/src/vm/methodtable.cpp
+++ b/src/vm/methodtable.cpp
@@ -33,7 +33,6 @@
#include "fieldmarshaler.h"
#include "cgensys.h"
#include "gcheaputilities.h"
-#include "security.h"
#include "dbginterface.h"
#include "comdelegate.h"
#include "eventtrace.h"
@@ -67,7 +66,6 @@
#include "typeequivalencehash.hpp"
#endif
-#include "listlock.inl"
#include "generics.h"
#include "genericdict.h"
#include "typestring.h"
@@ -593,7 +591,7 @@ void MethodTable::SetIsRestored()
// for details on the race.
//
{
- ReJitPublishMethodTableHolder(this);
+ PublishMethodTableHolder(this);
FastInterlockAnd(EnsureWritablePages(&(GetWriteableDataForWrite()->m_dwFlags)), ~MethodTableWriteableData::enum_flag_Unrestored);
}
#ifndef DACCESS_COMPILE
@@ -1013,7 +1011,7 @@ void MethodTable::SetInterfaceMap(WORD wNumInterfaces, InterfaceInfo_t* iMap)
m_wNumInterfaces = wNumInterfaces;
CONSISTENCY_CHECK(IS_ALIGNED(iMap, sizeof(void*)));
- m_pInterfaceMap = iMap;
+ m_pInterfaceMap.SetValue(iMap);
}
//==========================================================================================
@@ -1236,7 +1234,12 @@ void MethodTable::AddDynamicInterface(MethodTable *pItfMT)
if (TotalNumInterfaces > 0) {
InterfaceInfo_t *pInterfaceMap = GetInterfaceMap();
PREFIX_ASSUME(pInterfaceMap != NULL);
- memcpy(pNewItfMap, pInterfaceMap, TotalNumInterfaces * sizeof(InterfaceInfo_t));
+
+ for (unsigned index = 0; index < TotalNumInterfaces; ++index)
+ {
+ InterfaceInfo_t *pIntInfo = (InterfaceInfo_t *) (pNewItfMap + index);
+ pIntInfo->SetMethodTable((pInterfaceMap + index)->GetMethodTable());
+ }
}
// Add the new interface at the end of the map.
@@ -1246,7 +1249,8 @@ void MethodTable::AddDynamicInterface(MethodTable *pItfMT)
*(((DWORD_PTR *)pNewItfMap) - 1) = NumDynAddedInterfaces + 1;
// Switch the old interface map with the new one.
- VolatileStore(EnsureWritablePages(&m_pInterfaceMap), pNewItfMap);
+ EnsureWritablePages(&m_pInterfaceMap);
+ m_pInterfaceMap.SetValueVolatile(pNewItfMap);
// Log the fact that we leaked the interface vtable map.
#ifdef _DEBUG
@@ -1287,7 +1291,7 @@ void MethodTable::SetupGenericsStaticsInfo(FieldDesc* pStaticFieldDescs)
pInfo->m_DynamicTypeID = (SIZE_T)-1;
}
- pInfo->m_pFieldDescs = pStaticFieldDescs;
+ pInfo->m_pFieldDescs.SetValueMaybeNull(pStaticFieldDescs);
}
#endif // !DACCESS_COMPILE
@@ -1784,7 +1788,7 @@ TypeHandle::CastResult MethodTable::CanCastToClassNoGC(MethodTable *pTargetMT)
if (pMT == pTargetMT)
return TypeHandle::CanCast;
- pMT = MethodTable::GetParentMethodTableOrIndirection(pMT);
+ pMT = MethodTable::GetParentMethodTable(pMT);
} while (pMT);
}
@@ -3147,7 +3151,7 @@ void MethodTable::AllocateRegularStaticBoxes()
OBJECTREF* pStaticSlots = (OBJECTREF*)(pStaticBase + pClassCtorInfoEntry->firstBoxedStaticOffset);
GCPROTECT_BEGININTERIOR(pStaticSlots);
- ArrayDPTR(FixupPointer<PTR_MethodTable>) ppMTs = GetLoaderModule()->GetZapModuleCtorInfo()->
+ ArrayDPTR(RelativeFixupPointer<PTR_MethodTable>) ppMTs = GetLoaderModule()->GetZapModuleCtorInfo()->
GetGCStaticMTs(pClassCtorInfoEntry->firstBoxedStaticMTIndex);
DWORD numBoxedStatics = pClassCtorInfoEntry->numBoxedStatics;
@@ -4122,7 +4126,7 @@ void ModuleCtorInfo::AddElement(MethodTable *pMethodTable)
{
_ASSERTE(numElements == numLastAllocated);
- MethodTable ** ppOldMTEntries = ppMT;
+ RelativePointer<MethodTable *> *ppOldMTEntries = ppMT;
#ifdef _PREFAST_
#pragma warning(push)
@@ -4133,12 +4137,19 @@ void ModuleCtorInfo::AddElement(MethodTable *pMethodTable)
#pragma warning(pop)
#endif // _PREFAST_
- ppMT = new MethodTable* [numNewAllocated];
+ ppMT = new RelativePointer<MethodTable *> [numNewAllocated];
_ASSERTE(ppMT);
- memcpy(ppMT, ppOldMTEntries, sizeof(MethodTable *) * numLastAllocated);
- memset(ppMT + numLastAllocated, 0, sizeof(MethodTable *) * (numNewAllocated - numLastAllocated));
+ for (unsigned index = 0; index < numLastAllocated; ++index)
+ {
+ ppMT[index].SetValueMaybeNull(ppOldMTEntries[index].GetValueMaybeNull());
+ }
+
+ for (unsigned index = numLastAllocated; index < numNewAllocated; ++index)
+ {
+ ppMT[index].SetValueMaybeNull(NULL);
+ }
delete[] ppOldMTEntries;
@@ -4150,7 +4161,7 @@ void ModuleCtorInfo::AddElement(MethodTable *pMethodTable)
// Note the use of two "parallel" arrays. We do this to keep the workingset smaller since we
// often search (in GetClassCtorInfoIfExists) for a methodtable pointer but never actually find it.
- ppMT[numElements] = pMethodTable;
+ ppMT[numElements].SetValue(pMethodTable);
numElements++;
}
@@ -4276,16 +4287,32 @@ void MethodTable::Save(DataImage *image, DWORD profilingFlags)
// Dynamic interface maps have an additional DWORD_PTR preceding the InterfaceInfo_t array
if (HasDynamicInterfaceMap())
{
- ZapStoredStructure * pInterfaceMapNode = image->StoreInternedStructure(((DWORD_PTR *)GetInterfaceMap()) - 1,
- GetInterfaceMapSize(),
- DataImage::ITEM_INTERFACE_MAP);
-
+ ZapStoredStructure * pInterfaceMapNode;
+ if (decltype(InterfaceInfo_t::m_pMethodTable)::isRelative)
+ {
+ pInterfaceMapNode = image->StoreStructure(((DWORD_PTR *)GetInterfaceMap()) - 1,
+ GetInterfaceMapSize(),
+ DataImage::ITEM_INTERFACE_MAP);
+ }
+ else
+ {
+ pInterfaceMapNode = image->StoreInternedStructure(((DWORD_PTR *)GetInterfaceMap()) - 1,
+ GetInterfaceMapSize(),
+ DataImage::ITEM_INTERFACE_MAP);
+ }
image->BindPointer(GetInterfaceMap(), pInterfaceMapNode, sizeof(DWORD_PTR));
}
else
#endif // FEATURE_COMINTEROP
{
- image->StoreInternedStructure(GetInterfaceMap(), GetInterfaceMapSize(), DataImage::ITEM_INTERFACE_MAP);
+ if (decltype(InterfaceInfo_t::m_pMethodTable)::isRelative)
+ {
+ image->StoreStructure(GetInterfaceMap(), GetInterfaceMapSize(), DataImage::ITEM_INTERFACE_MAP);
+ }
+ else
+ {
+ image->StoreInternedStructure(GetInterfaceMap(), GetInterfaceMapSize(), DataImage::ITEM_INTERFACE_MAP);
+ }
}
SaveExtraInterfaceInfo(image);
@@ -4302,7 +4329,14 @@ void MethodTable::Save(DataImage *image, DWORD profilingFlags)
ZapStoredStructure * pPerInstInfoNode;
if (CanEagerBindToParentDictionaries(image, NULL))
{
- pPerInstInfoNode = image->StoreInternedStructure((BYTE *)GetPerInstInfo() - sizeof(GenericsDictInfo), GetPerInstInfoSize() + sizeof(GenericsDictInfo), DataImage::ITEM_DICTIONARY);
+ if (PerInstInfoElem_t::isRelative)
+ {
+ pPerInstInfoNode = image->StoreStructure((BYTE *)GetPerInstInfo() - sizeof(GenericsDictInfo), GetPerInstInfoSize() + sizeof(GenericsDictInfo), DataImage::ITEM_DICTIONARY);
+ }
+ else
+ {
+ pPerInstInfoNode = image->StoreInternedStructure((BYTE *)GetPerInstInfo() - sizeof(GenericsDictInfo), GetPerInstInfoSize() + sizeof(GenericsDictInfo), DataImage::ITEM_DICTIONARY);
+ }
}
else
{
@@ -4649,14 +4683,21 @@ BOOL MethodTable::IsWriteable()
// target module. Thus we want to call CanEagerBindToMethodTable
// to check we can hardbind to the containing structure.
static
-void HardBindOrClearDictionaryPointer(DataImage *image, MethodTable *pMT, void * p, SSIZE_T offset)
+void HardBindOrClearDictionaryPointer(DataImage *image, MethodTable *pMT, void * p, SSIZE_T offset, bool isRelative)
{
WRAPPER_NO_CONTRACT;
if (image->CanEagerBindToMethodTable(pMT) &&
image->CanHardBindToZapModule(pMT->GetLoaderModule()))
{
- image->FixupPointerField(p, offset);
+ if (isRelative)
+ {
+ image->FixupRelativePointerField(p, offset);
+ }
+ else
+ {
+ image->FixupPointerField(p, offset);
+ }
}
else
{
@@ -4694,7 +4735,7 @@ void MethodTable::Fixup(DataImage *image)
if (IsCanonicalMethodTable())
{
// Pointer to EEClass
- image->FixupPointerField(this, offsetof(MethodTable, m_pEEClass));
+ image->FixupPlainOrRelativePointerField(this, &MethodTable::m_pEEClass);
}
else
{
@@ -4709,7 +4750,7 @@ void MethodTable::Fixup(DataImage *image)
if (image->CanHardBindToZapModule(pCanonMT->GetLoaderModule()))
{
// Pointer to canonical methodtable
- image->FixupField(this, offsetof(MethodTable, m_pCanonMT), pCanonMT, UNION_METHODTABLE);
+ image->FixupPlainOrRelativeField(this, &MethodTable::m_pCanonMT, pCanonMT, UNION_METHODTABLE);
}
else
{
@@ -4727,18 +4768,28 @@ void MethodTable::Fixup(DataImage *image)
if (pImport != NULL)
{
- image->FixupFieldToNode(this, offsetof(MethodTable, m_pCanonMT), pImport, UNION_INDIRECTION);
+ image->FixupPlainOrRelativeFieldToNode(this, &MethodTable::m_pCanonMT, pImport, UNION_INDIRECTION);
}
}
- image->FixupField(this, offsetof(MethodTable, m_pLoaderModule), pZapModule);
+ image->FixupField(this, offsetof(MethodTable, m_pLoaderModule), pZapModule, 0, IMAGE_REL_BASED_RELPTR);
#ifdef _DEBUG
image->FixupPointerField(this, offsetof(MethodTable, debug_m_szClassName));
#endif // _DEBUG
MethodTable * pParentMT = GetParentMethodTable();
- _ASSERTE(!pNewMT->GetFlag(enum_flag_HasIndirectParent));
+ _ASSERTE(!pNewMT->m_pParentMethodTable.IsIndirectPtrMaybeNull());
+
+ ZapRelocationType relocType;
+ if (decltype(MethodTable::m_pParentMethodTable)::isRelative)
+ {
+ relocType = IMAGE_REL_BASED_RELPTR;
+ }
+ else
+ {
+ relocType = IMAGE_REL_BASED_PTR;
+ }
if (pParentMT != NULL)
{
@@ -4750,7 +4801,8 @@ void MethodTable::Fixup(DataImage *image)
{
if (image->CanHardBindToZapModule(pParentMT->GetLoaderModule()))
{
- image->FixupPointerField(this, offsetof(MethodTable, m_pParentMethodTable));
+ _ASSERTE(!m_pParentMethodTable.IsIndirectPtr());
+ image->FixupField(this, offsetof(MethodTable, m_pParentMethodTable), pParentMT, 0, relocType);
}
else
{
@@ -4786,8 +4838,7 @@ void MethodTable::Fixup(DataImage *image)
if (pImport != NULL)
{
- image->FixupFieldToNode(this, offsetof(MethodTable, m_pParentMethodTable), pImport, -(SSIZE_T)offsetof(MethodTable, m_pParentMethodTable));
- pNewMT->SetFlag(enum_flag_HasIndirectParent);
+ image->FixupFieldToNode(this, offsetof(MethodTable, m_pParentMethodTable), pImport, FIXUP_POINTER_INDIRECTION, relocType);
}
}
@@ -4800,14 +4851,14 @@ void MethodTable::Fixup(DataImage *image)
if (HasInterfaceMap())
{
- image->FixupPointerField(this, offsetof(MethodTable, m_pMultipurposeSlot2));
+ image->FixupPlainOrRelativePointerField(this, &MethodTable::m_pInterfaceMap);
FixupExtraInterfaceInfo(image);
}
_ASSERTE(GetWriteableData());
- image->FixupPointerField(this, offsetof(MethodTable, m_pWriteableData));
- m_pWriteableData->Fixup(image, this, needsRestore);
+ image->FixupPlainOrRelativePointerField(this, &MethodTable::m_pWriteableData);
+ m_pWriteableData.GetValue()->Fixup(image, this, needsRestore);
#ifdef FEATURE_COMINTEROP
if (HasGuidInfo())
@@ -4875,7 +4926,14 @@ void MethodTable::Fixup(DataImage *image)
VtableIndirectionSlotIterator it = IterateVtableIndirectionSlots();
while (it.Next())
{
- image->FixupPointerField(this, it.GetOffsetFromMethodTable());
+ if (VTableIndir_t::isRelative)
+ {
+ image->FixupRelativePointerField(this, it.GetOffsetFromMethodTable());
+ }
+ else
+ {
+ image->FixupPointerField(this, it.GetOffsetFromMethodTable());
+ }
}
}
@@ -4896,7 +4954,7 @@ void MethodTable::Fixup(DataImage *image)
{
// Virtual slots live in chunks pointed to by vtable indirections
- slotBase = (PVOID) GetVtableIndirections()[GetIndexOfVtableIndirection(slotNumber)];
+ slotBase = (PVOID) GetVtableIndirections()[GetIndexOfVtableIndirection(slotNumber)].GetValueMaybeNull();
slotOffset = GetIndexAfterVtableIndirection(slotNumber) * sizeof(PCODE);
}
else if (HasSingleNonVirtualSlot())
@@ -4991,7 +5049,7 @@ void MethodTable::Fixup(DataImage *image)
if (HasPerInstInfo())
{
// Fixup the pointer to the per-inst table
- image->FixupPointerField(this, offsetof(MethodTable, m_pPerInstInfo));
+ image->FixupPlainOrRelativePointerField(this, &MethodTable::m_pPerInstInfo);
for (MethodTable *pChain = this; pChain != NULL; pChain = pChain->GetParentMethodTable())
{
@@ -5004,10 +5062,23 @@ void MethodTable::Fixup(DataImage *image)
// We special-case the dictionary for this method table because we must always
// hard bind to it even if it's not in its preferred zap module
+ size_t sizeDict = sizeof(PerInstInfoElem_t);
+
if (pChain == this)
- image->FixupPointerField(GetPerInstInfo(), dictNum * sizeof(Dictionary *));
+ {
+ if (PerInstInfoElem_t::isRelative)
+ {
+ image->FixupRelativePointerField(GetPerInstInfo(), dictNum * sizeDict);
+ }
+ else
+ {
+ image->FixupPointerField(GetPerInstInfo(), dictNum * sizeDict);
+ }
+ }
else
- HardBindOrClearDictionaryPointer(image, pChain, GetPerInstInfo(), dictNum * sizeof(Dictionary *));
+ {
+ HardBindOrClearDictionaryPointer(image, pChain, GetPerInstInfo(), dictNum * sizeDict, PerInstInfoElem_t::isRelative);
+ }
}
}
}
@@ -5036,7 +5107,7 @@ void MethodTable::Fixup(DataImage *image)
{
GenericsStaticsInfo *pInfo = GetGenericsStaticsInfo();
- image->FixupPointerField(this, (BYTE *)&pInfo->m_pFieldDescs - (BYTE *)this);
+ image->FixupRelativePointerField(this, (BYTE *)&pInfo->m_pFieldDescs - (BYTE *)this);
if (!isCanonical)
{
for (DWORD i = 0; i < GetClass()->GetNumStaticFields(); i++)
@@ -5048,12 +5119,12 @@ void MethodTable::Fixup(DataImage *image)
if (NeedsCrossModuleGenericsStaticsInfo())
{
- MethodTableWriteableData * pNewWriteableData = (MethodTableWriteableData *)image->GetImagePointer(m_pWriteableData);
+ MethodTableWriteableData * pNewWriteableData = (MethodTableWriteableData *)image->GetImagePointer(m_pWriteableData.GetValue());
CrossModuleGenericsStaticsInfo * pNewCrossModuleGenericsStaticsInfo = pNewWriteableData->GetCrossModuleGenericsStaticsInfo();
pNewCrossModuleGenericsStaticsInfo->m_DynamicTypeID = pInfo->m_DynamicTypeID;
- image->ZeroPointerField(m_pWriteableData, sizeof(MethodTableWriteableData) + offsetof(CrossModuleGenericsStaticsInfo, m_pModuleForStatics));
+ image->ZeroPointerField(m_pWriteableData.GetValue(), sizeof(MethodTableWriteableData) + offsetof(CrossModuleGenericsStaticsInfo, m_pModuleForStatics));
pNewMT->SetFlag(enum_flag_StaticsMask_IfGenericsThenCrossModule);
}
@@ -5158,7 +5229,7 @@ void MethodTable::CheckRestore()
BOOL SatisfiesClassConstraints(TypeHandle instanceTypeHnd, TypeHandle typicalTypeHnd,
const InstantiationContext *pInstContext);
-static VOID DoAccessibilityCheck(MethodTable *pAskingMT, MethodTable *pTargetMT, UINT resIDWhy, BOOL checkTargetTypeTransparency)
+static VOID DoAccessibilityCheck(MethodTable *pAskingMT, MethodTable *pTargetMT, UINT resIDWhy)
{
CONTRACTL
{
@@ -5172,8 +5243,7 @@ static VOID DoAccessibilityCheck(MethodTable *pAskingMT, MethodTable *pTargetMT,
if (!ClassLoader::CanAccessClass(&accessContext,
pTargetMT, //the desired class
pTargetMT->GetAssembly(), //the desired class's assembly
- *AccessCheckOptions::s_pNormalAccessChecks,
- checkTargetTypeTransparency
+ *AccessCheckOptions::s_pNormalAccessChecks
))
{
SString displayName;
@@ -5222,7 +5292,7 @@ VOID DoAccessibilityCheckForConstraint(MethodTable *pAskingMT, TypeHandle thCons
}
else
{
- DoAccessibilityCheck(pAskingMT, thConstraint.GetMethodTable(), resIDWhy, FALSE);
+ DoAccessibilityCheck(pAskingMT, thConstraint.GetMethodTable(), resIDWhy);
}
}
@@ -5586,7 +5656,7 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const
// A transparenct type should not be allowed to derive from a critical type.
// However since this has never been enforced before we have many classes that
// violate this rule. Enforcing it now will be a breaking change.
- DoAccessibilityCheck(this, pParentMT, E_ACCESSDENIED, /* checkTargetTypeTransparency*/ FALSE);
+ DoAccessibilityCheck(this, pParentMT, E_ACCESSDENIED);
}
}
}
@@ -5605,7 +5675,7 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const
// A transparenct type should not be allowed to implement a critical interface.
// However since this has never been enforced before we have many classes that
// violate this rule. Enforcing it now will be a breaking change.
- DoAccessibilityCheck(this, it.GetInterface(), IDS_CLASSLOAD_INTERFACE_NO_ACCESS, /* checkTargetTypeTransparency*/ FALSE);
+ DoAccessibilityCheck(this, it.GetInterface(), IDS_CLASSLOAD_INTERFACE_NO_ACCESS);
}
}
}
@@ -5644,7 +5714,7 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const
if (fNeedAccessChecks)
{
- DoAccessibilityCheck(this, th.GetMethodTable(), E_ACCESSDENIED, FALSE);
+ DoAccessibilityCheck(this, th.GetMethodTable(), E_ACCESSDENIED);
}
}
@@ -5953,9 +6023,9 @@ void MethodTable::DoRestoreTypeKey()
// If we have an indirection cell then restore the m_pCanonMT and its module pointer
//
- if (union_getLowBits(m_pCanonMT) == UNION_INDIRECTION)
+ if (union_getLowBits(m_pCanonMT.GetValue()) == UNION_INDIRECTION)
{
- Module::RestoreMethodTablePointerRaw((MethodTable **)(union_getPointer(m_pCanonMT)),
+ Module::RestoreMethodTablePointerRaw((MethodTable **)(union_getPointer(m_pCanonMT.GetValue())),
GetLoaderModule(), CLASS_LOAD_UNRESTORED);
}
@@ -6031,7 +6101,7 @@ void MethodTable::Restore()
//
// Restore parent method table
//
- Module::RestoreMethodTablePointerRaw(GetParentMethodTablePtr(), GetLoaderModule(), CLASS_LOAD_APPROXPARENTS);
+ Module::RestoreMethodTablePointer(&m_pParentMethodTable, GetLoaderModule(), CLASS_LOAD_APPROXPARENTS);
//
// Restore interface classes
@@ -6192,7 +6262,7 @@ BOOL MethodTable::IsWinRTObjectType()
//==========================================================================================
// Return a pointer to the dictionary for an instantiated type
// Return NULL if not instantiated
-Dictionary* MethodTable::GetDictionary()
+PTR_Dictionary MethodTable::GetDictionary()
{
LIMITED_METHOD_DAC_CONTRACT;
@@ -6200,7 +6270,8 @@ Dictionary* MethodTable::GetDictionary()
{
// The instantiation for this class is stored in the type slots table
// *after* any inherited slots
- return GetPerInstInfo()[GetNumDicts()-1];
+ TADDR base = dac_cast<TADDR>(&(GetPerInstInfo()[GetNumDicts()-1]));
+ return PerInstInfoElem_t::GetValueMaybeNullAtPtr(base);
}
else
{
@@ -6217,7 +6288,8 @@ Instantiation MethodTable::GetInstantiation()
if (HasInstantiation())
{
PTR_GenericsDictInfo pDictInfo = GetGenericsDictInfo();
- return Instantiation(GetPerInstInfo()[pDictInfo->m_wNumDicts-1]->GetInstantiation(), pDictInfo->m_wNumTyPars);
+ TADDR base = dac_cast<TADDR>(&(GetPerInstInfo()[pDictInfo->m_wNumDicts-1]));
+ return Instantiation(PerInstInfoElem_t::GetValueMaybeNullAtPtr(base)->GetInstantiation(), pDictInfo->m_wNumTyPars);
}
else
{
@@ -7889,7 +7961,7 @@ BOOL MethodTable::SanityCheck()
// strings have component size2, all other non-arrays should have 0
_ASSERTE((GetComponentSize() <= 2) || IsArray());
- if (m_pEEClass == NULL)
+ if (m_pEEClass.IsNull())
{
if (IsAsyncPinType())
{
@@ -8029,7 +8101,7 @@ ClassCtorInfoEntry* MethodTable::GetClassCtorInfoIfExists()
if (HasBoxedRegularStatics())
{
ModuleCtorInfo *pModuleCtorInfo = GetZapModule()->GetZapModuleCtorInfo();
- DPTR(PTR_MethodTable) ppMT = pModuleCtorInfo->ppMT;
+ DPTR(RelativePointer<PTR_MethodTable>) ppMT = pModuleCtorInfo->ppMT;
PTR_DWORD hotHashOffsets = pModuleCtorInfo->hotHashOffsets;
PTR_DWORD coldHashOffsets = pModuleCtorInfo->coldHashOffsets;
@@ -8040,8 +8112,8 @@ ClassCtorInfoEntry* MethodTable::GetClassCtorInfoIfExists()
for (DWORD i = hotHashOffsets[hash]; i != hotHashOffsets[hash + 1]; i++)
{
- _ASSERTE(ppMT[i]);
- if (dac_cast<TADDR>(ppMT[i]) == dac_cast<TADDR>(this))
+ _ASSERTE(!ppMT[i].IsNull());
+ if (dac_cast<TADDR>(pModuleCtorInfo->GetMT(i)) == dac_cast<TADDR>(this))
{
return pModuleCtorInfo->cctorInfoHot + i;
}
@@ -8055,8 +8127,8 @@ ClassCtorInfoEntry* MethodTable::GetClassCtorInfoIfExists()
for (DWORD i = coldHashOffsets[hash]; i != coldHashOffsets[hash + 1]; i++)
{
- _ASSERTE(ppMT[i]);
- if (dac_cast<TADDR>(ppMT[i]) == dac_cast<TADDR>(this))
+ _ASSERTE(!ppMT[i].IsNull());
+ if (dac_cast<TADDR>(pModuleCtorInfo->GetMT(i)) == dac_cast<TADDR>(this))
{
return pModuleCtorInfo->cctorInfoCold + (i - pModuleCtorInfo->numElementsHot);
}
@@ -8080,13 +8152,8 @@ BOOL MethodTable::IsParentMethodTablePointerValid()
if (!GetWriteableData_NoLogging()->IsParentMethodTablePointerValid())
return FALSE;
- if (!GetFlag(enum_flag_HasIndirectParent))
- {
- return TRUE;
- }
- TADDR pMT;
- pMT = *PTR_TADDR(m_pParentMethodTable + offsetof(MethodTable, m_pParentMethodTable));
- return !CORCOMPILE_IS_POINTER_TAGGED(pMT);
+ TADDR base = dac_cast<TADDR>(this) + offsetof(MethodTable, m_pParentMethodTable);
+ return !m_pParentMethodTable.IsTagged(base);
}
#endif
@@ -9461,9 +9528,10 @@ MethodTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
DacEnumMemoryRegion(dac_cast<TADDR>(it.GetIndirectionSlot()), it.GetSize());
}
- if (m_pWriteableData.IsValid())
+ PTR_MethodTableWriteableData pWriteableData = ReadPointer(this, &MethodTable::m_pWriteableData);
+ if (pWriteableData.IsValid())
{
- m_pWriteableData.EnumMem();
+ pWriteableData.EnumMem();
}
if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
@@ -9651,24 +9719,21 @@ void MethodTable::SetSlot(UINT32 slotNumber, PCODE slotCode)
if (!IsCanonicalMethodTable())
{
- if (GetVtableIndirections()[indirectionIndex] == GetCanonicalMethodTable()->GetVtableIndirections()[indirectionIndex])
+ if (GetVtableIndirections()[indirectionIndex].GetValueMaybeNull() == GetCanonicalMethodTable()->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull())
fSharedVtableChunk = TRUE;
}
if (slotNumber < GetNumParentVirtuals())
{
- if (GetVtableIndirections()[indirectionIndex] == GetParentMethodTable()->GetVtableIndirections()[indirectionIndex])
+ if (GetVtableIndirections()[indirectionIndex].GetValueMaybeNull() == GetParentMethodTable()->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull())
fSharedVtableChunk = TRUE;
}
if (fSharedVtableChunk)
{
MethodDesc* pMD = GetMethodDescForSlotAddress(slotCode);
-#ifndef FEATURE_INTERPRETER
- // TBD: Make this take a "stable" debug arg, determining whether to make these assertions.
_ASSERTE(pMD->HasStableEntryPoint());
_ASSERTE(pMD->GetStableEntryPoint() == slotCode);
-#endif // FEATURE_INTERPRETER
}
}
#endif
@@ -9929,8 +9994,6 @@ bool MethodTable::ClassRequiresUnmanagedCodeCheck()
return false;
}
-#endif // !DACCESS_COMPILE
-
BOOL MethodTable::Validate()
@@ -9940,13 +10003,14 @@ BOOL MethodTable::Validate()
ASSERT_AND_CHECK(SanityCheck());
#ifdef _DEBUG
- if (m_pWriteableData == NULL)
+ if (m_pWriteableData.IsNull())
{
_ASSERTE(IsAsyncPinType());
return TRUE;
}
- DWORD dwLastVerifiedGCCnt = m_pWriteableData->m_dwLastVerifedGCCnt;
+ MethodTableWriteableData *pWriteableData = m_pWriteableData.GetValue();
+ DWORD dwLastVerifiedGCCnt = pWriteableData->m_dwLastVerifedGCCnt;
// Here we used to assert that (dwLastVerifiedGCCnt <= GCHeapUtilities::GetGCHeap()->GetGcCount()) but
// this is no longer true because with background gc. Since the purpose of having
// m_dwLastVerifedGCCnt is just to only verify the same method table once for each GC
@@ -9977,13 +10041,15 @@ BOOL MethodTable::Validate()
#ifdef _DEBUG
// It is not a fatal error to fail the update the counter. We will run slower and retry next time,
// but the system will function properly.
- if (EnsureWritablePagesNoThrow(m_pWriteableData, sizeof(MethodTableWriteableData)))
- m_pWriteableData->m_dwLastVerifedGCCnt = GCHeapUtilities::GetGCHeap()->GetGcCount();
+ if (EnsureWritablePagesNoThrow(pWriteableData, sizeof(MethodTableWriteableData)))
+ pWriteableData->m_dwLastVerifedGCCnt = GCHeapUtilities::GetGCHeap()->GetGcCount();
#endif //_DEBUG
return TRUE;
}
+#endif // !DACCESS_COMPILE
+
NOINLINE BYTE *MethodTable::GetLoaderAllocatorObjectForGC()
{
WRAPPER_NO_CONTRACT;
diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h
index 118a883b6e..60059675e9 100644
--- a/src/vm/methodtable.h
+++ b/src/vm/methodtable.h
@@ -110,25 +110,40 @@ struct InterfaceInfo_t
friend class NativeImageDumper;
#endif
- FixupPointer<PTR_MethodTable> m_pMethodTable; // Method table of the interface
+ // Method table of the interface
+#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_)
+ RelativeFixupPointer<PTR_MethodTable> m_pMethodTable;
+#else
+ FixupPointer<PTR_MethodTable> m_pMethodTable;
+#endif
public:
FORCEINLINE PTR_MethodTable GetMethodTable()
{
LIMITED_METHOD_CONTRACT;
- return m_pMethodTable.GetValue();
+ return ReadPointerMaybeNull(this, &InterfaceInfo_t::m_pMethodTable);
}
#ifndef DACCESS_COMPILE
void SetMethodTable(MethodTable * pMT)
{
LIMITED_METHOD_CONTRACT;
- m_pMethodTable.SetValue(pMT);
+ m_pMethodTable.SetValueMaybeNull(pMT);
}
// Get approximate method table. This is used by the type loader before the type is fully loaded.
PTR_MethodTable GetApproxMethodTable(Module * pContainingModule);
-#endif
+#endif // !DACCESS_COMPILE
+
+#ifndef DACCESS_COMPILE
+ InterfaceInfo_t(InterfaceInfo_t &right)
+ {
+ m_pMethodTable.SetValueMaybeNull(right.m_pMethodTable.GetValueMaybeNull());
+ }
+#else // !DACCESS_COMPILE
+private:
+ InterfaceInfo_t(InterfaceInfo_t &right);
+#endif // !DACCESS_COMPILE
}; // struct InterfaceInfo_t
typedef DPTR(InterfaceInfo_t) PTR_InterfaceInfo;
@@ -247,7 +262,7 @@ typedef DPTR(GenericsDictInfo) PTR_GenericsDictInfo;
struct GenericsStaticsInfo
{
// Pointer to field descs for statics
- PTR_FieldDesc m_pFieldDescs;
+ RelativePointer<PTR_FieldDesc> m_pFieldDescs;
// Method table ID for statics
SIZE_T m_DynamicTypeID;
@@ -390,6 +405,9 @@ struct MethodTableWriteableData
enum_flag_SkipWinRTOverride = 0x00000100, // No WinRT override is needed
+ enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x00000200, // Is any field type or sub field type overrode Equals or GetHashCode
+ enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode = 0x00000400, // Whether we have checked the overridden Equals or GetHashCode
+
#ifdef FEATURE_PREJIT
// These flags are used only at ngen time. We store them here since
// we are running out of available flags in MethodTable. They may eventually
@@ -1235,6 +1253,41 @@ public:
WRAPPER_NO_CONTRACT;
FastInterlockOr(EnsureWritablePages(&GetWriteableDataForWrite_NoLogging()->m_dwFlags), MethodTableWriteableData::enum_flag_SkipWinRTOverride);
}
+
+ inline BOOL CanCompareBitsOrUseFastGetHashCode()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (GetWriteableData_NoLogging()->m_dwFlags & MethodTableWriteableData::enum_flag_CanCompareBitsOrUseFastGetHashCode);
+ }
+
+ // If canCompare is true, this method ensure an atomic operation for setting
+ // enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode and enum_flag_CanCompareBitsOrUseFastGetHashCode flags.
+ inline void SetCanCompareBitsOrUseFastGetHashCode(BOOL canCompare)
+ {
+ WRAPPER_NO_CONTRACT
+ if (canCompare)
+ {
+ // Set checked and canCompare flags in one interlocked operation.
+ FastInterlockOr(EnsureWritablePages(&GetWriteableDataForWrite_NoLogging()->m_dwFlags),
+ MethodTableWriteableData::enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode | MethodTableWriteableData::enum_flag_CanCompareBitsOrUseFastGetHashCode);
+ }
+ else
+ {
+ SetHasCheckedCanCompareBitsOrUseFastGetHashCode();
+ }
+ }
+
+ inline BOOL HasCheckedCanCompareBitsOrUseFastGetHashCode()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (GetWriteableData_NoLogging()->m_dwFlags & MethodTableWriteableData::enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode);
+ }
+
+ inline void SetHasCheckedCanCompareBitsOrUseFastGetHashCode()
+ {
+ WRAPPER_NO_CONTRACT;
+ FastInterlockOr(EnsureWritablePages(&GetWriteableDataForWrite_NoLogging()->m_dwFlags), MethodTableWriteableData::enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode);
+ }
inline void SetIsDependenciesLoaded()
{
@@ -1498,7 +1551,10 @@ public:
CONSISTENCY_CHECK(slotNum < GetNumVirtuals());
// Virtual slots live in chunks pointed to by vtable indirections
- return *(GetVtableIndirections()[GetIndexOfVtableIndirection(slotNum)] + GetIndexAfterVtableIndirection(slotNum));
+
+ DWORD index = GetIndexOfVtableIndirection(slotNum);
+ TADDR base = dac_cast<TADDR>(&(GetVtableIndirections()[index]));
+ return *(VTableIndir_t::GetValueMaybeNullAtPtr(base) + GetIndexAfterVtableIndirection(slotNum));
}
PTR_PCODE GetSlotPtrRaw(UINT32 slotNum)
@@ -1510,7 +1566,9 @@ public:
if (slotNum < GetNumVirtuals())
{
// Virtual slots live in chunks pointed to by vtable indirections
- return GetVtableIndirections()[GetIndexOfVtableIndirection(slotNum)] + GetIndexAfterVtableIndirection(slotNum);
+ DWORD index = GetIndexOfVtableIndirection(slotNum);
+ TADDR base = dac_cast<TADDR>(&(GetVtableIndirections()[index]));
+ return VTableIndir_t::GetValueMaybeNullAtPtr(base) + GetIndexAfterVtableIndirection(slotNum);
}
else if (HasSingleNonVirtualSlot())
{
@@ -1594,12 +1652,18 @@ public:
#define VTABLE_SLOTS_PER_CHUNK 8
#define VTABLE_SLOTS_PER_CHUNK_LOG2 3
+#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_)
+ typedef RelativePointer<PTR_PCODE> VTableIndir_t;
+#else
+ typedef PlainPointer<PTR_PCODE> VTableIndir_t;
+#endif
+
static DWORD GetIndexOfVtableIndirection(DWORD slotNum);
static DWORD GetStartSlotForVtableIndirection(UINT32 indirectionIndex, DWORD wNumVirtuals);
static DWORD GetEndSlotForVtableIndirection(UINT32 indirectionIndex, DWORD wNumVirtuals);
static UINT32 GetIndexAfterVtableIndirection(UINT32 slotNum);
static DWORD GetNumVtableIndirections(DWORD wNumVirtuals);
- PTR_PTR_PCODE GetVtableIndirections();
+ DPTR(VTableIndir_t) GetVtableIndirections();
DWORD GetNumVtableIndirections();
class VtableIndirectionSlotIterator
@@ -1607,7 +1671,7 @@ public:
friend class MethodTable;
private:
- PTR_PTR_PCODE m_pSlot;
+ DPTR(VTableIndir_t) m_pSlot;
DWORD m_i;
DWORD m_count;
PTR_MethodTable m_pMT;
@@ -2004,8 +2068,18 @@ public:
LIMITED_METHOD_CONTRACT;
SetFlag(enum_flag_IsHFA);
}
+#else // !FEATURE_HFA
+ bool IsHFA();
#endif // FEATURE_HFA
+ // Get the HFA type. This is supported both with FEATURE_HFA, in which case it
+ // depends on the cached bit on the class, or without, in which case it is recomputed
+ // for each invocation.
+ CorElementType GetHFAType();
+ // The managed and unmanaged HFA type can differ for types with layout. The following two methods return the unmanaged HFA type.
+ bool IsNativeHFA();
+ CorElementType GetNativeHFAType();
+
#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
inline bool IsRegPassedStruct()
{
@@ -2020,15 +2094,6 @@ public:
}
#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
-#ifdef FEATURE_HFA
-
- CorElementType GetHFAType();
-
- // The managed and unmanaged HFA type can differ for types with layout. The following two methods return the unmanaged HFA type.
- bool IsNativeHFA();
- CorElementType GetNativeHFAType();
-#endif // FEATURE_HFA
-
#ifdef FEATURE_64BIT_ALIGNMENT
// Returns true iff the native view of this type requires 64-bit aligment.
bool NativeRequiresAlign8();
@@ -2100,6 +2165,12 @@ public:
// THE METHOD TABLE PARENT (SUPERCLASS/BASE CLASS)
//
+#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_)
+ typedef RelativeFixupPointer<PTR_MethodTable> ParentMT_t;
+#else
+ typedef PlainPointer<PTR_MethodTable> ParentMT_t;
+#endif
+
BOOL HasApproxParent()
{
LIMITED_METHOD_DAC_CONTRACT;
@@ -2118,32 +2189,24 @@ public:
LIMITED_METHOD_DAC_CONTRACT;
PRECONDITION(IsParentMethodTablePointerValid());
-
- TADDR pMT = m_pParentMethodTable;
-#ifdef FEATURE_PREJIT
- if (GetFlag(enum_flag_HasIndirectParent))
- pMT = *PTR_TADDR(m_pParentMethodTable + offsetof(MethodTable, m_pParentMethodTable));
-#endif
- return PTR_MethodTable(pMT);
+ return ReadPointerMaybeNull(this, &MethodTable::m_pParentMethodTable);
}
- inline static PTR_VOID GetParentMethodTableOrIndirection(PTR_VOID pMT)
+ inline static PTR_VOID GetParentMethodTable(PTR_VOID pMT)
{
- WRAPPER_NO_CONTRACT;
- return PTR_VOID(*PTR_TADDR(dac_cast<TADDR>(pMT) + offsetof(MethodTable, m_pParentMethodTable)));
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ PTR_MethodTable pMethodTable = dac_cast<PTR_MethodTable>(pMT);
+ return pMethodTable->GetParentMethodTable();
}
- inline MethodTable ** GetParentMethodTablePtr()
+#ifndef DACCESS_COMPILE
+ inline ParentMT_t * GetParentMethodTablePlainOrRelativePointerPtr()
{
- WRAPPER_NO_CONTRACT;
-
-#ifdef FEATURE_PREJIT
- return GetFlag(enum_flag_HasIndirectParent) ?
- (MethodTable **)(m_pParentMethodTable + offsetof(MethodTable, m_pParentMethodTable)) :(MethodTable **)&m_pParentMethodTable;
-#else
- return (MethodTable **)&m_pParentMethodTable;
-#endif
+ LIMITED_METHOD_CONTRACT;
+ return &m_pParentMethodTable;
}
+#endif // !DACCESS_COMPILE
// Is the parent method table pointer equal to the given argument?
BOOL ParentEquals(PTR_MethodTable pMT)
@@ -2162,8 +2225,8 @@ public:
void SetParentMethodTable (MethodTable *pParentMethodTable)
{
LIMITED_METHOD_CONTRACT;
- PRECONDITION(!GetFlag(enum_flag_HasIndirectParent));
- m_pParentMethodTable = (TADDR)pParentMethodTable;
+ PRECONDITION(!m_pParentMethodTable.IsIndirectPtrMaybeNull());
+ m_pParentMethodTable.SetValueMaybeNull(pParentMethodTable);
#ifdef _DEBUG
GetWriteableDataForWrite_NoLogging()->SetParentMethodTablePointerValid();
#endif
@@ -2209,12 +2272,12 @@ public:
inline void SetClass(EEClass *pClass)
{
LIMITED_METHOD_CONTRACT;
- m_pEEClass = pClass;
+ m_pEEClass.SetValue(pClass);
}
inline void SetCanonicalMethodTable(MethodTable * pMT)
{
- m_pCanonMT = (TADDR)pMT | MethodTable::UNION_METHODTABLE;
+ m_pCanonMT.SetValue((TADDR)pMT | MethodTable::UNION_METHODTABLE);
}
#endif
@@ -2647,7 +2710,7 @@ public:
{
WRAPPER_NO_CONTRACT;
_ASSERTE(HasGenericsStaticsInfo());
- return GetGenericsStaticsInfo()->m_pFieldDescs;
+ return ReadPointerMaybeNull((GenericsStaticsInfo *)GetGenericsStaticsInfo(), &GenericsStaticsInfo::m_pFieldDescs);
}
BOOL HasCrossModuleGenericStaticsInfo()
@@ -3000,12 +3063,20 @@ public:
// must have a dictionary entry. On the other hand, for instantiations shared with Dict<string,double> the opposite holds.
//
+#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_)
+ typedef RelativePointer<PTR_Dictionary> PerInstInfoElem_t;
+ typedef RelativePointer<DPTR(PerInstInfoElem_t)> PerInstInfo_t;
+#else
+ typedef PlainPointer<PTR_Dictionary> PerInstInfoElem_t;
+ typedef PlainPointer<DPTR(PerInstInfoElem_t)> PerInstInfo_t;
+#endif
+
// Return a pointer to the per-instantiation information. See field itself for comments.
- DPTR(PTR_Dictionary) GetPerInstInfo()
+ DPTR(PerInstInfoElem_t) GetPerInstInfo()
{
LIMITED_METHOD_DAC_CONTRACT;
_ASSERTE(HasPerInstInfo());
- return dac_cast<DPTR(PTR_Dictionary)>(m_pMultipurposeSlot1);
+ return ReadPointer(this, &MethodTable::m_pPerInstInfo);
}
BOOL HasPerInstInfo()
{
@@ -3013,15 +3084,20 @@ public:
return GetFlag(enum_flag_HasPerInstInfo) && !IsArray();
}
#ifndef DACCESS_COMPILE
+ static inline bool IsPerInstInfoRelative()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return decltype(m_pPerInstInfo)::isRelative;
+ }
static inline DWORD GetOffsetOfPerInstInfo()
{
LIMITED_METHOD_CONTRACT;
return offsetof(MethodTable, m_pPerInstInfo);
}
- void SetPerInstInfo(Dictionary** pPerInstInfo)
+ void SetPerInstInfo(PerInstInfoElem_t *pPerInstInfo)
{
LIMITED_METHOD_CONTRACT;
- m_pPerInstInfo = pPerInstInfo;
+ m_pPerInstInfo.SetValue(pPerInstInfo);
}
void SetDictInfo(WORD numDicts, WORD numTyPars)
{
@@ -3041,7 +3117,7 @@ public:
// Get a pointer to the dictionary for this instantiated type
// (The instantiation is stored in the initial slots of the dictionary)
// If not instantiated, return NULL
- Dictionary* GetDictionary();
+ PTR_Dictionary GetDictionary();
#ifdef FEATURE_PREJIT
//
@@ -3127,36 +3203,39 @@ public:
// Private part of MethodTable
// ------------------------------------------------------------------
+#ifndef DACCESS_COMPILE
inline void SetWriteableData(PTR_MethodTableWriteableData pMTWriteableData)
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(pMTWriteableData);
- m_pWriteableData = pMTWriteableData;
+ m_pWriteableData.SetValue(pMTWriteableData);
}
-
+#endif
+
inline PTR_Const_MethodTableWriteableData GetWriteableData() const
{
LIMITED_METHOD_DAC_CONTRACT;
g_IBCLogger.LogMethodTableWriteableDataAccess(this);
- return m_pWriteableData;
+ return GetWriteableData_NoLogging();
}
inline PTR_Const_MethodTableWriteableData GetWriteableData_NoLogging() const
{
LIMITED_METHOD_DAC_CONTRACT;
- return m_pWriteableData;
+ return ReadPointer(this, &MethodTable::m_pWriteableData);
}
inline PTR_MethodTableWriteableData GetWriteableDataForWrite()
{
- LIMITED_METHOD_CONTRACT;
+ LIMITED_METHOD_DAC_CONTRACT;
g_IBCLogger.LogMethodTableWriteableDataWriteAccess(this);
- return m_pWriteableData;
+ return GetWriteableDataForWrite_NoLogging();
}
inline PTR_MethodTableWriteableData GetWriteableDataForWrite_NoLogging()
{
- return m_pWriteableData;
+ LIMITED_METHOD_DAC_CONTRACT;
+ return ReadPointer(this, &MethodTable::m_pWriteableData);
}
//-------------------------------------------------------------------
@@ -4050,11 +4129,15 @@ private:
// if enum_flag_enum_flag_HasIndirectParent is set. The indirection is offset by offsetof(MethodTable, m_pParentMethodTable).
// It allows casting helpers to go through parent chain natually. Casting helper do not need need the explicit check
// for enum_flag_HasIndirectParentMethodTable.
- TADDR m_pParentMethodTable;
+ ParentMT_t m_pParentMethodTable;
- PTR_Module m_pLoaderModule; // LoaderModule. It is equal to the ZapModule in ngened images
+ RelativePointer<PTR_Module> m_pLoaderModule; // LoaderModule. It is equal to the ZapModule in ngened images
- PTR_MethodTableWriteableData m_pWriteableData;
+#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_)
+ RelativePointer<PTR_MethodTableWriteableData> m_pWriteableData;
+#else
+ PlainPointer<PTR_MethodTableWriteableData> m_pWriteableData;
+#endif
// The value of lowest two bits describe what the union contains
enum LowBits {
@@ -4066,8 +4149,13 @@ private:
static const TADDR UNION_MASK = 3;
union {
- EEClass * m_pEEClass;
- TADDR m_pCanonMT;
+#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_)
+ RelativePointer<DPTR(EEClass)> m_pEEClass;
+ RelativePointer<TADDR> m_pCanonMT;
+#else
+ PlainPointer<DPTR(EEClass)> m_pEEClass;
+ PlainPointer<TADDR> m_pCanonMT;
+#endif
};
__forceinline static LowBits union_getLowBits(TADDR pCanonMT)
@@ -4089,14 +4177,18 @@ private:
union
{
- PTR_Dictionary * m_pPerInstInfo;
- TADDR m_ElementTypeHnd;
- TADDR m_pMultipurposeSlot1;
+ PerInstInfo_t m_pPerInstInfo;
+ TADDR m_ElementTypeHnd;
+ TADDR m_pMultipurposeSlot1;
};
public:
union
{
- InterfaceInfo_t * m_pInterfaceMap;
+#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_)
+ RelativePointer<PTR_InterfaceInfo> m_pInterfaceMap;
+#else
+ PlainPointer<PTR_InterfaceInfo> m_pInterfaceMap;
+#endif
TADDR m_pMultipurposeSlot2;
};
diff --git a/src/vm/methodtable.inl b/src/vm/methodtable.inl
index a8a4d2301c..4fa81c931b 100644
--- a/src/vm/methodtable.inl
+++ b/src/vm/methodtable.inl
@@ -23,24 +23,26 @@ inline PTR_EEClass MethodTable::GetClass_NoLogging()
{
LIMITED_METHOD_DAC_CONTRACT;
+ TADDR addr = ReadPointer(this, &MethodTable::m_pCanonMT);
+
#ifdef _DEBUG
- LowBits lowBits = union_getLowBits(m_pCanonMT);
+ LowBits lowBits = union_getLowBits(addr);
if (lowBits == UNION_EECLASS)
{
- return PTR_EEClass(m_pCanonMT);
+ return PTR_EEClass(addr);
}
else if (lowBits == UNION_METHODTABLE)
{
// pointer to canonical MethodTable.
- TADDR canonicalMethodTable = union_getPointer(m_pCanonMT);
- return PTR_EEClass(PTR_MethodTable(canonicalMethodTable)->m_pCanonMT);
+ TADDR canonicalMethodTable = union_getPointer(addr);
+ return PTR_EEClass(ReadPointer((MethodTable *) PTR_MethodTable(canonicalMethodTable), &MethodTable::m_pCanonMT));
}
#ifdef FEATURE_PREJIT
else if (lowBits == UNION_INDIRECTION)
{
// pointer to indirection cell that points to canonical MethodTable
- TADDR canonicalMethodTable = *PTR_TADDR(union_getPointer(m_pCanonMT));
- return PTR_EEClass(PTR_MethodTable(canonicalMethodTable)->m_pCanonMT);
+ TADDR canonicalMethodTable = *PTR_TADDR(union_getPointer(addr));
+ return PTR_EEClass(ReadPointer((MethodTable *) PTR_MethodTable(canonicalMethodTable), &MethodTable::m_pCanonMT));
}
#endif
#ifdef DACCESS_COMPILE
@@ -52,8 +54,6 @@ inline PTR_EEClass MethodTable::GetClass_NoLogging()
#else
- TADDR addr = m_pCanonMT;
-
if ((addr & 2) == 0)
{
// pointer to EEClass
@@ -65,12 +65,12 @@ inline PTR_EEClass MethodTable::GetClass_NoLogging()
{
// pointer to indirection cell that points to canonical MethodTable
TADDR canonicalMethodTable = *PTR_TADDR(addr - 3);
- return PTR_EEClass(PTR_MethodTable(canonicalMethodTable)->m_pCanonMT);
+ return PTR_EEClass(ReadPointer((MethodTable *) PTR_MethodTable(canonicalMethodTable), &MethodTable::m_pCanonMT));
}
#endif
// pointer to canonical MethodTable.
- return PTR_EEClass(PTR_MethodTable(addr - 2)->m_pCanonMT);
+ return PTR_EEClass(ReadPointer((MethodTable *) PTR_MethodTable(addr - 2), &MethodTable::m_pCanonMT));
#endif
}
@@ -113,25 +113,27 @@ inline BOOL MethodTable::IsClassPointerValid()
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
- LowBits lowBits = union_getLowBits(m_pCanonMT);
+ TADDR addr = ReadPointer(this, &MethodTable::m_pCanonMT);
+
+ LowBits lowBits = union_getLowBits(addr);
if (lowBits == UNION_EECLASS)
{
- return (m_pEEClass != NULL);
+ return !m_pEEClass.IsNull();
}
else if (lowBits == UNION_METHODTABLE)
{
// pointer to canonical MethodTable.
- TADDR canonicalMethodTable = union_getPointer(m_pCanonMT);
- return (PTR_MethodTable(canonicalMethodTable)->m_pEEClass != NULL);
+ TADDR canonicalMethodTable = union_getPointer(addr);
+ return !PTR_MethodTable(canonicalMethodTable)->m_pEEClass.IsNull();
}
#ifdef FEATURE_PREJIT
else if (lowBits == UNION_INDIRECTION)
{
// pointer to indirection cell that points to canonical MethodTable
- TADDR canonicalMethodTable = *PTR_TADDR(union_getPointer(m_pCanonMT));
+ TADDR canonicalMethodTable = *PTR_TADDR(union_getPointer(addr));
if (CORCOMPILE_IS_POINTER_TAGGED(canonicalMethodTable))
return FALSE;
- return (PTR_MethodTable(canonicalMethodTable)->m_pEEClass != NULL);
+ return !PTR_MethodTable(canonicalMethodTable)->m_pEEClass.IsNull();
}
#endif
_ASSERTE(!"Malformed m_pEEClass in MethodTable");
@@ -161,7 +163,7 @@ inline PTR_Module MethodTable::GetZapModule()
PTR_Module zapModule = NULL;
if (IsZapped())
{
- zapModule = m_pLoaderModule;
+ zapModule = ReadPointer(this, &MethodTable::m_pLoaderModule);
}
return zapModule;
@@ -171,7 +173,7 @@ inline PTR_Module MethodTable::GetZapModule()
inline PTR_Module MethodTable::GetLoaderModule()
{
LIMITED_METHOD_DAC_CONTRACT;
- return m_pLoaderModule;
+ return ReadPointer(this, &MethodTable::m_pLoaderModule);
}
inline PTR_LoaderAllocator MethodTable::GetLoaderAllocator()
@@ -187,7 +189,7 @@ inline PTR_LoaderAllocator MethodTable::GetLoaderAllocator()
inline void MethodTable::SetLoaderModule(Module* pModule)
{
WRAPPER_NO_CONTRACT;
- m_pLoaderModule = pModule;
+ m_pLoaderModule.SetValue(pModule);
}
inline void MethodTable::SetLoaderAllocator(LoaderAllocator* pAllocator)
@@ -884,10 +886,10 @@ inline DWORD MethodTable::GetNumVtableIndirections(DWORD wNumVirtuals)
}
//==========================================================================================
-inline PTR_PTR_PCODE MethodTable::GetVtableIndirections()
+inline DPTR(MethodTable::VTableIndir_t) MethodTable::GetVtableIndirections()
{
LIMITED_METHOD_DAC_CONTRACT;
- return dac_cast<PTR_PTR_PCODE>(dac_cast<TADDR>(this) + sizeof(MethodTable));
+ return dac_cast<DPTR(VTableIndir_t)>(dac_cast<TADDR>(this) + sizeof(MethodTable));
}
//==========================================================================================
@@ -949,7 +951,7 @@ inline DWORD MethodTable::VtableIndirectionSlotIterator::GetOffsetFromMethodTabl
WRAPPER_NO_CONTRACT;
PRECONDITION(m_i != (DWORD) -1 && m_i < m_count);
- return GetVtableOffset() + sizeof(PTR_PCODE) * m_i;
+ return GetVtableOffset() + sizeof(VTableIndir_t) * m_i;
}
//==========================================================================================
@@ -958,7 +960,7 @@ inline PTR_PCODE MethodTable::VtableIndirectionSlotIterator::GetIndirectionSlot(
LIMITED_METHOD_DAC_CONTRACT;
PRECONDITION(m_i != (DWORD) -1 && m_i < m_count);
- return *m_pSlot;
+ return m_pSlot->GetValueMaybeNull(dac_cast<TADDR>(m_pSlot));
}
//==========================================================================================
@@ -966,7 +968,7 @@ inline PTR_PCODE MethodTable::VtableIndirectionSlotIterator::GetIndirectionSlot(
inline void MethodTable::VtableIndirectionSlotIterator::SetIndirectionSlot(PTR_PCODE pChunk)
{
LIMITED_METHOD_CONTRACT;
- *m_pSlot = pChunk;
+ m_pSlot->SetValueMaybeNull(pChunk);
}
#endif
@@ -1144,8 +1146,10 @@ inline PTR_MethodTable MethodTable::GetCanonicalMethodTable()
{
LIMITED_METHOD_DAC_CONTRACT;
+ TADDR addr = ReadPointer(this, &MethodTable::m_pCanonMT);
+
#ifdef _DEBUG
- LowBits lowBits = union_getLowBits(m_pCanonMT);
+ LowBits lowBits = union_getLowBits(addr);
if (lowBits == UNION_EECLASS)
{
return dac_cast<PTR_MethodTable>(this);
@@ -1153,18 +1157,17 @@ inline PTR_MethodTable MethodTable::GetCanonicalMethodTable()
else if (lowBits == UNION_METHODTABLE)
{
// pointer to canonical MethodTable.
- return PTR_MethodTable(union_getPointer(m_pCanonMT));
+ return PTR_MethodTable(union_getPointer(addr));
}
#ifdef FEATURE_PREJIT
else if (lowBits == UNION_INDIRECTION)
{
- return PTR_MethodTable(*PTR_TADDR(union_getPointer(m_pCanonMT)));
+ return PTR_MethodTable(*PTR_TADDR(union_getPointer(addr)));
}
#endif
_ASSERTE(!"Malformed m_pCanonMT in MethodTable");
return NULL;
#else
- TADDR addr = m_pCanonMT;
if ((addr & 2) == 0)
return dac_cast<PTR_MethodTable>(this);
@@ -1184,11 +1187,12 @@ inline TADDR MethodTable::GetCanonicalMethodTableFixup()
LIMITED_METHOD_DAC_CONTRACT;
#ifdef FEATURE_PREJIT
- LowBits lowBits = union_getLowBits(m_pCanonMT);
+ TADDR addr = ReadPointer(this, &MethodTable::m_pCanonMT);
+ LowBits lowBits = union_getLowBits(addr);
if (lowBits == UNION_INDIRECTION)
{
// pointer to canonical MethodTable.
- return *PTR_TADDR(union_getPointer(m_pCanonMT));
+ return *PTR_TADDR(union_getPointer(addr));
}
else
#endif
@@ -1251,7 +1255,7 @@ inline BOOL MethodTable::HasExplicitSize()
inline DWORD MethodTable::GetPerInstInfoSize()
{
LIMITED_METHOD_DAC_CONTRACT;
- return GetNumDicts() * sizeof(TypeHandle*);
+ return GetNumDicts() * sizeof(PerInstInfoElem_t);
}
//==========================================================================================
@@ -1303,7 +1307,7 @@ inline BOOL MethodTable::IsCanonicalMethodTable()
{
LIMITED_METHOD_DAC_CONTRACT;
- return (union_getLowBits(m_pCanonMT) == UNION_EECLASS);
+ return (union_getLowBits(ReadPointer(this, &MethodTable::m_pCanonMT)) == UNION_EECLASS);
}
//==========================================================================================
@@ -1337,7 +1341,7 @@ inline PTR_InterfaceInfo MethodTable::GetInterfaceMap()
{
LIMITED_METHOD_DAC_CONTRACT;
- return dac_cast<PTR_InterfaceInfo>(m_pMultipurposeSlot2); // m_pInterfaceMap
+ return ReadPointer(this, &MethodTable::m_pInterfaceMap);
}
//==========================================================================================
@@ -1350,7 +1354,7 @@ FORCEINLINE TADDR MethodTable::GetMultipurposeSlotPtr(WFLAGS2_ENUM flag, const B
DWORD offset = offsets[GetFlag((WFLAGS2_ENUM)(flag - 1))];
if (offset >= sizeof(MethodTable)) {
- offset += GetNumVtableIndirections() * sizeof(PTR_PCODE);
+ offset += GetNumVtableIndirections() * sizeof(VTableIndir_t);
}
return dac_cast<TADDR>(this) + offset;
@@ -1365,7 +1369,7 @@ FORCEINLINE DWORD MethodTable::GetOffsetOfOptionalMember(OptionalMemberId id)
DWORD offset = c_OptionalMembersStartOffsets[GetFlag(enum_flag_MultipurposeSlotsMask)];
- offset += GetNumVtableIndirections() * sizeof(PTR_PCODE);
+ offset += GetNumVtableIndirections() * sizeof(VTableIndir_t);
#undef METHODTABLE_OPTIONAL_MEMBER
#define METHODTABLE_OPTIONAL_MEMBER(NAME, TYPE, GETTER) \
@@ -1732,7 +1736,7 @@ FORCEINLINE PTR_Module MethodTable::GetGenericsStaticsModuleAndID(DWORD * pID)
_ASSERTE(!IsStringOrArray());
if (m_dwFlags & enum_flag_StaticsMask_IfGenericsThenCrossModule)
{
- CrossModuleGenericsStaticsInfo *pInfo = m_pWriteableData->GetCrossModuleGenericsStaticsInfo();
+ CrossModuleGenericsStaticsInfo *pInfo = ReadPointer(this, &MethodTable::m_pWriteableData)->GetCrossModuleGenericsStaticsInfo();
_ASSERTE(FitsIn<DWORD>(pInfo->m_DynamicTypeID) || pInfo->m_DynamicTypeID == (SIZE_T)-1);
*pID = static_cast<DWORD>(pInfo->m_DynamicTypeID);
return pInfo->m_pModuleForStatics;
diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp
index 3a738df496..f9876608eb 100644
--- a/src/vm/methodtablebuilder.cpp
+++ b/src/vm/methodtablebuilder.cpp
@@ -21,7 +21,6 @@
#include "encee.h"
#include "mdaassistants.h"
#include "ecmakey.h"
-#include "security.h"
#include "customattribute.h"
#include "typestring.h"
@@ -1822,7 +1821,7 @@ MethodTableBuilder::BuildMethodTableThrowing(
GetHalfBakedClass()->SetIsNotTightlyPacked();
#ifdef FEATURE_HFA
- CheckForHFA(pByValueClassCache);
+ GetHalfBakedClass()->CheckForHFA(pByValueClassCache);
#endif
#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
#ifdef FEATURE_HFA
@@ -1844,7 +1843,7 @@ MethodTableBuilder::BuildMethodTableThrowing(
#ifdef FEATURE_HFA
if (HasLayout())
{
- CheckForNativeHFA();
+ GetHalfBakedClass()->CheckForNativeHFA();
}
#endif
@@ -4168,19 +4167,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList,
pszFieldName
);
- // Check if the ValueType field containing non-publics is overlapped
- if (HasExplicitFieldOffsetLayout()
- && pLayoutFieldInfo != NULL
- && pLayoutFieldInfo->m_fIsOverlapped
- && pByValueClass != NULL
- && pByValueClass->GetClass()->HasNonPublicFields())
- {
- if (!Security::CanSkipVerification(GetAssembly()->GetDomainAssembly()))
- {
- BuildMethodTableThrowException(IDS_CLASSLOAD_BADOVERLAP);
- }
- }
-
// We're using FieldDesc::m_pMTOfEnclosingClass to temporarily store the field's size.
//
if (fIsByValue)
@@ -4281,14 +4267,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList,
BAD_FORMAT_NOTHROW_ASSERT(!"ObjectRef in an RVA field");
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
}
- if (pByValueClass->GetClass()->HasNonPublicFields())
- {
- if (!Security::CanHaveRVA(GetAssembly()))
- {
- BAD_FORMAT_NOTHROW_ASSERT(!"ValueType with non-public fields as a type of an RVA field");
- BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
- }
- }
}
}
@@ -4321,14 +4299,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList,
{
fldSize = GetSizeForCorElementType(FieldDescElementType);
}
- if (!GetModule()->CheckRvaField(rva, fldSize))
- {
- if (!Security::CanHaveRVA(GetAssembly()))
- {
- BAD_FORMAT_NOTHROW_ASSERT(!"Illegal RVA of a mapped field");
- BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
- }
- }
pFD->SetOffsetRVA(rva);
}
@@ -4368,14 +4338,6 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList,
BAD_FORMAT_NOTHROW_ASSERT(!"ObjectRef in an RVA self-referencing static field");
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
}
- if (HasNonPublicFields())
- { // RVA ValueTypes with non-public fields must be checked against security
- if (!Security::CanHaveRVA(GetAssembly()))
- {
- BAD_FORMAT_NOTHROW_ASSERT(!"ValueType with non-public fields as a type of an RVA self-referencing static field");
- BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
- }
- }
}
DWORD dwNumInstanceFields = dwCurrentDeclaredField + (HasParent() ? GetParentMethodTable()->GetNumInstanceFields() : 0);
@@ -4464,15 +4426,6 @@ MethodTableBuilder::VerifySelfReferencingStaticValueTypeFields_WithRVA(
{
DWORD rva;
IfFailThrow(GetMDImport()->GetFieldRVA(pFD->GetMemberDef(), &rva));
-
- if (!GetModule()->CheckRvaField(rva, bmtFP->NumInstanceFieldBytes))
- {
- if (!Security::CanHaveRVA(GetAssembly()))
- {
- BAD_FORMAT_NOTHROW_ASSERT(!"Illegal RVA of a mapped self-referencing static field");
- BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
- }
- }
}
}
}
@@ -5126,6 +5079,20 @@ MethodTableBuilder::InitNewMethodDesc(
pNewMD->SetNotInline(true);
}
+ // Check for methods marked as [Intrinsic]
+ if (GetModule()->IsSystem())
+ {
+ HRESULT hr = GetMDImport()->GetCustomAttributeByName(pMethod->GetMethodSignature().GetToken(),
+ g_CompilerServicesIntrinsicAttribute,
+ NULL,
+ NULL);
+
+ if (hr == S_OK)
+ {
+ pNewMD->SetIsJitIntrinsic();
+ }
+ }
+
pNewMD->SetSlot(pMethod->GetSlotIndex());
}
@@ -6937,6 +6904,12 @@ MethodTableBuilder::NeedsNativeCodeSlot(bmtMDMethod * pMDMethod)
}
#endif
+#if defined(FEATURE_JIT_PITCHING)
+ if ((CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchEnabled) != 0) &&
+ (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitPitchMemThreshold) != 0))
+ return TRUE;
+#endif
+
return GetModule()->IsEditAndContinueEnabled();
}
@@ -8216,188 +8189,6 @@ void MethodTableBuilder::StoreEightByteClassification(SystemVStructRegisterPassi
#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
-#ifdef FEATURE_HFA
-//---------------------------------------------------------------------------------------
-//
-VOID
-MethodTableBuilder::CheckForHFA(MethodTable ** pByValueClassCache)
-{
- STANDARD_VM_CONTRACT;
-
- // This method should be called for valuetypes only
- _ASSERTE(IsValueClass());
-
- // No HFAs with explicit layout. There may be cases where explicit layout may be still
- // eligible for HFA, but it is hard to tell the real intent. Make it simple and just
- // unconditionally disable HFAs for explicit layout.
- if (HasExplicitFieldOffsetLayout())
- return;
-
- CorElementType hfaType = ELEMENT_TYPE_END;
-
- FieldDesc *pFieldDescList = GetHalfBakedClass()->GetFieldDescList();
- for (UINT i = 0; i < bmtEnumFields->dwNumInstanceFields; i++)
- {
- FieldDesc *pFD = &pFieldDescList[i];
- CorElementType fieldType = pFD->GetFieldType();
-
- switch (fieldType)
- {
- case ELEMENT_TYPE_VALUETYPE:
- fieldType = pByValueClassCache[i]->GetHFAType();
- break;
-
- case ELEMENT_TYPE_R4:
- case ELEMENT_TYPE_R8:
- break;
-
- default:
- // Not HFA
- return;
- }
-
- // Field type should be a valid HFA type.
- if (fieldType == ELEMENT_TYPE_END)
- {
- return;
- }
-
- // Initialize with a valid HFA type.
- if (hfaType == ELEMENT_TYPE_END)
- {
- hfaType = fieldType;
- }
- // All field types should be equal.
- else if (fieldType != hfaType)
- {
- return;
- }
- }
-
- if (hfaType == ELEMENT_TYPE_END)
- return;
-
- int elemSize = (hfaType == ELEMENT_TYPE_R8) ? sizeof(double) : sizeof(float);
-
- // Note that we check the total size, but do not perform any checks on number of fields:
- // - Type of fields can be HFA valuetype itself
- // - Managed C++ HFA valuetypes have just one <alignment member> of type float to signal that
- // the valuetype is HFA and explicitly specified size
-
- DWORD totalSize = bmtFP->NumInstanceFieldBytes;
-
- if (totalSize % elemSize != 0)
- return;
-
- // On ARM, HFAs can have a maximum of four fields regardless of whether those are float or double.
- if (totalSize / elemSize > 4)
- return;
-
- // All the above tests passed. It's HFA!
- GetHalfBakedMethodTable()->SetIsHFA();
-}
-
-//
-// The managed and unmanaged views of the types can differ for non-blitable types. This method
-// mirrors the HFA type computation for the unmanaged view.
-//
-void MethodTableBuilder::CheckForNativeHFA()
-{
- STANDARD_VM_CONTRACT;
-
- // No HFAs with inheritance
- if (!(IsValueClass() || (GetParentMethodTable() == g_pObjectClass)))
- return;
-
- // No HFAs with explicit layout. There may be cases where explicit layout may be still
- // eligible for HFA, but it is hard to tell the real intent. Make it simple and just
- // unconditionally disable HFAs for explicit layout.
- if (HasExplicitFieldOffsetLayout())
- return;
-
- const FieldMarshaler *pFieldMarshaler = GetLayoutInfo()->GetFieldMarshalers();
- UINT numReferenceFields = GetLayoutInfo()->GetNumCTMFields();
-
- CorElementType hfaType = ELEMENT_TYPE_END;
-
- while (numReferenceFields--)
- {
- CorElementType fieldType = ELEMENT_TYPE_END;
-
- switch (pFieldMarshaler->GetNStructFieldType())
- {
- case NFT_COPY4:
- case NFT_COPY8:
- fieldType = pFieldMarshaler->GetFieldDesc()->GetFieldType();
- if (fieldType != ELEMENT_TYPE_R4 && fieldType != ELEMENT_TYPE_R8)
- return;
- break;
-
- case NFT_NESTEDLAYOUTCLASS:
- fieldType = ((FieldMarshaler_NestedLayoutClass *)pFieldMarshaler)->GetMethodTable()->GetNativeHFAType();
- break;
-
- case NFT_NESTEDVALUECLASS:
- fieldType = ((FieldMarshaler_NestedValueClass *)pFieldMarshaler)->GetMethodTable()->GetNativeHFAType();
- break;
-
- case NFT_FIXEDARRAY:
- fieldType = ((FieldMarshaler_FixedArray *)pFieldMarshaler)->GetElementTypeHandle().GetMethodTable()->GetNativeHFAType();
- break;
-
- case NFT_DATE:
- fieldType = ELEMENT_TYPE_R8;
- break;
-
- default:
- // Not HFA
- return;
- }
-
- // Field type should be a valid HFA type.
- if (fieldType == ELEMENT_TYPE_END)
- {
- return;
- }
-
- // Initialize with a valid HFA type.
- if (hfaType == ELEMENT_TYPE_END)
- {
- hfaType = fieldType;
- }
- // All field types should be equal.
- else if (fieldType != hfaType)
- {
- return;
- }
-
- ((BYTE*&)pFieldMarshaler) += MAXFIELDMARSHALERSIZE;
- }
-
- if (hfaType == ELEMENT_TYPE_END)
- return;
-
- int elemSize = (hfaType == ELEMENT_TYPE_R8) ? sizeof(double) : sizeof(float);
-
- // Note that we check the total size, but do not perform any checks on number of fields:
- // - Type of fields can be HFA valuetype itself
- // - Managed C++ HFA valuetypes have just one <alignment member> of type float to signal that
- // the valuetype is HFA and explicitly specified size
-
- DWORD totalSize = GetHalfBakedClass()->GetNativeSize();
-
- if (totalSize % elemSize != 0)
- return;
-
- // On ARM, HFAs can have a maximum of four fields regardless of whether those are float or double.
- if (totalSize / elemSize > 4)
- return;
-
- // All the above tests passed. It's HFA!
- GetLayoutInfo()->SetNativeHFAType(hfaType);
-}
-#endif // FEATURE_HFA
-
//---------------------------------------------------------------------------------------
//
// make sure that no object fields are overlapped incorrectly and define the
@@ -8633,17 +8424,6 @@ MethodTableBuilder::HandleExplicitLayout(
IDS_CLASSLOAD_EXPLICIT_LAYOUT);
}
- if (!explicitClassTrust.IsVerifiable())
- {
- if (!Security::CanSkipVerification(GetAssembly()->GetDomainAssembly()))
- {
- ThrowFieldLayoutError(GetCl(),
- GetModule(),
- firstObjectOverlapOffset,
- IDS_CLASSLOAD_UNVERIFIABLE_FIELD_LAYOUT);
- }
- }
-
if (!explicitClassTrust.IsNonOverLayed())
{
SetHasOverLayedFields();
@@ -9006,7 +8786,7 @@ void MethodTableBuilder::CopyExactParentSlots(MethodTable *pMT, MethodTable *pAp
//
// Non-canonical method tables either share everything or nothing so it is sufficient to check
// just the first indirection to detect sharing.
- if (pMT->GetVtableIndirections()[0] != pCanonMT->GetVtableIndirections()[0])
+ if (pMT->GetVtableIndirections()[0].GetValueMaybeNull() != pCanonMT->GetVtableIndirections()[0].GetValueMaybeNull())
{
for (DWORD i = 0; i < nParentVirtuals; i++)
{
@@ -9033,7 +8813,7 @@ void MethodTableBuilder::CopyExactParentSlots(MethodTable *pMT, MethodTable *pAp
// We need to re-inherit this slot from the exact parent.
DWORD indirectionIndex = MethodTable::GetIndexOfVtableIndirection(i);
- if (pMT->GetVtableIndirections()[indirectionIndex] == pApproxParentMT->GetVtableIndirections()[indirectionIndex])
+ if (pMT->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull() == pApproxParentMT->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull())
{
// The slot lives in a chunk shared from the approximate parent MT
// If so, we need to change to share the chunk from the exact parent MT
@@ -9044,7 +8824,7 @@ void MethodTableBuilder::CopyExactParentSlots(MethodTable *pMT, MethodTable *pAp
_ASSERTE(MethodTable::CanShareVtableChunksFrom(pParentMT, pMT->GetLoaderModule()));
#endif
- pMT->GetVtableIndirections()[indirectionIndex] = pParentMT->GetVtableIndirections()[indirectionIndex];
+ pMT->GetVtableIndirections()[indirectionIndex].SetValueMaybeNull(pParentMT->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull());
i = MethodTable::GetEndSlotForVtableIndirection(indirectionIndex, nParentVirtuals) - 1;
continue;
@@ -9901,7 +9681,7 @@ MethodTable * MethodTableBuilder::AllocateNewMT(Module *pLoaderModule,
S_SIZE_T cbTotalSize = S_SIZE_T(dwGCSize) + S_SIZE_T(sizeof(MethodTable));
// vtable
- cbTotalSize += MethodTable::GetNumVtableIndirections(dwVirtuals) * sizeof(PTR_PCODE);
+ cbTotalSize += MethodTable::GetNumVtableIndirections(dwVirtuals) * sizeof(MethodTable::VTableIndir_t);
DWORD dwMultipurposeSlotsMask = 0;
@@ -9945,7 +9725,7 @@ MethodTable * MethodTableBuilder::AllocateNewMT(Module *pLoaderModule,
if (dwNumDicts != 0)
{
cbTotalSize += sizeof(GenericsDictInfo);
- cbTotalSize += S_SIZE_T(dwNumDicts) * S_SIZE_T(sizeof(TypeHandle*));
+ cbTotalSize += S_SIZE_T(dwNumDicts) * S_SIZE_T(sizeof(MethodTable::PerInstInfoElem_t));
cbTotalSize += cbInstAndDict;
}
@@ -10050,7 +9830,7 @@ MethodTable * MethodTableBuilder::AllocateNewMT(Module *pLoaderModule,
{
// Share the parent chunk
_ASSERTE(it.GetEndSlot() <= pMTParent->GetNumVirtuals());
- it.SetIndirectionSlot(pMTParent->GetVtableIndirections()[it.GetIndex()]);
+ it.SetIndirectionSlot(pMTParent->GetVtableIndirections()[it.GetIndex()].GetValueMaybeNull());
}
else
{
@@ -10098,19 +9878,20 @@ MethodTable * MethodTableBuilder::AllocateNewMT(Module *pLoaderModule,
// the dictionary pointers follow the interface map
if (dwNumDicts)
{
- Dictionary** pPerInstInfo = (Dictionary**)(pData + offsetOfInstAndDict.Value() + sizeof(GenericsDictInfo));
+ MethodTable::PerInstInfoElem_t *pPerInstInfo = (MethodTable::PerInstInfoElem_t *)(pData + offsetOfInstAndDict.Value() + sizeof(GenericsDictInfo));
pMT->SetPerInstInfo ( pPerInstInfo);
// Fill in the dictionary for this type, if it's instantiated
if (cbInstAndDict)
{
- *(pPerInstInfo + (dwNumDicts-1)) = (Dictionary*) (pPerInstInfo + dwNumDicts);
+ MethodTable::PerInstInfoElem_t *pPInstInfo = (MethodTable::PerInstInfoElem_t *)(pPerInstInfo + (dwNumDicts-1));
+ pPInstInfo->SetValueMaybeNull((Dictionary*) (pPerInstInfo + dwNumDicts));
}
}
#ifdef _DEBUG
- pMT->m_pWriteableData->m_dwLastVerifedGCCnt = (DWORD)-1;
+ pMT->m_pWriteableData.GetValue()->m_dwLastVerifedGCCnt = (DWORD)-1;
#endif // _DEBUG
RETURN(pMT);
@@ -10599,7 +10380,7 @@ MethodTableBuilder::SetupMethodTable2(
// with code:MethodDesc::SetStableEntryPointInterlocked.
//
DWORD indirectionIndex = MethodTable::GetIndexOfVtableIndirection(iCurSlot);
- if (GetParentMethodTable()->GetVtableIndirections()[indirectionIndex] != pMT->GetVtableIndirections()[indirectionIndex])
+ if (GetParentMethodTable()->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull() != pMT->GetVtableIndirections()[indirectionIndex].GetValueMaybeNull())
pMT->SetSlot(iCurSlot, pMD->GetMethodEntryPoint());
}
else
diff --git a/src/vm/methodtablebuilder.h b/src/vm/methodtablebuilder.h
index 8c838d37e6..fcb10a86fc 100644
--- a/src/vm/methodtablebuilder.h
+++ b/src/vm/methodtablebuilder.h
@@ -223,7 +223,6 @@ private:
BOOL IsDelegate() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->IsDelegate(); }
BOOL IsNested() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->IsNested(); }
BOOL HasFieldsWhichMustBeInited() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->HasFieldsWhichMustBeInited(); }
- BOOL HasRemotingProxyAttribute() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->HasRemotingProxyAttribute(); }
BOOL IsBlittable() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->IsBlittable(); }
PTR_MethodDescChunk GetChunks() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->GetChunks(); }
BOOL HasExplicitFieldOffsetLayout() { WRAPPER_NO_CONTRACT; return GetHalfBakedClass()->HasExplicitFieldOffsetLayout(); }
@@ -258,7 +257,6 @@ private:
void SetNumBoxedRegularStatics(WORD x) { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetNumBoxedRegularStatics(x); }
void SetNumBoxedThreadStatics(WORD x) { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetNumBoxedThreadStatics(x); }
void SetAlign8Candidate() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetAlign8Candidate(); }
- void SetHasRemotingProxyAttribute() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetHasRemotingProxyAttribute(); }
void SetHasOverLayedFields() { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetHasOverLayedFields(); }
void SetNonGCRegularStaticFieldBytes(DWORD x) { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetNonGCRegularStaticFieldBytes(x); }
void SetNonGCThreadStaticFieldBytes(DWORD x) { WRAPPER_NO_CONTRACT; GetHalfBakedClass()->SetNonGCThreadStaticFieldBytes(x); }
diff --git a/src/vm/mngstdinterfaces.cpp b/src/vm/mngstdinterfaces.cpp
index 5aafe8a97c..d6de4f32a0 100644
--- a/src/vm/mngstdinterfaces.cpp
+++ b/src/vm/mngstdinterfaces.cpp
@@ -22,7 +22,6 @@
#include "method.hpp"
#include "runtimecallablewrapper.h"
#include "excep.h"
-#include "security.h"
#include "typeparse.h"
//
@@ -217,9 +216,6 @@ LPVOID MngStdItfBase::ForwardCallToManagedView(
// The target isn't a TP so it better be a COM object.
_ASSERTE(Lr.Obj->GetMethodTable()->IsComObjectType());
- // We are about to call out to ummanaged code so we need to make a security check.
- Security::SpecialDemand(SSWT_DEMAND_FROM_NATIVE, SECURITY_UNMANAGED_CODE);
-
{
RCWHolder pRCW(GetThread());
RCWPROTECT_BEGIN(pRCW, Lr.Obj);
diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h
index a9574213af..45462b33dd 100644
--- a/src/vm/mscorlib.h
+++ b/src/vm/mscorlib.h
@@ -113,11 +113,6 @@ DEFINE_CLASS_U(System, AppDomainSetup, AppDomainSetupObjec
DEFINE_FIELD_U(_Entries, AppDomainSetupObject, m_Entries)
DEFINE_FIELD_U(_AppBase, AppDomainSetupObject, m_AppBase)
DEFINE_FIELD_U(_CompatFlags, AppDomainSetupObject, m_CompatFlags)
-DEFINE_FIELD_U(_TargetFrameworkName, AppDomainSetupObject, m_TargetFrameworkName)
-DEFINE_FIELD_U(_CheckedForTargetFrameworkName, AppDomainSetupObject, m_CheckedForTargetFrameworkName)
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
-DEFINE_FIELD_U(_UseRandomizedStringHashing, AppDomainSetupObject, m_UseRandomizedStringHashing)
-#endif
DEFINE_CLASS(ARG_ITERATOR, System, ArgIterator)
DEFINE_CLASS_U(System, ArgIterator, VARARGS) // Includes a SigPointer.
@@ -165,7 +160,9 @@ DEFINE_FIELD_U(_Minor, VersionBaseObject, m_Minor)
DEFINE_FIELD_U(_Build, VersionBaseObject, m_Build)
DEFINE_FIELD_U(_Revision, VersionBaseObject, m_Revision)
DEFINE_CLASS(VERSION, System, Version)
-DEFINE_METHOD(VERSION, CTOR, .ctor, IM_Int_Int_Int_Int_RetVoid)
+DEFINE_METHOD(VERSION, CTOR_Ix2, .ctor, IM_Int_Int_RetVoid)
+DEFINE_METHOD(VERSION, CTOR_Ix3, .ctor, IM_Int_Int_Int_RetVoid)
+DEFINE_METHOD(VERSION, CTOR_Ix4, .ctor, IM_Int_Int_Int_Int_RetVoid)
DEFINE_CLASS(ASSEMBLY_VERSION_COMPATIBILITY, Assemblies, AssemblyVersionCompatibility)
@@ -609,12 +606,7 @@ DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_CLOSED, CtorClosed,
DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_CLOSED_STATIC, CtorClosedStatic, IM_Obj_IntPtr_RetVoid)
DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_RT_CLOSED, CtorRTClosed, IM_Obj_IntPtr_RetVoid)
DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_OPENED, CtorOpened, IM_Obj_IntPtr_IntPtr_RetVoid)
-DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_SECURE_CLOSED, CtorSecureClosed, IM_Obj_IntPtr_IntPtr_IntPtr_RetVoid)
-DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_SECURE_CLOSED_STATIC,CtorSecureClosedStatic, IM_Obj_IntPtr_IntPtr_IntPtr_RetVoid)
-DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_SECURE_RT_CLOSED, CtorSecureRTClosed, IM_Obj_IntPtr_IntPtr_IntPtr_RetVoid)
-DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_SECURE_OPENED, CtorSecureOpened, IM_Obj_IntPtr_IntPtr_IntPtr_IntPtr_RetVoid)
DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_VIRTUAL_DISPATCH, CtorVirtualDispatch, IM_Obj_IntPtr_IntPtr_RetVoid)
-DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_SECURE_VIRTUAL_DISPATCH, CtorSecureVirtualDispatch, IM_Obj_IntPtr_IntPtr_IntPtr_IntPtr_RetVoid)
DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_COLLECTIBLE_CLOSED_STATIC, CtorCollectibleClosedStatic, IM_Obj_IntPtr_IntPtr_RetVoid)
DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_COLLECTIBLE_OPENED, CtorCollectibleOpened, IM_Obj_IntPtr_IntPtr_IntPtr_RetVoid)
DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_COLLECTIBLE_VIRTUAL_DISPATCH, CtorCollectibleVirtualDispatch, IM_Obj_IntPtr_IntPtr_IntPtr_RetVoid)
@@ -785,8 +777,10 @@ DEFINE_METHOD(JIT_HELPERS, GET_RAW_SZ_ARRAY_DATA, GetRawSzArrayData, N
DEFINE_CLASS(UNSAFE, CompilerServices, Unsafe)
DEFINE_METHOD(UNSAFE, AS_POINTER, AsPointer, NoSig)
DEFINE_METHOD(UNSAFE, SIZEOF, SizeOf, NoSig)
-DEFINE_METHOD(UNSAFE, BYREF_AS, As, NoSig)
-DEFINE_METHOD(UNSAFE, BYREF_ADD, Add, NoSig)
+DEFINE_METHOD(UNSAFE, BYREF_AS, As, GM_RefTFrom_RetRefTTo)
+DEFINE_METHOD(UNSAFE, OBJECT_AS, As, GM_Obj_RetT)
+DEFINE_METHOD(UNSAFE, BYREF_ADD, Add, GM_RefT_Int_RetRefT)
+DEFINE_METHOD(UNSAFE, PTR_ADD, Add, GM_PtrVoid_Int_RetPtrVoid)
DEFINE_METHOD(UNSAFE, BYREF_ADD_BYTE_OFFSET, AddByteOffset, NoSig)
DEFINE_METHOD(UNSAFE, BYREF_ARE_SAME, AreSame, NoSig)
DEFINE_METHOD(UNSAFE, BYREF_INIT_BLOCK_UNALIGNED, InitBlockUnaligned, NoSig)
@@ -807,7 +801,7 @@ DEFINE_FIELD(ARRAY_PINNING_HELPER, M_ARRAY_DATA, m_arrayData)
DEFINE_CLASS(RUNTIME_WRAPPED_EXCEPTION, CompilerServices, RuntimeWrappedException)
DEFINE_METHOD(RUNTIME_WRAPPED_EXCEPTION, OBJ_CTOR, .ctor, IM_Obj_RetVoid)
-DEFINE_FIELD(RUNTIME_WRAPPED_EXCEPTION, WRAPPED_EXCEPTION, m_wrappedException)
+DEFINE_FIELD(RUNTIME_WRAPPED_EXCEPTION, WRAPPED_EXCEPTION, _wrappedException)
DEFINE_CLASS_U(Interop, SafeHandle, SafeHandle)
DEFINE_FIELD_U(handle, SafeHandle, m_handle)
@@ -881,6 +875,7 @@ DEFINE_METHOD(STRING, CTORF_CHARARRAY_START_LEN,CtorCharArrayStart
DEFINE_METHOD(STRING, CTORF_CHAR_COUNT, CtorCharCount, IM_Char_Int_RetStr)
DEFINE_METHOD(STRING, CTORF_CHARPTR, CtorCharPtr, IM_PtrChar_RetStr)
DEFINE_METHOD(STRING, CTORF_CHARPTR_START_LEN,CtorCharPtrStartLength, IM_PtrChar_Int_Int_RetStr)
+DEFINE_METHOD(STRING, CTORF_READONLYSPANOFCHAR,CtorReadOnlySpanOfChar, IM_ReadOnlySpanOfChar_RetStr)
DEFINE_METHOD(STRING, INTERNAL_COPY, InternalCopy, SM_Str_IntPtr_Int_RetVoid)
DEFINE_METHOD(STRING, WCSLEN, wcslen, SM_PtrChar_RetInt)
DEFINE_PROPERTY(STRING, LENGTH, Length, Int)
@@ -971,6 +966,8 @@ DEFINE_CLASS(UNKNOWN_WRAPPER, Interop, UnknownWrapper)
#endif
DEFINE_CLASS(VALUE_TYPE, System, ValueType)
+DEFINE_METHOD(VALUE_TYPE, GET_HASH_CODE, GetHashCode, IM_RetInt)
+DEFINE_METHOD(VALUE_TYPE, EQUALS, Equals, IM_Obj_RetBool)
#ifdef FEATURE_COMINTEROP
DEFINE_CLASS(VARIANT_WRAPPER, Interop, VariantWrapper)
diff --git a/src/vm/multicorejit.cpp b/src/vm/multicorejit.cpp
index 4ad5447950..d35c3f7d9a 100644
--- a/src/vm/multicorejit.cpp
+++ b/src/vm/multicorejit.cpp
@@ -12,12 +12,10 @@
#include "common.h"
#include "vars.hpp"
-#include "security.h"
#include "eeconfig.h"
#include "dllimport.h"
#include "comdelegate.h"
#include "dbginterface.h"
-#include "listlock.inl"
#include "stubgen.h"
#include "eventtrace.h"
#include "array.h"
@@ -993,7 +991,7 @@ PCODE MulticoreJitRecorder::RequestMethodCode(MethodDesc * pMethod, MulticoreJit
PCODE pCode = NULL;
- pCode = pManager->GetMulticoreJitCodeStorage().QueryMethodCode(pMethod);
+ pCode = pManager->GetMulticoreJitCodeStorage().QueryMethodCode(pMethod, TRUE);
if ((pCode != NULL) && pManager->IsRecorderActive()) // recorder may be off when player is on (e.g. for Appx)
{
diff --git a/src/vm/multicorejit.h b/src/vm/multicorejit.h
index b7a0951ee1..047ba01a5f 100644
--- a/src/vm/multicorejit.h
+++ b/src/vm/multicorejit.h
@@ -103,7 +103,7 @@ public:
void StoreMethodCode(MethodDesc * pMethod, PCODE pCode);
- PCODE QueryMethodCode(MethodDesc * pMethod);
+ PCODE QueryMethodCode(MethodDesc * pMethod, BOOL shouldRemoveCode);
inline unsigned GetRemainingMethodCount() const
{
diff --git a/src/vm/multicorejitplayer.cpp b/src/vm/multicorejitplayer.cpp
index d7c2cec8a1..247fa0a14a 100644
--- a/src/vm/multicorejitplayer.cpp
+++ b/src/vm/multicorejitplayer.cpp
@@ -12,12 +12,10 @@
#include "common.h"
#include "vars.hpp"
-#include "security.h"
#include "eeconfig.h"
#include "dllimport.h"
#include "comdelegate.h"
#include "dbginterface.h"
-#include "listlock.inl"
#include "stubgen.h"
#include "eventtrace.h"
#include "array.h"
@@ -103,7 +101,7 @@ void MulticoreJitCodeStorage::StoreMethodCode(MethodDesc * pMD, PCODE pCode)
// Query from MakeJitWorker: Lookup stored JITted methods
-PCODE MulticoreJitCodeStorage::QueryMethodCode(MethodDesc * pMethod)
+PCODE MulticoreJitCodeStorage::QueryMethodCode(MethodDesc * pMethod, BOOL shouldRemoveCode)
{
STANDARD_VM_CONTRACT;
@@ -113,7 +111,7 @@ PCODE MulticoreJitCodeStorage::QueryMethodCode(MethodDesc * pMethod)
{
CrstHolder holder(& m_crstCodeMap);
- if (m_nativeCodeMap.Lookup(pMethod, & code))
+ if (m_nativeCodeMap.Lookup(pMethod, & code) && shouldRemoveCode)
{
m_nReturned ++;
@@ -507,6 +505,23 @@ HRESULT MulticoreJitProfilePlayer::HandleModuleRecord(const ModuleRecord * pMod)
}
+#ifndef DACCESS_COMPILE
+class MulticoreJitPrepareCodeConfig : public PrepareCodeConfig
+{
+public:
+ MulticoreJitPrepareCodeConfig(MethodDesc* pMethod) :
+ PrepareCodeConfig(NativeCodeVersion(pMethod), FALSE, FALSE)
+ {}
+
+ virtual BOOL SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse)
+ {
+ MulticoreJitManager & mcJitManager = GetAppDomain()->GetMulticoreJitManager();
+ mcJitManager.GetMulticoreJitCodeStorage().StoreMethodCode(GetMethodDesc(), pCode);
+ return TRUE;
+ }
+};
+#endif
+
// Call JIT to compile a method
bool MulticoreJitProfilePlayer::CompileMethodDesc(Module * pModule, MethodDesc * pMD)
@@ -529,8 +544,9 @@ bool MulticoreJitProfilePlayer::CompileMethodDesc(Module * pModule, MethodDesc *
// Reset the flag to allow managed code to be called in multicore JIT background thread from this routine
ThreadStateNCStackHolder holder(-1, Thread::TSNC_CallingManagedCodeDisabled);
- // MakeJitWorker calls back to MulticoreJitCodeStorage::StoreMethodCode under MethodDesc lock
- pMD->MakeJitWorker(& header, CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_MCJIT_BACKGROUND));
+ // PrepareCode calls back to MulticoreJitCodeStorage::StoreMethodCode under MethodDesc lock
+ MulticoreJitPrepareCodeConfig config(pMD);
+ pMD->PrepareCode(&config);
return true;
}
diff --git a/src/vm/object.h b/src/vm/object.h
index add704a2f7..20d7d50961 100644
--- a/src/vm/object.h
+++ b/src/vm/object.h
@@ -1898,12 +1898,6 @@ class AppDomainSetupObject : public Object
PTRARRAYREF m_Entries;
STRINGREF m_AppBase;
OBJECTREF m_CompatFlags;
- STRINGREF m_TargetFrameworkName;
- CLR_BOOL m_CheckedForTargetFrameworkName;
-#ifdef FEATURE_RANDOMIZED_STRING_HASHING
- CLR_BOOL m_UseRandomizedStringHashing;
-#endif
-
protected:
AppDomainSetupObject() { LIMITED_METHOD_CONTRACT; }
diff --git a/src/vm/olevariant.cpp b/src/vm/olevariant.cpp
index a15a1979e2..1ec6f70213 100644
--- a/src/vm/olevariant.cpp
+++ b/src/vm/olevariant.cpp
@@ -14,7 +14,6 @@
#include "excep.h"
#include "frames.h"
#include "vars.hpp"
-#include "security.h"
#include "olevariant.h"
#include "comdatetime.h"
#include "fieldmarshaler.h"
@@ -2563,12 +2562,6 @@ void OleVariant::MarshalRecordVariantOleToCom(VARIANT *pOleVariant,
if (!pValueClass)
COMPlusThrow(kArgumentException, IDS_EE_CANNOT_MAP_TO_MANAGED_VC);
- Module* pModule = pValueClass->GetModule();
- if (!Security::CanCallUnmanagedCode(pModule))
- {
- COMPlusThrow(kArgumentException, IDS_EE_VTRECORD_SECURITY);
- }
-
// Now that we have the value class, allocate an instance of the
// boxed value class and copy the contents of the record into it.
BoxedValueClass = AllocateObject(pValueClass);
@@ -2597,12 +2590,6 @@ void OleVariant::MarshalRecordVariantComToOle(VariantData *pComVariant,
GCPROTECT_BEGIN(BoxedValueClass)
{
_ASSERTE(BoxedValueClass != NULL);
- Module* pModule = BoxedValueClass->GetMethodTable()->GetModule();
- if (!Security::CanCallUnmanagedCode(pModule))
- {
- COMPlusThrow(kArgumentException, IDS_EE_VTRECORD_SECURITY);
- }
-
ConvertValueClassToVariant(&BoxedValueClass, pOleVariant);
}
GCPROTECT_END();
@@ -2633,12 +2620,6 @@ void OleVariant::MarshalRecordArrayOleToCom(void *oleArray, BASEARRAYREF *pComAr
}
CONTRACTL_END;
- Module* pModule = pElementMT->GetModule();
- if (!Security::CanCallUnmanagedCode(pModule))
- {
- COMPlusThrow(kArgumentException, IDS_EE_VTRECORD_SECURITY);
- }
-
if (pElementMT->IsBlittable())
{
// The array is blittable so we can simply copy it.
@@ -2671,12 +2652,6 @@ void OleVariant::MarshalRecordArrayComToOle(BASEARRAYREF *pComArray, void *oleAr
}
CONTRACTL_END;
- Module* pModule = pElementMT->GetModule();
- if (!Security::CanCallUnmanagedCode(pModule))
- {
- COMPlusThrow(kArgumentException, IDS_EE_VTRECORD_SECURITY);
- }
-
if (pElementMT->IsBlittable())
{
// The array is blittable so we can simply copy it.
diff --git a/src/vm/pefile.cpp b/src/vm/pefile.cpp
index 5d83ee97cb..306a52269c 100644
--- a/src/vm/pefile.cpp
+++ b/src/vm/pefile.cpp
@@ -17,7 +17,6 @@
#include "eeconfig.h"
#include "product_version.h"
#include "eventtrace.h"
-#include "security.h"
#include "corperm.h"
#include "dbginterface.h"
#include "peimagelayout.inl"
diff --git a/src/vm/perfmap.cpp b/src/vm/perfmap.cpp
index b664b72d7a..e6643fe772 100644
--- a/src/vm/perfmap.cpp
+++ b/src/vm/perfmap.cpp
@@ -27,6 +27,13 @@ void PerfMap::Initialize()
// Create the map.
s_Current = new PerfMap(currentPid);
+
+ int signalNum = (int) CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapIgnoreSignal);
+
+ if (signalNum > 0)
+ {
+ PAL_IgnoreProfileSignal(signalNum);
+ }
}
}
@@ -50,6 +57,8 @@ PerfMap::PerfMap(int pid)
// Initialize with no failures.
m_ErrorEncountered = false;
+ m_StubsMapped = 0;
+
// Build the path to the map file on disk.
WCHAR tempPath[MAX_LONGPATH+1];
if(!GetTempPathW(MAX_LONGPATH, tempPath))
@@ -76,6 +85,8 @@ PerfMap::PerfMap()
// Initialize with no failures.
m_ErrorEncountered = false;
+
+ m_StubsMapped = 0;
}
// Clean-up resources.
@@ -218,6 +229,38 @@ void PerfMap::LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t cod
}
}
+// Log a set of stub to the map.
+void PerfMap::LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, size_t codeSize)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (s_Current == nullptr || s_Current->m_FileStream == nullptr)
+ {
+ return;
+ }
+
+ // Logging failures should not cause any exceptions to flow upstream.
+ EX_TRY
+ {
+ if(!stubOwner)
+ {
+ stubOwner = "?";
+ }
+ if(!stubType)
+ {
+ stubOwner = "?";
+ }
+
+ // Build the map file line.
+ SString line;
+ line.Printf("%p %x stub<%d> %s<%s>\n", pCode, codeSize, ++(s_Current->m_StubsMapped), stubType, stubOwner);
+
+ // Write the line.
+ s_Current->WriteLine(line);
+ }
+ EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
+}
+
void PerfMap::GetNativeImageSignature(PEFile * pFile, WCHAR * pwszSig, unsigned int nSigSize)
{
CONTRACTL{
diff --git a/src/vm/perfmap.h b/src/vm/perfmap.h
index fe38ed3ad5..1f06bd4091 100644
--- a/src/vm/perfmap.h
+++ b/src/vm/perfmap.h
@@ -28,6 +28,9 @@ private:
// Set to true if an error is encountered when writing to the file.
bool m_ErrorEncountered;
+ // Set to true if an error is encountered when writing to the file.
+ unsigned m_StubsMapped;
+
// Construct a new map for the specified pid.
PerfMap(int pid);
@@ -64,6 +67,9 @@ public:
// Log a JIT compiled method to the map.
static void LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize);
+ // Log a set of stub to the map.
+ static void LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, size_t codeSize);
+
// Close the map and flush any remaining data.
static void Destroy();
};
diff --git a/src/vm/precode.cpp b/src/vm/precode.cpp
index 1daf6e32b8..8891d5a903 100644
--- a/src/vm/precode.cpp
+++ b/src/vm/precode.cpp
@@ -15,6 +15,10 @@
#include "compile.h"
#endif
+#ifdef FEATURE_PERFMAP
+#include "perfmap.h"
+#endif
+
//==========================================================================================
// class Precode
//==========================================================================================
@@ -556,6 +560,9 @@ TADDR Precode::AllocateTemporaryEntryPoints(MethodDescChunk * pChunk,
pMD = (MethodDesc *)(dac_cast<TADDR>(pMD) + pMD->SizeOf());
}
+#ifdef FEATURE_PERFMAP
+ PerfMap::LogStubs(__FUNCTION__, "PRECODE_FIXUP", (PCODE)temporaryEntryPoints, count * sizeof(FixupPrecode));
+#endif
ClrFlushInstructionCache((LPVOID)temporaryEntryPoints, count * sizeof(FixupPrecode));
return temporaryEntryPoints;
@@ -575,6 +582,10 @@ TADDR Precode::AllocateTemporaryEntryPoints(MethodDescChunk * pChunk,
pMD = (MethodDesc *)(dac_cast<TADDR>(pMD) + pMD->SizeOf());
}
+#ifdef FEATURE_PERFMAP
+ PerfMap::LogStubs(__FUNCTION__, "PRECODE_STUB", (PCODE)temporaryEntryPoints, count * oneSize);
+#endif
+
ClrFlushInstructionCache((LPVOID)temporaryEntryPoints, count * oneSize);
return temporaryEntryPoints;
diff --git a/src/vm/prestub.cpp b/src/vm/prestub.cpp
index 31a5670e00..f96135008d 100644
--- a/src/vm/prestub.cpp
+++ b/src/vm/prestub.cpp
@@ -13,12 +13,10 @@
#include "common.h"
#include "vars.hpp"
-#include "security.h"
#include "eeconfig.h"
#include "dllimport.h"
#include "comdelegate.h"
#include "dbginterface.h"
-#include "listlock.inl"
#include "stubgen.h"
#include "eventtrace.h"
#include "array.h"
@@ -52,15 +50,18 @@
#include "callcounter.h"
#endif
-#ifndef DACCESS_COMPILE
-
-EXTERN_C void STDCALL ThePreStub();
-
-#if defined(HAS_COMPACT_ENTRYPOINTS) && defined (_TARGET_ARM_)
+#if defined(FEATURE_GDBJIT)
+#include "gdbjit.h"
+#endif // FEATURE_GDBJIT
-EXTERN_C void STDCALL ThePreStubCompactARM();
+#ifndef DACCESS_COMPILE
-#endif // defined(HAS_COMPACT_ENTRYPOINTS) && defined (_TARGET_ARM_)
+#if defined(FEATURE_JIT_PITCHING)
+EXTERN_C void CheckStacksAndPitch();
+EXTERN_C void SavePitchingCandidate(MethodDesc* pMD, ULONG sizeOfCode);
+EXTERN_C void DeleteFromPitchingCandidate(MethodDesc* pMD);
+EXTERN_C void MarkMethodNotPitchingCandidate(MethodDesc* pMD);
+#endif
EXTERN_C void STDCALL ThePreStubPatch();
@@ -72,17 +73,11 @@ PCODE MethodDesc::DoBackpatch(MethodTable * pMT, MethodTable *pDispatchingMT, BO
{
STANDARD_VM_CHECK;
PRECONDITION(!ContainsGenericVariables());
-#ifndef FEATURE_INTERPRETER
PRECONDITION(HasStableEntryPoint());
-#endif // FEATURE_INTERPRETER
PRECONDITION(pMT == GetMethodTable());
}
CONTRACTL_END;
-#ifdef FEATURE_INTERPRETER
- PCODE pTarget = GetMethodEntryPoint();
-#else
PCODE pTarget = GetStableEntryPoint();
-#endif
if (!HasTemporaryEntryPoint())
return pTarget;
@@ -231,17 +226,13 @@ void DACNotifyCompilationFinished(MethodDesc *methodDesc)
_ASSERTE(modulePtr);
-#ifndef FEATURE_GDBJIT
// Are we listed?
USHORT jnt = jn.Requested((TADDR) modulePtr, t);
if (jnt & CLRDATA_METHNOTIFY_GENERATED)
{
// If so, throw an exception!
-#endif
DACNotify::DoJITNotification(methodDesc);
-#ifndef FEATURE_GDBJIT
}
-#endif
}
}
@@ -250,90 +241,350 @@ void DACNotifyCompilationFinished(MethodDesc *methodDesc)
#endif
// </TODO>
+PCODE MethodDesc::PrepareInitialCode()
+{
+ STANDARD_VM_CONTRACT;
+ PrepareCodeConfig config(NativeCodeVersion(this), TRUE, TRUE);
+ PCODE pCode = PrepareCode(&config);
-// ********************************************************************
-// README!!
-// ********************************************************************
+#if defined(FEATURE_GDBJIT) && defined(FEATURE_PAL) && !defined(CROSSGEN_COMPILE)
+ NotifyGdb::MethodPrepared(this);
+#endif
-// MakeJitWorker is the thread safe way to invoke the JIT compiler
-// If multiple threads get in here for the same pMD, ALL of them
-// MUST return the SAME value for pstub.
-//
-// This function creates a DeadlockAware list of methods being jitted
-// which prevents us from trying to JIT the same method more that once.
+ return pCode;
+}
+PCODE MethodDesc::PrepareCode(NativeCodeVersion codeVersion)
+{
+ STANDARD_VM_CONTRACT;
+
+#ifdef FEATURE_CODE_VERSIONING
+ if (codeVersion.IsDefaultVersion())
+ {
+#endif
+ // fast path
+ PrepareCodeConfig config(codeVersion, TRUE, TRUE);
+ return PrepareCode(&config);
+#ifdef FEATURE_CODE_VERSIONING
+ }
+ else
+ {
+ // a bit slower path (+1 usec?)
+ VersionedPrepareCodeConfig config;
+ {
+ CodeVersionManager::TableLockHolder lock(GetCodeVersionManager());
+ config = VersionedPrepareCodeConfig(codeVersion);
+ }
+ config.FinishConfiguration();
+ return PrepareCode(&config);
+ }
+#endif
+
+}
-PCODE MethodDesc::MakeJitWorker(COR_ILMETHOD_DECODER* ILHeader, CORJIT_FLAGS flags)
+PCODE MethodDesc::PrepareCode(PrepareCodeConfig* pConfig)
{
STANDARD_VM_CONTRACT;
- BOOL fIsILStub = IsILStub(); // @TODO: understand the need for this special case
+ // If other kinds of code need multi-versioning we could add more cases here,
+ // but for now generation of all other code/stubs occurs in other code paths
+ _ASSERTE(IsIL() || IsNoMetadata());
+ return PrepareILBasedCode(pConfig);
+}
- LOG((LF_JIT, LL_INFO1000000,
- "MakeJitWorker(" FMT_ADDR ", %s) for %s:%s\n",
- DBG_ADDR(this),
- fIsILStub ? " TRUE" : "FALSE",
- GetMethodTable()->GetDebugClassName(),
- m_pszDebugMethodName));
+PCODE MethodDesc::PrepareILBasedCode(PrepareCodeConfig* pConfig)
+{
+ STANDARD_VM_CONTRACT;
+ PCODE pCode = NULL;
+
+ if (pConfig->MayUsePrecompiledCode())
+ {
+ pCode = GetPrecompiledCode(pConfig);
+ }
+ if (pCode == NULL)
+ {
+ LOG((LF_CLASSLOADER, LL_INFO1000000,
+ " In PrepareILBasedCode, calling JitCompileCode\n"));
+ // Mark the code as hot in case the method ends up in the native image
+ g_IBCLogger.LogMethodCodeAccess(this);
+ pCode = JitCompileCode(pConfig);
+ }
+ return pCode;
+}
+
+PCODE MethodDesc::GetPrecompiledCode(PrepareCodeConfig* pConfig)
+{
+ STANDARD_VM_CONTRACT;
PCODE pCode = NULL;
- ULONG sizeOfCode = 0;
-#if defined(FEATURE_INTERPRETER) || defined(FEATURE_TIERED_COMPILATION)
- BOOL fStable = TRUE; // True iff the new code address (to be stored in pCode), is a stable entry point.
+
+#ifdef FEATURE_PREJIT
+ pCode = GetPrecompiledNgenCode();
#endif
-#ifdef FEATURE_INTERPRETER
- PCODE pPreviousInterpStub = NULL;
- BOOL fInterpreted = FALSE;
+
+#ifdef FEATURE_READYTORUN
+ if (pCode == NULL)
+ {
+ pCode = GetPrecompiledR2RCode();
+ if (pCode != NULL)
+ {
+ pConfig->SetNativeCode(pCode, &pCode);
+ }
+ }
+#endif // FEATURE_READYTORUN
+
+ return pCode;
+}
+
+PCODE MethodDesc::GetPrecompiledNgenCode()
+{
+ STANDARD_VM_CONTRACT;
+ PCODE pCode = NULL;
+
+#ifdef FEATURE_PREJIT
+ pCode = GetPreImplementedCode();
+
+#ifdef PROFILING_SUPPORTED
+
+ // The pre-existing cache search callbacks aren't implemented as you might expect.
+ // Instead of sending a cache search started for all methods, we only send the notification
+ // when we already know a pre-compiled version of the method exists. In the NGEN case we also
+ // don't send callbacks unless the method triggers the prestub which excludes a lot of methods.
+ // From the profiler's perspective this technique is only reliable/predictable when using profiler
+ // instrumented NGEN images (that virtually no profilers use). As-is the callback only
+ // gives an opportunity for the profiler to say whether or not it wants to use the ngen'ed
+ // code.
+ //
+ // Despite those oddities I am leaving this behavior as-is during refactoring because trying to
+ // improve it probably offers little value vs. the potential for compat issues and creating more
+ // complexity reasoning how the API behavior changed across runtime releases.
+ if (pCode != NULL)
+ {
+ BOOL fShouldSearchCache = TRUE;
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackCacheSearches());
+ g_profControlBlock.pProfInterface->JITCachedFunctionSearchStarted((FunctionID)this, &fShouldSearchCache);
+ END_PIN_PROFILER();
+ }
+
+ if (!fShouldSearchCache)
+ {
+ SetNativeCodeInterlocked(NULL, pCode);
+ _ASSERTE(!IsPreImplemented());
+ pCode = NULL;
+ }
+ }
+#endif // PROFILING_SUPPORTED
+
+ if (pCode != NULL)
+ {
+ LOG((LF_ZAP, LL_INFO10000,
+ "ZAP: Using code" FMT_ADDR "for %s.%s sig=\"%s\" (token %x).\n",
+ DBG_ADDR(pCode),
+ m_pszDebugClassName,
+ m_pszDebugMethodName,
+ m_pszDebugMethodSignature,
+ GetMemberDef()));
+
+ TADDR pFixupList = GetFixupList();
+ if (pFixupList != NULL)
+ {
+ Module *pZapModule = GetZapModule();
+ _ASSERTE(pZapModule != NULL);
+ if (!pZapModule->FixupDelayList(pFixupList))
+ {
+ _ASSERTE(!"FixupDelayList failed");
+ ThrowHR(COR_E_BADIMAGEFORMAT);
+ }
+ }
+
+#ifdef HAVE_GCCOVER
+ if (GCStress<cfg_instr_ngen>::IsEnabled())
+ SetupGcCoverage(this, (BYTE*)pCode);
+#endif // HAVE_GCCOVER
+
+#ifdef PROFILING_SUPPORTED
+ /*
+ * This notifies the profiler that a search to find a
+ * cached jitted function has been made.
+ */
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackCacheSearches());
+ g_profControlBlock.pProfInterface->
+ JITCachedFunctionSearchFinished((FunctionID)this, COR_PRF_CACHED_FUNCTION_FOUND);
+ END_PIN_PROFILER();
+ }
+#endif // PROFILING_SUPPORTED
+
+ }
+#endif // FEATURE_PREJIT
+
+ return pCode;
+}
+
+
+PCODE MethodDesc::GetPrecompiledR2RCode()
+{
+ STANDARD_VM_CONTRACT;
+
+ PCODE pCode = NULL;
+#ifdef FEATURE_READYTORUN
+ Module * pModule = GetModule();
+ if (pModule->IsReadyToRun())
+ {
+ pCode = pModule->GetReadyToRunInfo()->GetEntryPoint(this);
+ }
#endif
+ return pCode;
+}
+PCODE MethodDesc::GetMulticoreJitCode()
+{
+ STANDARD_VM_CONTRACT;
+
+ PCODE pCode = NULL;
#ifdef FEATURE_MULTICOREJIT
+ // Quick check before calling expensive out of line function on this method's domain has code JITted by background thread
MulticoreJitManager & mcJitManager = GetAppDomain()->GetMulticoreJitManager();
-
- bool fBackgroundThread = flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_MCJIT_BACKGROUND);
+ if (mcJitManager.GetMulticoreJitCodeStorage().GetRemainingMethodCount() > 0)
+ {
+ if (MulticoreJitManager::IsMethodSupported(this))
+ {
+ pCode = mcJitManager.RequestMethodCode(this); // Query multi-core JIT manager for compiled code
+ }
+ }
#endif
+ return pCode;
+}
- // If this is the first stage of a tiered compilation progression, use tier0, otherwise
- // use default compilation options
-#ifdef FEATURE_TIERED_COMPILATION
- if (!IsEligibleForTieredCompilation())
+COR_ILMETHOD_DECODER* MethodDesc::GetAndVerifyMetadataILHeader(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pDecoderMemory)
+{
+ STANDARD_VM_CONTRACT;
+
+ _ASSERTE(!IsNoMetadata());
+
+ COR_ILMETHOD_DECODER* pHeader = NULL;
+ COR_ILMETHOD* ilHeader = pConfig->GetILHeader();
+ if (ilHeader == NULL)
+ {
+#ifdef FEATURE_COMINTEROP
+ // Abstract methods can be called through WinRT derivation if the deriving type
+ // is not implemented in managed code, and calls through the CCW to the abstract
+ // method. Throw a sensible exception in that case.
+ if (GetMethodTable()->IsExportedToWinRT() && IsAbstract())
+ {
+ COMPlusThrowHR(E_NOTIMPL);
+ }
+#endif // FEATURE_COMINTEROP
+
+ COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL);
+ }
+
+ COR_ILMETHOD_DECODER::DecoderStatus status = COR_ILMETHOD_DECODER::FORMAT_ERROR;
+ {
+ // Decoder ctor can AV on a malformed method header
+ AVInRuntimeImplOkayHolder AVOkay;
+ pHeader = new (pDecoderMemory) COR_ILMETHOD_DECODER(ilHeader, GetMDImport(), &status);
+ }
+
+ if (status == COR_ILMETHOD_DECODER::FORMAT_ERROR)
+ {
+ COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL);
+ }
+
+#ifdef _VER_EE_VERIFICATION_ENABLED
+ static ConfigDWORD peVerify;
+
+ if (peVerify.val(CLRConfig::EXTERNAL_PEVerify))
+ m_pMethod->Verify(pHeader, TRUE, FALSE); // Throws a VerifierException if verification fails
+#endif // _VER_EE_VERIFICATION_ENABLED
+
+ return pHeader;
+}
+
+COR_ILMETHOD_DECODER* MethodDesc::GetAndVerifyNoMetadataILHeader()
+{
+ STANDARD_VM_CONTRACT;
+
+ if (IsILStub())
+ {
+ ILStubResolver* pResolver = AsDynamicMethodDesc()->GetILStubResolver();
+ return pResolver->GetILHeader();
+ }
+ else
+ {
+ return NULL;
+ }
+
+ // NoMetadata currently doesn't verify the IL. I'm not sure if that was
+ // a deliberate decision in the past or not, but I've left the behavior
+ // as-is during refactoring.
+}
+
+COR_ILMETHOD_DECODER* MethodDesc::GetAndVerifyILHeader(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pIlDecoderMemory)
+{
+ STANDARD_VM_CONTRACT;
+ _ASSERTE(IsIL() || IsNoMetadata());
+
+ if (IsNoMetadata())
{
- fStable = TRUE;
+ // The NoMetadata version already has a decoder to use, it doesn't need the stack allocated one
+ return GetAndVerifyNoMetadataILHeader();
}
else
{
- fStable = FALSE;
- flags.Add(CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_TIER0));
+ return GetAndVerifyMetadataILHeader(pConfig, pIlDecoderMemory);
}
+}
+
+// ********************************************************************
+// README!!
+// ********************************************************************
+
+// JitCompileCode is the thread safe way to invoke the JIT compiler
+// If multiple threads get in here for the same config, ALL of them
+// MUST return the SAME value for pcode.
+//
+// This function creates a DeadlockAware list of methods being jitted
+// which prevents us from trying to JIT the same method more that once.
+
+PCODE MethodDesc::JitCompileCode(PrepareCodeConfig* pConfig)
+{
+ STANDARD_VM_CONTRACT;
+
+ LOG((LF_JIT, LL_INFO1000000,
+ "JitCompileCode(" FMT_ADDR ", %s) for %s:%s\n",
+ DBG_ADDR(this),
+ IsILStub() ? " TRUE" : "FALSE",
+ GetMethodTable()->GetDebugClassName(),
+ m_pszDebugMethodName));
+
+#if defined(FEATURE_JIT_PITCHING)
+ CheckStacksAndPitch();
#endif
+ PCODE pCode = NULL;
{
// Enter the global lock which protects the list of all functions being JITd
- ListLockHolder pJitLock (GetDomain()->GetJitLock());
+ JitListLock::LockHolder pJitLock(GetDomain()->GetJitLock());
// It is possible that another thread stepped in before we entered the global lock for the first time.
- pCode = GetNativeCode();
- if (pCode != NULL)
+ if ((pCode = pConfig->IsJitCancellationRequested()))
{
-#ifdef FEATURE_INTERPRETER
- if (Interpreter::InterpretationStubToMethodInfo(pCode) == this)
- {
- pPreviousInterpStub = pCode;
- }
- else
-#endif // FEATURE_INTERPRETER
- goto Done;
+ return pCode;
}
const char *description = "jit lock";
INDEBUG(description = m_pszDebugMethodName;)
- ListLockEntryHolder pEntry(ListLockEntry::Find(pJitLock, this, description));
+ ReleaseHolder<JitListLockEntry> pEntry(JitListLockEntry::Find(
+ pJitLock, pConfig->GetCodeVersion(), description));
// We have an entry now, we can release the global lock
pJitLock.Release();
// Take the entry lock
{
- ListLockEntryLockHolder pEntryLock(pEntry, FALSE);
+ JitListLockEntry::LockHolder pEntryLock(pEntry, FALSE);
if (pEntryLock.DeadlockAwareAcquire())
{
@@ -374,313 +625,458 @@ PCODE MethodDesc::MakeJitWorker(COR_ILMETHOD_DECODER* ILHeader, CORJIT_FLAGS fla
}
// It is possible that another thread stepped in before we entered the lock.
- pCode = GetNativeCode();
-#ifdef FEATURE_INTERPRETER
- if (pCode != NULL && (pCode != pPreviousInterpStub))
-#else
- if (pCode != NULL)
-#endif // FEATURE_INTERPRETER
+ if ((pCode = pConfig->IsJitCancellationRequested()))
{
- goto Done;
+ return pCode;
}
- SString namespaceOrClassName, methodName, methodSignature;
-
- PCODE pOtherCode = NULL; // Need to move here due to 'goto GotNewCode'
-
-#ifdef FEATURE_MULTICOREJIT
-
- bool fCompiledInBackground = false;
-
- // If not called from multi-core JIT thread,
- if (! fBackgroundThread)
+ pCode = GetMulticoreJitCode();
+ if (pCode != NULL)
{
- // Quick check before calling expensive out of line function on this method's domain has code JITted by background thread
- if (mcJitManager.GetMulticoreJitCodeStorage().GetRemainingMethodCount() > 0)
- {
- if (MulticoreJitManager::IsMethodSupported(this))
- {
- pCode = mcJitManager.RequestMethodCode(this); // Query multi-core JIT manager for compiled code
-
- // Multicore JIT manager starts background thread to pre-compile methods, but it does not back-patch it/notify profiler/notify DAC,
- // Jumtp to GotNewCode to do so
- if (pCode != NULL)
- {
- fCompiledInBackground = true;
-
-#ifdef DEBUGGING_SUPPORTED
- // Notify the debugger of the jitted function
- if (g_pDebugInterface != NULL)
- {
- g_pDebugInterface->JITComplete(this, pCode);
- }
-#endif
-
- goto GotNewCode;
- }
- }
- }
+ pConfig->SetNativeCode(pCode, &pCode);
+ pEntry->m_hrResultCode = S_OK;
+ return pCode;
}
-#endif
-
- if (fIsILStub)
+ else
{
- // we race with other threads to JIT the code for an IL stub and the
- // IL header is released once one of the threads completes. As a result
- // we must be inside the lock to reliably get the IL header for the
- // stub.
-
- ILStubResolver* pResolver = AsDynamicMethodDesc()->GetILStubResolver();
- ILHeader = pResolver->GetILHeader();
+ return JitCompileCodeLockedEventWrapper(pConfig, pEntryLock);
}
+ }
+ }
+}
+
+PCODE MethodDesc::JitCompileCodeLockedEventWrapper(PrepareCodeConfig* pConfig, JitListLockEntry* pEntry)
+{
+ STANDARD_VM_CONTRACT;
+
+ PCODE pCode = NULL;
+ ULONG sizeOfCode = 0;
+ CORJIT_FLAGS flags;
#ifdef MDA_SUPPORTED
- MdaJitCompilationStart* pProbe = MDA_GET_ASSISTANT(JitCompilationStart);
- if (pProbe)
- pProbe->NowCompiling(this);
+ MdaJitCompilationStart* pProbe = MDA_GET_ASSISTANT(JitCompilationStart);
+ if (pProbe)
+ pProbe->NowCompiling(this);
#endif // MDA_SUPPORTED
#ifdef PROFILING_SUPPORTED
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackJITInfo());
+ // For methods with non-zero rejit id we send ReJITCompilationStarted, otherwise
+ // JITCompilationStarted. It isn't clear if this is the ideal policy for these
+ // notifications yet.
+ ReJITID rejitId = pConfig->GetCodeVersion().GetILCodeVersionId();
+ if (rejitId != 0)
+ {
+ g_profControlBlock.pProfInterface->ReJITCompilationStarted((FunctionID)this,
+ rejitId,
+ TRUE);
+ }
+ else
// If profiling, need to give a chance for a tool to examine and modify
// the IL before it gets to the JIT. This allows one to add probe calls for
// things like code coverage, performance, or whatever.
+ {
+ if (!IsNoMetadata())
{
- BEGIN_PIN_PROFILER(CORProfilerTrackJITInfo());
+ g_profControlBlock.pProfInterface->JITCompilationStarted((FunctionID)this, TRUE);
-#ifdef FEATURE_MULTICOREJIT
- // Multicore JIT should be disabled when CORProfilerTrackJITInfo is on
- // But there could be corner case in which profiler is attached when multicore background thread is calling MakeJitWorker
- // Disable this block when calling from multicore JIT background thread
- if (!fBackgroundThread)
-#endif
- {
- if (!IsNoMetadata())
- {
- g_profControlBlock.pProfInterface->JITCompilationStarted((FunctionID) this, TRUE);
- // The profiler may have changed the code on the callback. Need to
- // pick up the new code. Note that you have to be fully trusted in
- // this mode and the code will not be verified.
- COR_ILMETHOD *pilHeader = GetILHeader(TRUE);
- new (ILHeader) COR_ILMETHOD_DECODER(pilHeader, GetMDImport(), NULL);
- }
- else
- {
- unsigned int ilSize, unused;
- CorInfoOptions corOptions;
- LPCBYTE ilHeaderPointer = this->AsDynamicMethodDesc()->GetResolver()->GetCodeInfo(&ilSize, &unused, &corOptions, &unused);
-
- g_profControlBlock.pProfInterface->DynamicMethodJITCompilationStarted((FunctionID) this, TRUE, ilHeaderPointer, ilSize);
- }
- }
- END_PIN_PROFILER();
}
-#endif // PROFILING_SUPPORTED
-#ifdef FEATURE_INTERPRETER
- // We move the ETW event for start of JITting inward, after we make the decision
- // to JIT rather than interpret.
-#else // FEATURE_INTERPRETER
- // Fire an ETW event to mark the beginning of JIT'ing
- ETW::MethodLog::MethodJitting(this, &namespaceOrClassName, &methodName, &methodSignature);
-#endif // FEATURE_INTERPRETER
-
-#ifdef FEATURE_STACK_SAMPLING
-#ifdef FEATURE_MULTICOREJIT
- if (!fBackgroundThread)
-#endif // FEATURE_MULTICOREJIT
+ else
{
- StackSampler::RecordJittingInfo(this, flags);
- }
-#endif // FEATURE_STACK_SAMPLING
+ unsigned int ilSize, unused;
+ CorInfoOptions corOptions;
+ LPCBYTE ilHeaderPointer = this->AsDynamicMethodDesc()->GetResolver()->GetCodeInfo(&ilSize, &unused, &corOptions, &unused);
- EX_TRY
- {
- pCode = UnsafeJitFunction(this, ILHeader, flags, &sizeOfCode);
- }
- EX_CATCH
- {
- // If the current thread threw an exception, but a competing thread
- // somehow succeeded at JITting the same function (e.g., out of memory
- // encountered on current thread but not competing thread), then go ahead
- // and swallow this current thread's exception, since we somehow managed
- // to successfully JIT the code on the other thread.
- //
- // Note that if a deadlock cycle is broken, that does not result in an
- // exception--the thread would just pass through the lock and JIT the
- // function in competition with the other thread (with the winner of the
- // race decided later on when we do SetNativeCodeInterlocked). This
- // try/catch is purely to deal with the (unusual) case where a competing
- // thread succeeded where we aborted.
-
- pOtherCode = GetNativeCode();
-
- if (pOtherCode == NULL)
- {
- pEntry->m_hrResultCode = E_FAIL;
- EX_RETHROW;
- }
+ g_profControlBlock.pProfInterface->DynamicMethodJITCompilationStarted((FunctionID)this, TRUE, ilHeaderPointer, ilSize);
}
- EX_END_CATCH(RethrowTerminalExceptions)
+ }
+ END_PIN_PROFILER();
+ }
+#endif // PROFILING_SUPPORTED
- if (pOtherCode != NULL)
- {
- // Somebody finished jitting recursively while we were jitting the method.
- // Just use their method & leak the one we finished. (Normally we hope
- // not to finish our JIT in this case, as we will abort early if we notice
- // a reentrant jit has occurred. But we may not catch every place so we
- // do a definitive final check here.
- pCode = pOtherCode;
- goto Done;
- }
+ if (!ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
+ TRACE_LEVEL_VERBOSE,
+ CLR_JIT_KEYWORD))
+ {
+ pCode = JitCompileCodeLocked(pConfig, pEntry, &sizeOfCode, &flags);
+ }
+ else
+ {
+ SString namespaceOrClassName, methodName, methodSignature;
- _ASSERTE(pCode != NULL);
+ // Methods that may be interpreted defer this notification until it is certain
+ // we are jitting and not interpreting in CompileMethodWithEtwWrapper.
+ // Some further refactoring could consolidate the notification to always
+ // occur at the point the interpreter does it, but it might even better
+ // to fix the issues that cause us to avoid generating jit notifications
+ // for interpreted methods in the first place. The interpreter does generate
+ // a small stub of native code but no native-IL mapping.
+#ifndef FEATURE_INTERPRETER
+ ETW::MethodLog::MethodJitting(this,
+ &namespaceOrClassName,
+ &methodName,
+ &methodSignature);
+#endif
-#ifdef HAVE_GCCOVER
- if (GCStress<cfg_instr_jit>::IsEnabled())
- {
- SetupGcCoverage(this, (BYTE*) pCode);
- }
-#endif // HAVE_GCCOVER
+ pCode = JitCompileCodeLocked(pConfig, pEntry, &sizeOfCode, &flags);
+ // Interpretted methods skip this notification
#ifdef FEATURE_INTERPRETER
- // Determine whether the new code address is "stable"...= is not an interpreter stub.
- fInterpreted = (Interpreter::InterpretationStubToMethodInfo(pCode) == this);
- fStable = !fInterpreted;
-#endif // FEATURE_INTERPRETER
-
-#ifdef FEATURE_MULTICOREJIT
-
- // If called from multi-core JIT background thread, store code under lock, delay patching until code is queried from application threads
- if (fBackgroundThread)
- {
- // Fire an ETW event to mark the end of JIT'ing
- ETW::MethodLog::MethodJitted(this, &namespaceOrClassName, &methodName, &methodSignature, pCode, 0 /* ReJITID */);
-
-#ifdef FEATURE_PERFMAP
- // Save the JIT'd method information so that perf can resolve JIT'd call frames.
- PerfMap::LogJITCompiledMethod(this, pCode, sizeOfCode);
+ if (Interpreter::InterpretationStubToMethodInfo(pCode) == NULL)
#endif
-
- mcJitManager.GetMulticoreJitCodeStorage().StoreMethodCode(this, pCode);
-
- goto Done;
- }
+ {
+ // Fire an ETW event to mark the end of JIT'ing
+ ETW::MethodLog::MethodJitted(this,
+ &namespaceOrClassName,
+ &methodName,
+ &methodSignature,
+ pCode,
+ pConfig->GetCodeVersion().GetVersionId());
+ }
-GotNewCode:
-#endif
- // If this function had already been requested for rejit (before its original
- // code was jitted), then give the rejit manager a chance to jump-stamp the
- // code we just compiled so the first thread entering the function will jump
- // to the prestub and trigger the rejit. Note that the PublishMethodHolder takes
- // a lock to avoid a particular kind of rejit race. See
- // code:ReJitManager::PublishMethodHolder::PublishMethodHolder#PublishCode for
- // details on the rejit race.
- //
- // Aside from rejit, performing a SetNativeCodeInterlocked at this point
- // generally ensures that there is only one winning version of the native
- // code. This also avoid races with profiler overriding ngened code (see
- // matching SetNativeCodeInterlocked done after
- // JITCachedFunctionSearchStarted)
-#ifdef FEATURE_INTERPRETER
- PCODE pExpected = pPreviousInterpStub;
- if (pExpected == NULL) pExpected = GetTemporaryEntryPoint();
-#endif
- {
- ReJitPublishMethodHolder publishWorker(this, pCode);
- if (!SetNativeCodeInterlocked(pCode
-#ifdef FEATURE_INTERPRETER
- , pExpected, fStable
-#endif
- ))
- {
- // Another thread beat us to publishing its copy of the JITted code.
- pCode = GetNativeCode();
- goto Done;
- }
- }
+ }
-#ifdef FEATURE_INTERPRETER
- // State for dynamic methods cannot be freed if the method was ever interpreted,
- // since there is no way to ensure that it is not in use at the moment.
- if (IsDynamicMethod() && !fInterpreted && (pPreviousInterpStub == NULL))
- {
- AsDynamicMethodDesc()->GetResolver()->FreeCompileTimeState();
- }
-#endif // FEATURE_INTERPRETER
+#ifdef FEATURE_STACK_SAMPLING
+ StackSampler::RecordJittingInfo(this, flags);
+#endif // FEATURE_STACK_SAMPLING
- // We succeeded in jitting the code, and our jitted code is the one that's going to run now.
- pEntry->m_hrResultCode = S_OK;
+#ifdef PROFILING_SUPPORTED
+ {
+ BEGIN_PIN_PROFILER(CORProfilerTrackJITInfo());
+ // For methods with non-zero rejit id we send ReJITCompilationFinished, otherwise
+ // JITCompilationFinished. It isn't clear if this is the ideal policy for these
+ // notifications yet.
+ ReJITID rejitId = pConfig->GetCodeVersion().GetILCodeVersionId();
+ if (rejitId != 0)
+ {
- #ifdef PROFILING_SUPPORTED
+ g_profControlBlock.pProfInterface->ReJITCompilationFinished((FunctionID)this,
+ rejitId,
+ S_OK,
+ TRUE);
+ }
+ else
// Notify the profiler that JIT completed.
// Must do this after the address has been set.
// @ToDo: Why must we set the address before notifying the profiler ??
- // Note that if IsInterceptedForDeclSecurity is set no one should access the jitted code address anyway.
+ {
+ if (!IsNoMetadata())
{
- BEGIN_PIN_PROFILER(CORProfilerTrackJITInfo());
- if (!IsNoMetadata())
- {
- g_profControlBlock.pProfInterface->
- JITCompilationFinished((FunctionID) this,
- pEntry->m_hrResultCode,
- TRUE);
- }
- else
- {
- g_profControlBlock.pProfInterface->DynamicMethodJITCompilationFinished((FunctionID) this, pEntry->m_hrResultCode, TRUE);
- }
- END_PIN_PROFILER();
+ g_profControlBlock.pProfInterface->
+ JITCompilationFinished((FunctionID)this,
+ pEntry->m_hrResultCode,
+ TRUE);
+ }
+ else
+ {
+ g_profControlBlock.pProfInterface->DynamicMethodJITCompilationFinished((FunctionID)this, pEntry->m_hrResultCode, TRUE);
}
+ }
+ END_PIN_PROFILER();
+ }
#endif // PROFILING_SUPPORTED
-#ifdef FEATURE_MULTICOREJIT
- if (! fCompiledInBackground)
-#endif
#ifdef FEATURE_INTERPRETER
- // If we didn't JIT, but rather, created an interpreter stub (i.e., fStable is false), don't tell ETW that we did.
- if (fStable)
-#endif // FEATURE_INTERPRETER
- {
- // Fire an ETW event to mark the end of JIT'ing
- ETW::MethodLog::MethodJitted(this, &namespaceOrClassName, &methodName, &methodSignature, pCode, 0 /* ReJITID */);
+ bool isJittedMethod = (Interpreter::InterpretationStubToMethodInfo(pCode) == NULL);
+#endif
+ // Interpretted methods skip this notification
+#ifdef FEATURE_INTERPRETER
+ if (isJittedMethod)
+#endif
+ {
#ifdef FEATURE_PERFMAP
- // Save the JIT'd method information so that perf can resolve JIT'd call frames.
- PerfMap::LogJITCompiledMethod(this, pCode, sizeOfCode);
+ // Save the JIT'd method information so that perf can resolve JIT'd call frames.
+ PerfMap::LogJITCompiledMethod(this, pCode, sizeOfCode);
#endif
- }
-
+ }
-#ifdef FEATURE_MULTICOREJIT
- // If not called from multi-core JIT thread, not got code from storage, quick check before calling out of line function
- if (! fBackgroundThread && ! fCompiledInBackground && mcJitManager.IsRecorderActive())
+#ifdef FEATURE_MULTICOREJIT
+ // Non-initial code versions and multicore jit initial compilation all skip this
+ if (pConfig->NeedsMulticoreJitNotification())
+ {
+ MulticoreJitManager & mcJitManager = GetAppDomain()->GetMulticoreJitManager();
+ if (mcJitManager.IsRecorderActive())
+ {
+ if (MulticoreJitManager::IsMethodSupported(this))
{
- if (MulticoreJitManager::IsMethodSupported(this))
- {
- mcJitManager.RecordMethodJit(this); // Tell multi-core JIT manager to record method on successful JITting
- }
+ mcJitManager.RecordMethodJit(this); // Tell multi-core JIT manager to record method on successful JITting
}
+ }
+ }
#endif
- if (!fIsILStub)
- {
- // The notification will only occur if someone has registered for this method.
- DACNotifyCompilationFinished(this);
- }
+#ifdef FEATURE_INTERPRETER
+ if (isJittedMethod)
+#endif
+ {
+ // The notification will only occur if someone has registered for this method.
+ DACNotifyCompilationFinished(this);
+ }
+
+ return pCode;
+}
+
+PCODE MethodDesc::JitCompileCodeLocked(PrepareCodeConfig* pConfig, JitListLockEntry* pEntry, ULONG* pSizeOfCode, CORJIT_FLAGS* pFlags)
+{
+ STANDARD_VM_CONTRACT;
+
+ PCODE pCode = NULL;
+
+ // The profiler may have changed the code on the callback. Need to
+ // pick up the new code.
+ COR_ILMETHOD_DECODER ilDecoderTemp;
+ COR_ILMETHOD_DECODER *pilHeader = GetAndVerifyILHeader(pConfig, &ilDecoderTemp);
+ *pFlags = pConfig->GetJitCompilationFlags();
+ PCODE pOtherCode = NULL;
+ EX_TRY
+ {
+ pCode = UnsafeJitFunction(this, pilHeader, *pFlags, pSizeOfCode);
+ }
+ EX_CATCH
+ {
+ // If the current thread threw an exception, but a competing thread
+ // somehow succeeded at JITting the same function (e.g., out of memory
+ // encountered on current thread but not competing thread), then go ahead
+ // and swallow this current thread's exception, since we somehow managed
+ // to successfully JIT the code on the other thread.
+ //
+ // Note that if a deadlock cycle is broken, that does not result in an
+ // exception--the thread would just pass through the lock and JIT the
+ // function in competition with the other thread (with the winner of the
+ // race decided later on when we do SetNativeCodeInterlocked). This
+ // try/catch is purely to deal with the (unusual) case where a competing
+ // thread succeeded where we aborted.
+
+ if (!(pOtherCode = pConfig->IsJitCancellationRequested()))
+ {
+ pEntry->m_hrResultCode = E_FAIL;
+ EX_RETHROW;
}
}
+ EX_END_CATCH(RethrowTerminalExceptions)
-Done:
+ if (pOtherCode != NULL)
+ {
+ // Somebody finished jitting recursively while we were jitting the method.
+ // Just use their method & leak the one we finished. (Normally we hope
+ // not to finish our JIT in this case, as we will abort early if we notice
+ // a reentrant jit has occurred. But we may not catch every place so we
+ // do a definitive final check here.
+ return pOtherCode;
+ }
- // We must have a code by now.
_ASSERTE(pCode != NULL);
+
+ // Aside from rejit, performing a SetNativeCodeInterlocked at this point
+ // generally ensures that there is only one winning version of the native
+ // code. This also avoid races with profiler overriding ngened code (see
+ // matching SetNativeCodeInterlocked done after
+ // JITCachedFunctionSearchStarted)
+ {
+ if (!pConfig->SetNativeCode(pCode, &pOtherCode))
+ {
+ // Another thread beat us to publishing its copy of the JITted code.
+ return pOtherCode;
+ }
+#if defined(FEATURE_JIT_PITCHING)
+ else
+ {
+ SavePitchingCandidate(this, sizeOfCode);
+ }
+#endif
+ }
+
+#ifdef HAVE_GCCOVER
+ if (GCStress<cfg_instr_jit>::IsEnabled())
+ {
+ SetupGcCoverage(this, (BYTE*)pCode);
+ }
+#endif // HAVE_GCCOVER
- LOG((LF_CORDB, LL_EVERYTHING, "MethodDesc::MakeJitWorker finished. Stub is" FMT_ADDR "\n",
- DBG_ADDR(pCode)));
+ // We succeeded in jitting the code, and our jitted code is the one that's going to run now.
+ pEntry->m_hrResultCode = S_OK;
return pCode;
}
+
+
+PrepareCodeConfig::PrepareCodeConfig() {}
+
+PrepareCodeConfig::PrepareCodeConfig(NativeCodeVersion codeVersion, BOOL needsMulticoreJitNotification, BOOL mayUsePrecompiledCode) :
+ m_pMethodDesc(codeVersion.GetMethodDesc()),
+ m_nativeCodeVersion(codeVersion),
+ m_needsMulticoreJitNotification(needsMulticoreJitNotification),
+ m_mayUsePrecompiledCode(mayUsePrecompiledCode)
+{}
+
+MethodDesc* PrepareCodeConfig::GetMethodDesc()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_pMethodDesc;
+}
+
+PCODE PrepareCodeConfig::IsJitCancellationRequested()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_pMethodDesc->GetNativeCode();
+}
+
+BOOL PrepareCodeConfig::NeedsMulticoreJitNotification()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_needsMulticoreJitNotification;
+}
+
+NativeCodeVersion PrepareCodeConfig::GetCodeVersion()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_nativeCodeVersion;
+}
+
+BOOL PrepareCodeConfig::SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // If this function had already been requested for rejit (before its original
+ // code was jitted), then give the CodeVersionManager a chance to jump-stamp the
+ // code we just compiled so the first thread entering the function will jump
+ // to the prestub and trigger the rejit. Note that the PublishMethodHolder takes
+ // a lock to avoid a particular kind of rejit race. See
+ // code:CodeVersionManager::PublishMethodHolder::PublishMethodHolder#PublishCode for
+ // details on the rejit race.
+ //
+ if (m_pMethodDesc->IsVersionableWithJumpStamp())
+ {
+ PublishMethodHolder publishWorker(GetMethodDesc(), pCode);
+ if (m_pMethodDesc->SetNativeCodeInterlocked(pCode, NULL))
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ if (m_pMethodDesc->SetNativeCodeInterlocked(pCode, NULL))
+ {
+ return TRUE;
+ }
+ }
+
+ *ppAlternateCodeToUse = m_pMethodDesc->GetNativeCode();
+ return FALSE;
+}
+
+COR_ILMETHOD* PrepareCodeConfig::GetILHeader()
+{
+ STANDARD_VM_CONTRACT;
+ return m_pMethodDesc->GetILHeader(TRUE);
+}
+
+CORJIT_FLAGS PrepareCodeConfig::GetJitCompilationFlags()
+{
+ STANDARD_VM_CONTRACT;
+
+ CORJIT_FLAGS flags;
+ if (m_pMethodDesc->IsILStub())
+ {
+ ILStubResolver* pResolver = m_pMethodDesc->AsDynamicMethodDesc()->GetILStubResolver();
+ flags = pResolver->GetJitFlags();
+ }
+#ifdef FEATURE_TIERED_COMPILATION
+ flags.Add(TieredCompilationManager::GetJitFlags(m_nativeCodeVersion));
+#endif
+ return flags;
+}
+
+BOOL PrepareCodeConfig::MayUsePrecompiledCode()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_mayUsePrecompiledCode;
+}
+
+#ifdef FEATURE_CODE_VERSIONING
+VersionedPrepareCodeConfig::VersionedPrepareCodeConfig() {}
+
+VersionedPrepareCodeConfig::VersionedPrepareCodeConfig(NativeCodeVersion codeVersion) :
+ PrepareCodeConfig(codeVersion, TRUE, FALSE)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(!m_nativeCodeVersion.IsDefaultVersion());
+ _ASSERTE(m_pMethodDesc->GetCodeVersionManager()->LockOwnedByCurrentThread());
+ m_ilCodeVersion = m_nativeCodeVersion.GetILCodeVersion();
+}
+
+HRESULT VersionedPrepareCodeConfig::FinishConfiguration()
+{
+ STANDARD_VM_CONTRACT;
+
+ _ASSERTE(!GetMethodDesc()->GetCodeVersionManager()->LockOwnedByCurrentThread());
+
+ // Any code build stages that do just in time configuration should
+ // be configured now
+#ifdef FEATURE_REJIT
+ if (m_ilCodeVersion.GetRejitState() != ILCodeVersion::kStateActive)
+ {
+ ReJitManager::ConfigureILCodeVersion(m_ilCodeVersion);
+ }
+ _ASSERTE(m_ilCodeVersion.GetRejitState() == ILCodeVersion::kStateActive);
+#endif
+
+ return S_OK;
+}
+
+PCODE VersionedPrepareCodeConfig::IsJitCancellationRequested()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_nativeCodeVersion.GetNativeCode();
+}
+
+BOOL VersionedPrepareCodeConfig::SetNativeCode(PCODE pCode, PCODE * ppAlternateCodeToUse)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ //This isn't the default version so jumpstamp is never needed
+ _ASSERTE(!m_nativeCodeVersion.IsDefaultVersion());
+ if (m_nativeCodeVersion.SetNativeCodeInterlocked(pCode, NULL))
+ {
+ return TRUE;
+ }
+ else
+ {
+ *ppAlternateCodeToUse = m_nativeCodeVersion.GetNativeCode();
+ return FALSE;
+ }
+}
+
+COR_ILMETHOD* VersionedPrepareCodeConfig::GetILHeader()
+{
+ STANDARD_VM_CONTRACT;
+ return m_ilCodeVersion.GetIL();
+}
+
+CORJIT_FLAGS VersionedPrepareCodeConfig::GetJitCompilationFlags()
+{
+ STANDARD_VM_CONTRACT;
+ CORJIT_FLAGS flags;
+
+#ifdef FEATURE_REJIT
+ DWORD profilerFlags = m_ilCodeVersion.GetJitFlags();
+ flags.Add(ReJitManager::JitFlagsFromProfCodegenFlags(profilerFlags));
+#endif
+
+#ifdef FEATURE_TIERED_COMPILATION
+ flags.Add(TieredCompilationManager::GetJitFlags(m_nativeCodeVersion));
+#endif
+
+ return flags;
+}
+
+#endif //FEATURE_CODE_VERSIONING
+
#ifdef FEATURE_STUBS_AS_IL
// CreateInstantiatingILStubTargetSig:
@@ -1264,21 +1660,6 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
GCStress<cfg_any, EeconfigFastGcSPolicy, CoopGcModePolicy>::MaybeTrigger();
- // Are we in the prestub because of a rejit request? If so, let the ReJitManager
- // take it from here.
- pCode = ReJitManager::DoReJitIfNecessary(this);
- if (pCode != NULL)
- {
- // A ReJIT was performed, so nothing left for DoPrestub() to do. Return now.
- //
- // The stable entrypoint will either be a pointer to the original JITted code
- // (with a jmp at the top to jump to the newly-rejitted code) OR a pointer to any
- // stub code that must be executed first (e.g., a remoting stub), which in turn
- // will call the original JITted code (which then jmps to the newly-rejitted
- // code).
- RETURN GetStableEntryPoint();
- }
-
#ifdef FEATURE_COMINTEROP
/************************** INTEROP *************************/
@@ -1317,40 +1698,54 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
pMT->CheckRunClassInitThrowing();
}
- /************************** BACKPATCHING *************************/
- // See if the addr of code has changed from the pre-stub
-#ifdef FEATURE_INTERPRETER
- if (!IsReallyPointingToPrestub())
-#else
- if (!IsPointingToPrestub())
+
+ /*************************** CALL COUNTER ***********************/
+ // If we are counting calls for tiered compilation, leave the prestub
+ // in place so that we can continue intercepting method invocations.
+ // When the TieredCompilationManager has received enough call notifications
+ // for this method only then do we back-patch it.
+ BOOL fCanBackpatchPrestub = TRUE;
+#ifdef FEATURE_TIERED_COMPILATION
+ BOOL fEligibleForTieredCompilation = IsEligibleForTieredCompilation();
+ if (fEligibleForTieredCompilation)
+ {
+ CallCounter * pCallCounter = GetCallCounter();
+ fCanBackpatchPrestub = pCallCounter->OnMethodCalled(this);
+ }
#endif
+
+ /*************************** VERSIONABLE CODE *********************/
+
+ BOOL fIsPointingToPrestub = IsPointingToPrestub();
+#ifdef FEATURE_CODE_VERSIONING
+ if (IsVersionableWithPrecode() ||
+ (!fIsPointingToPrestub && IsVersionableWithJumpStamp()))
{
- // If we are counting calls for tiered compilation, leave the prestub
- // in place so that we can continue intercepting method invocations.
- // When the TieredCompilationManager has received enough call notifications
- // for this method only then do we back-patch it.
-#ifdef FEATURE_TIERED_COMPILATION
- PCODE pNativeCode = GetNativeCode();
- if (pNativeCode && IsEligibleForTieredCompilation())
- {
- CallCounter * pCallCounter = GetAppDomain()->GetCallCounter();
- BOOL doBackPatch = pCallCounter->OnMethodCalled(this);
- if (!doBackPatch)
- {
- return pNativeCode;
- }
- }
+ pCode = GetCodeVersionManager()->PublishVersionableCodeIfNecessary(this, fCanBackpatchPrestub);
+ fIsPointingToPrestub = IsPointingToPrestub();
+ }
#endif
+
+ /************************** BACKPATCHING *************************/
+ // See if the addr of code has changed from the pre-stub
+ if (!fIsPointingToPrestub)
+ {
LOG((LF_CLASSLOADER, LL_INFO10000,
" In PreStubWorker, method already jitted, backpatching call point\n"));
-
+#if defined(FEATURE_JIT_PITCHING)
+ MarkMethodNotPitchingCandidate(this);
+#endif
RETURN DoBackpatch(pMT, pDispatchingMT, TRUE);
}
-
- // record if remoting needs to intercept this call
- BOOL fRemotingIntercepted = IsRemotingInterceptedViaPrestub();
-
- BOOL fReportCompilationFinished = FALSE;
+
+ if (pCode)
+ {
+ // The only reason we are still pointing to prestub is because the call counter
+ // prevented it. We should still short circuit and return the code without
+ // backpatching.
+ _ASSERTE(!fCanBackpatchPrestub);
+ RETURN pCode;
+ }
/************************** CODE CREATION *************************/
if (IsUnboxingStub())
@@ -1365,209 +1760,11 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
#endif // defined(FEATURE_SHARE_GENERIC_CODE)
else if (IsIL() || IsNoMetadata())
{
- // remember if we need to backpatch the MethodTable slot
- BOOL fBackpatch = !fRemotingIntercepted
- && IsNativeCodeStableAfterInit();
-
-#ifdef FEATURE_PREJIT
- //
- // See if we have any prejitted code to use.
- //
-
- pCode = GetPreImplementedCode();
-
-#ifdef PROFILING_SUPPORTED
- if (pCode != NULL)
- {
- BOOL fShouldSearchCache = TRUE;
-
- {
- BEGIN_PIN_PROFILER(CORProfilerTrackCacheSearches());
- g_profControlBlock.pProfInterface->
- JITCachedFunctionSearchStarted((FunctionID) this,
- &fShouldSearchCache);
- END_PIN_PROFILER();
- }
-
- if (!fShouldSearchCache)
- {
-#ifdef FEATURE_INTERPRETER
- SetNativeCodeInterlocked(NULL, pCode, FALSE);
-#else
- SetNativeCodeInterlocked(NULL, pCode);
-#endif
- _ASSERTE(!IsPreImplemented());
- pCode = NULL;
- }
- }
-#endif // PROFILING_SUPPORTED
-
- if (pCode != NULL)
+ if (!IsNativeCodeStableAfterInit())
{
- LOG((LF_ZAP, LL_INFO10000,
- "ZAP: Using code" FMT_ADDR "for %s.%s sig=\"%s\" (token %x).\n",
- DBG_ADDR(pCode),
- m_pszDebugClassName,
- m_pszDebugMethodName,
- m_pszDebugMethodSignature,
- GetMemberDef()));
-
- TADDR pFixupList = GetFixupList();
- if (pFixupList != NULL)
- {
- Module *pZapModule = GetZapModule();
- _ASSERTE(pZapModule != NULL);
- if (!pZapModule->FixupDelayList(pFixupList))
- {
- _ASSERTE(!"FixupDelayList failed");
- ThrowHR(COR_E_BADIMAGEFORMAT);
- }
- }
-
-#ifdef HAVE_GCCOVER
- if (GCStress<cfg_instr_ngen>::IsEnabled())
- SetupGcCoverage(this, (BYTE*) pCode);
-#endif // HAVE_GCCOVER
-
-#ifdef PROFILING_SUPPORTED
- /*
- * This notifies the profiler that a search to find a
- * cached jitted function has been made.
- */
- {
- BEGIN_PIN_PROFILER(CORProfilerTrackCacheSearches());
- g_profControlBlock.pProfInterface->
- JITCachedFunctionSearchFinished((FunctionID) this, COR_PRF_CACHED_FUNCTION_FOUND);
- END_PIN_PROFILER();
- }
-#endif // PROFILING_SUPPORTED
- }
-
- //
- // If not, try to jit it
- //
-
-#endif // FEATURE_PREJIT
-
-#ifdef FEATURE_READYTORUN
- if (pCode == NULL)
- {
- Module * pModule = GetModule();
- if (pModule->IsReadyToRun())
- {
- pCode = pModule->GetReadyToRunInfo()->GetEntryPoint(this);
- if (pCode != NULL)
- fReportCompilationFinished = TRUE;
- }
+ GetOrCreatePrecode();
}
-#endif // FEATURE_READYTORUN
-
- if (pCode == NULL)
- {
- NewHolder<COR_ILMETHOD_DECODER> pHeader(NULL);
- // Get the information on the method
- if (!IsNoMetadata())
- {
- COR_ILMETHOD* ilHeader = GetILHeader(TRUE);
- if(ilHeader == NULL)
- {
-#ifdef FEATURE_COMINTEROP
- // Abstract methods can be called through WinRT derivation if the deriving type
- // is not implemented in managed code, and calls through the CCW to the abstract
- // method. Throw a sensible exception in that case.
- if (pMT->IsExportedToWinRT() && IsAbstract())
- {
- COMPlusThrowHR(E_NOTIMPL);
- }
-#endif // FEATURE_COMINTEROP
-
- COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL);
- }
-
- COR_ILMETHOD_DECODER::DecoderStatus status = COR_ILMETHOD_DECODER::FORMAT_ERROR;
-
- {
- // Decoder ctor can AV on a malformed method header
- AVInRuntimeImplOkayHolder AVOkay;
- pHeader = new COR_ILMETHOD_DECODER(ilHeader, GetMDImport(), &status);
- if(pHeader == NULL)
- status = COR_ILMETHOD_DECODER::FORMAT_ERROR;
- }
-
- if (status == COR_ILMETHOD_DECODER::VERIFICATION_ERROR &&
- Security::CanSkipVerification(GetModule()->GetDomainAssembly()))
- {
- status = COR_ILMETHOD_DECODER::SUCCESS;
- }
-
- if (status != COR_ILMETHOD_DECODER::SUCCESS)
- {
- if (status == COR_ILMETHOD_DECODER::VERIFICATION_ERROR)
- {
- // Throw a verification HR
- COMPlusThrowHR(COR_E_VERIFICATION);
- }
- else
- {
- COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL);
- }
- }
-
-#ifdef _VER_EE_VERIFICATION_ENABLED
- static ConfigDWORD peVerify;
-
- if (peVerify.val(CLRConfig::EXTERNAL_PEVerify))
- Verify(pHeader, TRUE, FALSE); // Throws a VerifierException if verification fails
-#endif // _VER_EE_VERIFICATION_ENABLED
- } // end if (!IsNoMetadata())
-
- // JIT it
- LOG((LF_CLASSLOADER, LL_INFO1000000,
- " In PreStubWorker, calling MakeJitWorker\n"));
-
- // Create the precode eagerly if it is going to be needed later.
- if (!fBackpatch)
- {
- GetOrCreatePrecode();
- }
-
- // Mark the code as hot in case the method ends up in the native image
- g_IBCLogger.LogMethodCodeAccess(this);
-
- pCode = MakeJitWorker(pHeader, CORJIT_FLAGS());
-
-#ifdef FEATURE_INTERPRETER
- if ((pCode != NULL) && !HasStableEntryPoint())
- {
- // We don't yet have a stable entry point, so don't do backpatching yet.
- // But we do have to handle some extra cases that occur in backpatching.
- // (Perhaps I *should* get to the backpatching code, but in a mode where we know
- // we're not dealing with the stable entry point...)
- if (HasNativeCodeSlot())
- {
- // We called "SetNativeCodeInterlocked" in MakeJitWorker, which updated the native
- // code slot, but I think we also want to update the regular slot...
- PCODE tmpEntry = GetTemporaryEntryPoint();
- PCODE pFound = FastInterlockCompareExchangePointer(GetAddrOfSlot(), pCode, tmpEntry);
- // Doesn't matter if we failed -- if we did, it's because somebody else made progress.
- if (pFound != tmpEntry) pCode = pFound;
- }
-
- // Now we handle the case of a FuncPtrPrecode.
- FuncPtrStubs * pFuncPtrStubs = GetLoaderAllocator()->GetFuncPtrStubsNoCreate();
- if (pFuncPtrStubs != NULL)
- {
- Precode* pFuncPtrPrecode = pFuncPtrStubs->Lookup(this);
- if (pFuncPtrPrecode != NULL)
- {
- // If there is a funcptr precode to patch, attempt to patch it. If we lose, that's OK,
- // somebody else made progress.
- pFuncPtrPrecode->SetTargetInterlocked(pCode);
- }
- }
- }
-#endif // FEATURE_INTERPRETER
- } // end if (pCode == NULL)
+ pCode = PrepareInitialCode();
} // end else if (IsIL() || IsNoMetadata())
else if (IsNDirect())
{
@@ -1603,13 +1800,7 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
}
/************************** POSTJIT *************************/
-#ifndef FEATURE_INTERPRETER
_ASSERTE(pCode == NULL || GetNativeCode() == NULL || pCode == GetNativeCode());
-#else // FEATURE_INTERPRETER
- // Interpreter adds a new possiblity == someone else beat us to installing an intepreter stub.
- _ASSERTE(pCode == NULL || GetNativeCode() == NULL || pCode == GetNativeCode()
- || Interpreter::InterpretationStubToMethodInfo(pCode) == this);
-#endif // FEATURE_INTERPRETER
// At this point we must have either a pointer to managed code or to a stub. All of the above code
// should have thrown an exception if it couldn't make a stub.
@@ -1638,42 +1829,15 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
MemoryBarrier();
#endif
- // If we are counting calls for tiered compilation, leave the prestub
- // in place so that we can continue intercepting method invocations.
- // When the TieredCompilationManager has received enough call notifications
- // for this method only then do we back-patch it.
-#ifdef FEATURE_TIERED_COMPILATION
- if (pCode && IsEligibleForTieredCompilation())
- {
- CallCounter * pCallCounter = GetAppDomain()->GetCallCounter();
- BOOL doBackPatch = pCallCounter->OnMethodCalled(this);
- if (!doBackPatch)
- {
- return pCode;
- }
- }
-#endif
-
if (pCode != NULL)
{
if (HasPrecode())
GetPrecode()->SetTargetInterlocked(pCode);
else
- if (!HasStableEntryPoint())
- {
- // Is the result an interpreter stub?
-#ifdef FEATURE_INTERPRETER
- if (Interpreter::InterpretationStubToMethodInfo(pCode) == this)
- {
- SetEntryPointInterlocked(pCode);
- }
- else
-#endif // FEATURE_INTERPRETER
+ if (!HasStableEntryPoint())
{
- ReJitPublishMethodHolder publishWorker(this, pCode);
SetStableEntryPointInterlocked(pCode);
}
- }
}
else
{
@@ -1690,15 +1854,8 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
}
}
-#ifdef FEATURE_INTERPRETER
- _ASSERTE(!IsReallyPointingToPrestub());
-#else // FEATURE_INTERPRETER
_ASSERTE(!IsPointingToPrestub());
_ASSERTE(HasStableEntryPoint());
-#endif // FEATURE_INTERPRETER
-
- if (fReportCompilationFinished)
- DACNotifyCompilationFinished(this);
RETURN DoBackpatch(pMT, pDispatchingMT, FALSE);
}
@@ -2127,6 +2284,10 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl
pCode = PatchNonVirtualExternalMethod(pMD, pCode, pImportSection, pIndirection);
}
}
+
+#if defined (FEATURE_JIT_PITCHING)
+ DeleteFromPitchingCandidate(pMD);
+#endif
}
// Force a GC on every jit if the stress level is high enough
@@ -2385,6 +2546,7 @@ void ProcessDynamicDictionaryLookup(TransitionBlock * pTransitionBlock
pResult->signature = NULL;
pResult->indirectFirstOffset = 0;
+ pResult->indirectSecondOffset = 0;
pResult->indirections = CORINFO_USEHELPER;
@@ -2424,6 +2586,9 @@ void ProcessDynamicDictionaryLookup(TransitionBlock * pTransitionBlock
//
// Optimization cases
//
+ // TODO-ARM : If the optimization cases are implemented in CreateDictionaryLookupHelper,
+ // It's ifndef for ARM will be removed.
+#ifndef _TARGET_ARM_
if (signatureKind == ENCODE_TYPE_HANDLE)
{
SigPointer sigptr(pBlob, -1);
@@ -2457,9 +2622,16 @@ void ProcessDynamicDictionaryLookup(TransitionBlock * pTransitionBlock
IfFailThrow(sigptr.GetData(&data));
pResult->offsets[2] = sizeof(TypeHandle) * data;
+ if (MethodTable::IsPerInstInfoRelative())
+ {
+ pResult->indirectFirstOffset = 1;
+ pResult->indirectSecondOffset = 1;
+ }
+
return;
}
}
+#endif // !_TARGET_ARM_
if (pContextMT != NULL && pContextMT->GetNumDicts() > 0xFFFF)
ThrowHR(COR_E_BADIMAGEFORMAT);
@@ -2502,6 +2674,12 @@ void ProcessDynamicDictionaryLookup(TransitionBlock * pTransitionBlock
// Next indirect through the dictionary appropriate to this instantiated type
pResult->offsets[1] = sizeof(TypeHandle*) * (pContextMT->GetNumDicts() - 1);
+ if (MethodTable::IsPerInstInfoRelative())
+ {
+ pResult->indirectFirstOffset = 1;
+ pResult->indirectSecondOffset = 1;
+ }
+
*pDictionaryIndexAndSlot |= dictionarySlot;
}
}
diff --git a/src/vm/profilingenumerators.cpp b/src/vm/profilingenumerators.cpp
index 5044eb7c2b..2406f5aa42 100644
--- a/src/vm/profilingenumerators.cpp
+++ b/src/vm/profilingenumerators.cpp
@@ -79,7 +79,7 @@ BOOL ProfilerFunctionEnum::Init(BOOL fWithReJITIDs)
if (fWithReJITIDs)
{
// This guy causes triggering and locking, while the non-rejitid case does not.
- element->reJitId = pMD->GetReJitManager()->GetReJitId(pMD, heapIterator.GetMethodCode());
+ element->reJitId = ReJitManager::GetReJitId(pMD, heapIterator.GetMethodCode());
}
else
{
diff --git a/src/vm/proftoeeinterfaceimpl.cpp b/src/vm/proftoeeinterfaceimpl.cpp
index cfd99adf27..3958bdf354 100644
--- a/src/vm/proftoeeinterfaceimpl.cpp
+++ b/src/vm/proftoeeinterfaceimpl.cpp
@@ -986,7 +986,7 @@ HRESULT AllowObjectInspection()
#endif // PROFILING_SUPPORTED
-#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
+#if defined(PROFILING_SUPPORTED) || defined(FEATURE_EVENT_TRACE)
//---------------------------------------------------------------------------------------
//
@@ -2117,7 +2117,7 @@ HRESULT ProfToEEInterfaceImpl::GetFunctionFromIP2(LPCBYTE ip, FunctionID * pFunc
if (pReJitId != NULL)
{
MethodDesc * pMD = codeInfo.GetMethodDesc();
- *pReJitId = pMD->GetReJitManager()->GetReJitId(pMD, codeInfo.GetStartAddress());
+ *pReJitId = ReJitManager::GetReJitId(pMD, codeInfo.GetStartAddress());
}
return S_OK;
@@ -2592,13 +2592,24 @@ HRESULT ProfToEEInterfaceImpl::GetCodeInfo3(FunctionID functionId,
hr = ValidateParametersForGetCodeInfo(pMethodDesc, cCodeInfos, codeInfos);
if (SUCCEEDED(hr))
{
- hr = GetCodeInfoFromCodeStart(
- // Note here that we must consult the rejit manager to determine the code
- // start address
- pMethodDesc->GetReJitManager()->GetCodeStart(pMethodDesc, reJitId),
- cCodeInfos,
- pcCodeInfos,
- codeInfos);
+ CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
+ ILCodeVersion ilCodeVersion = pCodeVersionManager->GetILCodeVersion(pMethodDesc, reJitId);
+
+ // Now that tiered compilation can create more than one jitted code version for the same rejit id
+ // we are arbitrarily choosing the first one to return. To return all of them we'd presumably need
+ // a new profiler API.
+ NativeCodeVersionCollection nativeCodeVersions = ilCodeVersion.GetNativeCodeVersions(pMethodDesc);
+ for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++)
+ {
+ PCODE pCodeStart = iter->GetNativeCode();
+ hr = GetCodeInfoFromCodeStart(
+ pCodeStart,
+ cCodeInfos,
+ pcCodeInfos,
+ codeInfos);
+ break;
+ }
+
}
}
EX_CATCH_HRESULT(hr);
@@ -6425,7 +6436,7 @@ HRESULT ProfToEEInterfaceImpl::GetFunctionFromIP3(LPCBYTE ip, FunctionID * pFunc
if (pReJitId != NULL)
{
MethodDesc * pMD = codeInfo.GetMethodDesc();
- *pReJitId = pMD->GetReJitManager()->GetReJitId(pMD, codeInfo.GetStartAddress());
+ *pReJitId = ReJitManager::GetReJitId(pMD, codeInfo.GetStartAddress());
}
return S_OK;
@@ -6832,7 +6843,7 @@ HRESULT ProfToEEInterfaceImpl::GetClassLayout(ClassID classID,
// running into - attempting to get the class layout for all types at module load time.
// If we don't detect this the runtime will AV during the field iteration below. Feel
// free to eliminate this check when a more complete solution is available.
- if (CORCOMPILE_IS_POINTER_TAGGED(*(typeHandle.AsMethodTable()->GetParentMethodTablePtr())))
+ if (typeHandle.AsMethodTable()->GetParentMethodTablePlainOrRelativePointerPtr()->IsTagged())
{
return CORPROF_E_DATAINCOMPLETE;
}
@@ -8239,7 +8250,7 @@ HRESULT ProfToEEInterfaceImpl::GetReJITIDs(
MethodDesc * pMD = FunctionIdToMethodDesc(functionId);
- return pMD->GetReJitManager()->GetReJITIDs(pMD, cReJitIds, pcReJitIds, reJitIds);
+ return ReJitManager::GetReJITIDs(pMD, cReJitIds, pcReJitIds, reJitIds);
}
HRESULT ProfToEEInterfaceImpl::RequestReJIT(ULONG cFunctions, // in
diff --git a/src/vm/readytoruninfo.cpp b/src/vm/readytoruninfo.cpp
index b85cf9a9c3..996a862431 100644
--- a/src/vm/readytoruninfo.cpp
+++ b/src/vm/readytoruninfo.cpp
@@ -483,6 +483,12 @@ PTR_ReadyToRunInfo ReadyToRunInfo::Initialize(Module * pModule, AllocMemTracker
return NULL;
}
+ if (CORProfilerDisableAllNGenImages() || CORProfilerUseProfileImages())
+ {
+ DoLog("Ready to Run disabled - profiler disabled native images");
+ return NULL;
+ }
+
if (g_pConfig->ExcludeReadyToRun(pModule->GetSimpleName()))
{
DoLog("Ready to Run disabled - module on exclusion list");
diff --git a/src/vm/reflectioninvocation.cpp b/src/vm/reflectioninvocation.cpp
index 7f8a9e0075..00556d8805 100644
--- a/src/vm/reflectioninvocation.cpp
+++ b/src/vm/reflectioninvocation.cpp
@@ -12,7 +12,6 @@
#include "method.hpp"
#include "typehandle.h"
#include "field.h"
-#include "security.h"
#include "eeconfig.h"
#include "vars.hpp"
#include "jitinterface.h"
@@ -36,13 +35,13 @@
// it's used for both method and field to signify that no access is allowed
#define INVOCATION_FLAGS_NO_INVOKE 0x00000002
-#define INVOCATION_FLAGS_NEED_SECURITY 0x00000004
+// #define unused 0x00000004
// because field and method are different we can reuse the same bits
//method
#define INVOCATION_FLAGS_IS_CTOR 0x00000010
#define INVOCATION_FLAGS_RISKY_METHOD 0x00000020
-#define INVOCATION_FLAGS_W8P_API 0x00000040
+// #define unused 0x00000040
#define INVOCATION_FLAGS_IS_DELEGATE_CTOR 0x00000080
#define INVOCATION_FLAGS_CONTAINS_STACK_POINTERS 0x00000100
// field
@@ -76,24 +75,6 @@ static TypeHandle NullableTypeOfByref(TypeHandle th) {
return subType;
}
-static void TryDemand(DWORD whatPermission, RuntimeExceptionKind reKind, LPCWSTR wszTag) {
- CONTRACTL {
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- }
- CONTRACTL_END;
-
-
- EX_TRY {
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, whatPermission);
- }
- EX_CATCH {
- COMPlusThrow(reKind, wszTag);
- }
- EX_END_CATCH_UNREACHABLE
-}
-
static void TryCallMethodWorker(MethodDescCallSite* pMethodCallSite, ARG_SLOT* args, Frame* pDebuggerCatchFrame)
{
// Use static contracts b/c we have SEH.
@@ -128,7 +109,7 @@ static void TryCallMethodWorker(MethodDescCallSite* pMethodCallSite, ARG_SLOT* a
// then transfers that data to the newly produced TargetInvocationException. This one
// doesn't take those same steps.
//
-static void TryCallMethod(MethodDescCallSite* pMethodCallSite, ARG_SLOT* args) {
+static void TryCallMethod(MethodDescCallSite* pMethodCallSite, ARG_SLOT* args, bool wrapExceptions) {
CONTRACTL {
THROWS;
GC_TRIGGERS;
@@ -136,32 +117,39 @@ static void TryCallMethod(MethodDescCallSite* pMethodCallSite, ARG_SLOT* args) {
}
CONTRACTL_END;
- OBJECTREF ppException = NULL;
- GCPROTECT_BEGIN(ppException);
+ if (wrapExceptions)
+ {
+ OBJECTREF ppException = NULL;
+ GCPROTECT_BEGIN(ppException);
- // The sole purpose of having this frame is to tell the debugger that we have a catch handler here
- // which may swallow managed exceptions. The debugger needs this in order to send a
- // CatchHandlerFound (CHF) notification.
- FrameWithCookie<DebuggerU2MCatchHandlerFrame> catchFrame;
- EX_TRY {
- TryCallMethodWorker(pMethodCallSite, args, &catchFrame);
- }
- EX_CATCH {
- ppException = GET_THROWABLE();
- _ASSERTE(ppException);
- }
- EX_END_CATCH(RethrowTransientExceptions)
- catchFrame.Pop();
+ // The sole purpose of having this frame is to tell the debugger that we have a catch handler here
+ // which may swallow managed exceptions. The debugger needs this in order to send a
+ // CatchHandlerFound (CHF) notification.
+ FrameWithCookie<DebuggerU2MCatchHandlerFrame> catchFrame;
+ EX_TRY{
+ TryCallMethodWorker(pMethodCallSite, args, &catchFrame);
+ }
+ EX_CATCH{
+ ppException = GET_THROWABLE();
+ _ASSERTE(ppException);
+ }
+ EX_END_CATCH(RethrowTransientExceptions)
+ catchFrame.Pop();
- // It is important to re-throw outside the catch block because re-throwing will invoke
- // the jitter and managed code and will cause us to use more than the backout stack limit.
- if (ppException != NULL)
+ // It is important to re-throw outside the catch block because re-throwing will invoke
+ // the jitter and managed code and will cause us to use more than the backout stack limit.
+ if (ppException != NULL)
+ {
+ // If we get here we need to throw an TargetInvocationException
+ OBJECTREF except = InvokeUtil::CreateTargetExcept(&ppException);
+ COMPlusThrow(except);
+ }
+ GCPROTECT_END();
+ }
+ else
{
- // If we get here we need to throw an TargetInvocationException
- OBJECTREF except = InvokeUtil::CreateTargetExcept(&ppException);
- COMPlusThrow(except);
+ pMethodCallSite->CallWithValueTypes(args);
}
- GCPROTECT_END();
}
@@ -222,55 +210,6 @@ FCIMPL5(Object*, RuntimeFieldHandle::GetValue, ReflectFieldObject *pFieldUNSAFE,
}
FCIMPLEND
-FCIMPL5(void, ReflectionInvocation::PerformVisibilityCheckOnField, FieldDesc *pFieldDesc, Object *target, ReflectClassBaseObject *pDeclaringTypeUNSAFE, DWORD attr, DWORD invocationFlags) {
- CONTRACTL {
- FCALL_CHECK;
- PRECONDITION(CheckPointer(pFieldDesc));
- PRECONDITION(CheckPointer(pDeclaringTypeUNSAFE));
- }
- CONTRACTL_END;
-
-
- REFLECTCLASSBASEREF refDeclaringType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pDeclaringTypeUNSAFE);
-
- TypeHandle declaringType = refDeclaringType->GetType();
- OBJECTREF targetObj = ObjectToOBJECTREF(target);
-
- HELPER_METHOD_FRAME_BEGIN_2(targetObj, refDeclaringType);
-
- if ((invocationFlags & INVOCATION_FLAGS_SPECIAL_FIELD) != 0) {
- // Verify that this is not a Final Field
- if (IsFdInitOnly(attr))
- TryDemand(SECURITY_SERIALIZATION, kFieldAccessException, W("Acc_ReadOnly"));
- if (IsFdHasFieldRVA(attr))
- TryDemand(SECURITY_SKIP_VER, kFieldAccessException, W("Acc_RvaStatic"));
- }
-
- if ((invocationFlags & INVOCATION_FLAGS_NEED_SECURITY) != 0) {
- // Verify the callee/caller access
-
- bool targetRemoted = FALSE;
-
-
- RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType(targetRemoted));
-
- MethodTable* pInstanceMT = NULL;
- if (targetObj != NULL && !pFieldDesc->IsStatic()) {
- TypeHandle targetType = targetObj->GetTypeHandle();
- if (!targetType.IsTypeDesc())
- pInstanceMT = targetType.AsMethodTable();
- }
-
- // Perform the normal access check (caller vs field).
- InvokeUtil::CanAccessField(&sCtx,
- declaringType.GetMethodTable(),
- pInstanceMT,
- pFieldDesc);
- }
- HELPER_METHOD_FRAME_END();
-}
-FCIMPLEND
-
FCIMPL2(FC_BOOL_RET, ReflectionInvocation::CanValueSpecialCast, ReflectClassBaseObject *pValueTypeUNSAFE, ReflectClassBaseObject *pTargetTypeUNSAFE) {
CONTRACTL {
FCALL_CHECK;
@@ -296,9 +235,7 @@ FCIMPL2(FC_BOOL_RET, ReflectionInvocation::CanValueSpecialCast, ReflectClassBase
// the object must be an IntPtr or a System.Reflection.Pointer
if (valueType == TypeHandle(MscorlibBinder::GetClass(CLASS__INTPTR))) {
//
- // it's an IntPtr, it's good. Demand SkipVerification and proceed
-
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
+ // it's an IntPtr, it's good.
}
//
// it's a System.Reflection.Pointer object
@@ -307,13 +244,7 @@ FCIMPL2(FC_BOOL_RET, ReflectionInvocation::CanValueSpecialCast, ReflectClassBase
else if (!InvokeUtil::IsVoidPtr(targetType)) {
if (!valueType.CanCastTo(targetType))
ret = FALSE;
- else
- // demand SkipVerification and proceed
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
}
- else
- // demand SkipVerification and proceed
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
} else {
// the field type is an enum or a primitive. To have any chance of assignement the object type must
// be an enum or primitive as well.
@@ -470,8 +401,9 @@ FCIMPL1(Object*, RuntimeTypeHandle::Allocate, ReflectClassBaseObject* pTypeUNSAF
}//Allocate
FCIMPLEND
-FCIMPL4(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refThisUNSAFE,
+FCIMPL5(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refThisUNSAFE,
CLR_BOOL publicOnly,
+ CLR_BOOL wrapExceptions,
CLR_BOOL* pbCanBeCached,
MethodDesc** pConstructor) {
CONTRACTL {
@@ -523,10 +455,6 @@ FCIMPL4(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refT
if (!pClassFactory)
COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY);
- // Check for the required permissions (SecurityPermission.UnmanagedCode),
- // since arbitrary unmanaged code in the class factory will execute below).
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_UNMANAGED_CODE);
-
// create an instance of the Com Object
rv = ((ComClassFactory*)pClassFactory)->CreateInstance(NULL);
@@ -539,11 +467,6 @@ FCIMPL4(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refT
else
#endif // FEATURE_COMINTEROP
{
- // If we are creating a COM object which has backing metadata we still
- // need to ensure that the caller has unmanaged code access permission.
- if (pVMT->IsComObjectType())
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_UNMANAGED_CODE);
-
// if this is an abstract class then we will fail this
if (pVMT->IsAbstract()) {
if (pVMT->IsInterface())
@@ -585,17 +508,17 @@ FCIMPL4(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refT
else // !pVMT->HasDefaultConstructor()
{
pMeth = pVMT->GetDefaultConstructor();
-
+
// Validate the method can be called by this caller
DWORD attr = pMeth->GetAttrs();
if (!IsMdPublic(attr) && publicOnly)
- COMPlusThrow(kMissingMethodException,W("Arg_NoDefCTor"));
+ COMPlusThrow(kMissingMethodException, W("Arg_NoDefCTor"));
// We've got the class, lets allocate it and call the constructor
OBJECTREF o;
bool remoting = false;
-
+
o = AllocateObject(pVMT);
GCPROTECT_BEGIN(o);
@@ -609,7 +532,7 @@ FCIMPL4(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refT
arg = ObjToArgSlot(o);
// Call the method
- TryCallMethod(&ctor, &arg);
+ TryCallMethod(&ctor, &arg, wrapExceptions);
rv = o;
GCPROTECT_END();
@@ -674,7 +597,7 @@ FCIMPL2(Object*, RuntimeTypeHandle::CreateInstanceForGenericType, ReflectClassBa
ARG_SLOT arg = ObjToArgSlot(gc.rv);
// Call the method
- TryCallMethod(&ctor, &arg);
+ TryCallMethod(&ctor, &arg, true);
HELPER_METHOD_FRAME_END();
return OBJECTREFToObject(gc.rv);
@@ -767,15 +690,6 @@ FCIMPL1(DWORD, ReflectionInvocation::GetSpecialSecurityFlags, ReflectMethodObjec
if (InvokeUtil::IsDangerousMethod(pMethod))
dwFlags |= INVOCATION_FLAGS_RISKY_METHOD;
- // Is there a link demand?
- if (pMethod->RequiresLinktimeCheck()) {
- dwFlags |= INVOCATION_FLAGS_NEED_SECURITY;
- }
- else
- if (Security::IsMethodCritical(pMethod) && !Security::IsMethodSafeCritical(pMethod)) {
- dwFlags |= INVOCATION_FLAGS_NEED_SECURITY;
- }
-
HELPER_METHOD_FRAME_END();
return dwFlags;
}
@@ -846,8 +760,6 @@ OBJECTREF InvokeArrayConstructor(ArrayTypeDesc* arrayDesc, MethodDesc* pMeth, PT
// If we're trying to create an array of pointers or function pointers,
// check that the caller has skip verification permission.
CorElementType et = arrayDesc->GetArrayElementTypeHandle().GetVerifierCorElementType();
- if (et == ELEMENT_TYPE_PTR || et == ELEMENT_TYPE_FNPTR)
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_SKIP_VER);
// Validate the argCnt an the Rank. Also allow nested SZARRAY's.
_ASSERTE(argCnt == (int) arrayDesc->GetRank() || argCnt == (int) arrayDesc->GetRank() * 2 ||
@@ -1122,8 +1034,9 @@ void DECLSPEC_NORETURN ThrowInvokeMethodException(MethodDesc * pMethod, OBJECTRE
GCPROTECT_END();
}
-FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod,
- Object *target, PTRArray *objs, SignatureNative* pSigUNSAFE, CLR_BOOL fConstructor)
+FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
+ Object *target, PTRArray *objs, SignatureNative* pSigUNSAFE,
+ CLR_BOOL fConstructor, CLR_BOOL fWrapExceptions)
{
FCALL_CONTRACT;
@@ -1429,30 +1342,38 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod,
FrameWithCookie<ProtectValueClassFrame>(pThread, pValueClasses);
}
- // The sole purpose of having this frame is to tell the debugger that we have a catch handler here
- // which may swallow managed exceptions. The debugger needs this in order to send a
- // CatchHandlerFound (CHF) notification.
- FrameWithCookie<DebuggerU2MCatchHandlerFrame> catchFrame(pThread);
-
// Call the method
bool fExceptionThrown = false;
- EX_TRY_THREAD(pThread) {
- CallDescrWorkerReflectionWrapper(&callDescrData, &catchFrame);
- } EX_CATCH {
- // Rethrow transient exceptions for constructors for backward compatibility
- if (fConstructor && GET_EXCEPTION()->IsTransient())
- {
- EX_RETHROW;
- }
+ if (fWrapExceptions)
+ {
+ // The sole purpose of having this frame is to tell the debugger that we have a catch handler here
+ // which may swallow managed exceptions. The debugger needs this in order to send a
+ // CatchHandlerFound (CHF) notification.
+ FrameWithCookie<DebuggerU2MCatchHandlerFrame> catchFrame(pThread);
+
+ EX_TRY_THREAD(pThread) {
+ CallDescrWorkerReflectionWrapper(&callDescrData, &catchFrame);
+ } EX_CATCH{
+ // Rethrow transient exceptions for constructors for backward compatibility
+ if (fConstructor && GET_EXCEPTION()->IsTransient())
+ {
+ EX_RETHROW;
+ }
// Abuse retval to store the exception object
gc.retVal = GET_THROWABLE();
_ASSERTE(gc.retVal);
fExceptionThrown = true;
- } EX_END_CATCH(SwallowAllExceptions);
+ } EX_END_CATCH(SwallowAllExceptions);
+
+ catchFrame.Pop(pThread);
+ }
+ else
+ {
+ CallDescrWorkerWithHandler(&callDescrData);
+ }
- catchFrame.Pop(pThread);
// Now that we are safely out of the catch block, we can create and raise the
// TargetInvocationException.
@@ -1826,12 +1747,6 @@ FCIMPL5(void, RuntimeFieldHandle::SetValueDirect, ReflectFieldObject *pFieldUNSA
// Verify that this is not a Final Field
DWORD attr = pField->GetAttributes(); // should we cache?
- if (IsFdInitOnly(attr)) {
- TryDemand(SECURITY_SERIALIZATION, kFieldAccessException, W("Acc_ReadOnly"));
- }
- if (IsFdHasFieldRVA(attr)) {
- TryDemand(SECURITY_SKIP_VER, kFieldAccessException, W("Acc_RvaStatic"));
- }
if (IsFdLiteral(attr))
COMPlusThrow(kFieldAccessException,W("Acc_ReadOnly"));
@@ -2581,10 +2496,6 @@ FCIMPL8(Object*, ReflectionInvocation::InvokeDispMethod, ReflectClassBaseObject*
_ASSERTE(gc.target != NULL);
_ASSERTE(gc.target->GetMethodTable()->IsComObjectType());
- // Unless security is turned off, we need to validate that the calling code
- // has unmanaged code access privilege.
- Security::SpecialDemand(SSWT_LATEBOUND_LINKDEMAND, SECURITY_UNMANAGED_CODE);
-
WORD flags = 0;
if (invokeAttr & BINDER_InvokeMethod)
flags |= DISPATCH_METHOD;
diff --git a/src/vm/reflectioninvocation.h b/src/vm/reflectioninvocation.h
index 206e7516be..6a183b134c 100644
--- a/src/vm/reflectioninvocation.h
+++ b/src/vm/reflectioninvocation.h
@@ -38,6 +38,7 @@
#define BINDER_OptionalParamBinding 0x040000
#define BINDER_IgnoreReturn 0x1000000
+#define BINDER_DoNotWrapExceptions 0x2000000
#define BINDER_DefaultLookup (BINDER_Instance | BINDER_Static | BINDER_Public)
#define BINDER_AllLookup (BINDER_Instance | BINDER_Static | BINDER_Public | BINDER_Instance)
@@ -80,8 +81,6 @@ public:
static FCDECL4(void, PerformSecurityCheck, Object *target, MethodDesc *pMeth, ReflectClassBaseObject *pParent, DWORD dwFlags);
static FCDECL2(void, CheckArgs, PTRArray *objs, SignatureNative sig);
- static FCDECL5(void, PerformVisibilityCheckOnField, FieldDesc *fieldDesc, Object *target, ReflectClassBaseObject *pDeclaringType, DWORD attr, DWORD invocationFlags);
-
static void PrepareDelegateHelper(OBJECTREF* pDelegate, BOOL onlyContractedMethod);
static void CanCacheTargetAndCrackedSig(MethodDesc* pMD);
};
diff --git a/src/vm/rejit.cpp b/src/vm/rejit.cpp
index 7bbd0e2f71..2a9c9e78a3 100644
--- a/src/vm/rejit.cpp
+++ b/src/vm/rejit.cpp
@@ -37,7 +37,7 @@
// appropriate IL and codegen flags, calling UnsafeJitFunction(), and redirecting the
// jump-stamp from the prestub to the newly-rejitted code.
//
-// * code:ReJitPublishMethodHolder::ReJitPublishMethodHolder
+// * code:PublishMethodHolder::PublishMethodHolder
// MethodDesc::MakeJitWorker() calls this to determine if there's an outstanding
// "pre-rejit" request for a MethodDesc that has just been jitted for the first time. We
// also call this from MethodDesc::CheckRestore when restoring generic methods.
@@ -48,8 +48,8 @@
// the PCODE, which is required to avoid races with a profiler that calls RequestReJIT
// just as the method finishes compiling/restoring.
//
-// * code:ReJitPublishMethodTableHolder::ReJitPublishMethodTableHolder
-// Does the same thing as ReJitPublishMethodHolder except iterating over every
+// * code:PublishMethodTableHolder::PublishMethodTableHolder
+// Does the same thing as PublishMethodHolder except iterating over every
// method in the MethodTable. This is called from MethodTable::SetIsRestored.
//
// * code:ReJitManager::GetCurrentReJitFlags:
@@ -156,20 +156,21 @@
#include "threadsuspend.h"
#ifdef FEATURE_REJIT
+#ifdef FEATURE_CODE_VERSIONING
#include "../debug/ee/debugger.h"
#include "../debug/ee/walker.h"
#include "../debug/ee/controller.h"
+#include "codeversion.h"
-// This HRESULT is only used as a private implementation detail. If it escapes functions
-// defined in this file it is a bug. Corerror.xml has a comment in it reserving this
-// value for our use but it doesn't appear in the public headers.
+// This HRESULT is only used as a private implementation detail. Corerror.xml has a comment in it
+// reserving this value for our use but it doesn't appear in the public headers.
#define CORPROF_E_RUNTIME_SUSPEND_REQUIRED 0x80131381
// This is just used as a unique id. Overflow is OK. If we happen to have more than 4+Billion rejits
// and somehow manage to not run out of memory, we'll just have to redefine ReJITID as size_t.
/* static */
-ReJITID SharedReJitInfo::s_GlobalReJitId = 1;
+static ReJITID s_GlobalReJitId = 1;
/* static */
CrstStatic ReJitManager::s_csGlobalRequest;
@@ -178,19 +179,20 @@ CrstStatic ReJitManager::s_csGlobalRequest;
//---------------------------------------------------------------------------------------
// Helpers
-inline CORJIT_FLAGS JitFlagsFromProfCodegenFlags(DWORD dwCodegenFlags)
+//static
+CORJIT_FLAGS ReJitManager::JitFlagsFromProfCodegenFlags(DWORD dwCodegenFlags)
{
LIMITED_METHOD_DAC_CONTRACT;
CORJIT_FLAGS jitFlags;
-
- // Note: COR_PRF_CODEGEN_DISABLE_INLINING is checked in
- // code:CEEInfo::canInline#rejit (it has no equivalent CORJIT flag).
-
if ((dwCodegenFlags & COR_PRF_CODEGEN_DISABLE_ALL_OPTIMIZATIONS) != 0)
{
jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_CODE);
}
+ if ((dwCodegenFlags & COR_PRF_CODEGEN_DISABLE_INLINING) != 0)
+ {
+ jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_NO_INLINING);
+ }
// In the future more flags may be added that need to be converted here (e.g.,
// COR_PRF_CODEGEN_ENTERLEAVE / CORJIT_FLAG_PROF_ENTERLEAVE)
@@ -199,94 +201,6 @@ inline CORJIT_FLAGS JitFlagsFromProfCodegenFlags(DWORD dwCodegenFlags)
}
//---------------------------------------------------------------------------------------
-// Allocation helpers used by ReJitInfo / SharedReJitInfo to ensure they
-// stick stuff on the appropriate loader heap.
-
-void * LoaderHeapAllocatedRejitStructure::operator new (size_t size, LoaderHeap * pHeap, const NoThrow&)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- INJECT_FAULT(return NULL;);
- PRECONDITION(CheckPointer(pHeap));
- }
- CONTRACTL_END;
-
-#ifdef DACCESS_COMPILE
- return ::operator new(size, nothrow);
-#else
- return pHeap->AllocMem_NoThrow(S_SIZE_T(size));
-#endif
-}
-
-void * LoaderHeapAllocatedRejitStructure::operator new (size_t size, LoaderHeap * pHeap)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- INJECT_FAULT(COMPlusThrowOM());
- PRECONDITION(CheckPointer(pHeap));
- }
- CONTRACTL_END;
-
-#ifdef DACCESS_COMPILE
- return ::operator new(size);
-#else
- return pHeap->AllocMem(S_SIZE_T(size));
-#endif
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Simple, thin abstraction of debugger breakpoint patching. Given an address and a
-// previously procured DebuggerControllerPatch governing the code address, this decides
-// whether the code address is patched. If so, it returns a pointer to the debugger's
-// buffer (of what's "underneath" the int 3 patch); otherwise, it returns the code
-// address itself.
-//
-// Arguments:
-// * pbCode - Code address to return if unpatched
-// * dbgpatch - DebuggerControllerPatch to test
-//
-// Return Value:
-// Either pbCode or the debugger's patch buffer, as per description above.
-//
-// Assumptions:
-// Caller must manually grab (and hold) the ControllerLockHolder and get the
-// DebuggerControllerPatch before calling this helper.
-//
-// Notes:
-// pbCode need not equal the code address governed by dbgpatch, but is always
-// "related" (and sometimes really is equal). For example, this helper may be used
-// when writing a code byte to an internal rejit buffer (e.g., in preparation for an
-// eventual 64-bit interlocked write into the code stream), and thus pbCode would
-// point into the internal rejit buffer whereas dbgpatch governs the corresponding
-// code byte in the live code stream. This function would then be used to determine
-// whether a byte should be written into the internal rejit buffer OR into the
-// debugger controller's breakpoint buffer.
-//
-
-LPBYTE FirstCodeByteAddr(LPBYTE pbCode, DebuggerControllerPatch * dbgpatch)
-{
- LIMITED_METHOD_CONTRACT;
-
- if (dbgpatch != NULL && dbgpatch->IsActivated())
- {
- // Debugger has patched the code, so return the address of the buffer
- return LPBYTE(&(dbgpatch->opcode));
- }
-
- // no active patch, just return the direct code address
- return pbCode;
-}
-
-
-//---------------------------------------------------------------------------------------
// ProfilerFunctionControl implementation
ProfilerFunctionControl::ProfilerFunctionControl(LoaderHeap * pHeap) :
@@ -532,30 +446,6 @@ COR_IL_MAP* ProfilerFunctionControl::GetInstrumentedMapEntries()
#ifndef DACCESS_COMPILE
//---------------------------------------------------------------------------------------
-// Called by the prestub worker, this function is a simple wrapper which determines the
-// appropriate ReJitManager, and then calls DoReJitIfNecessaryWorker() on it. See the
-// comment at the top of code:ReJitManager::DoReJitIfNecessaryWorker for more info,
-// including parameter & return value descriptions.
-
-// static
-PCODE ReJitManager::DoReJitIfNecessary(PTR_MethodDesc pMD)
-{
- STANDARD_VM_CONTRACT;
-
- if (!pMD->HasNativeCode())
- {
- // If method hasn't been jitted yet, the prestub worker should just continue as
- // usual.
- return NULL;
- }
-
- // We've already published the JITted code for this MethodDesc, and yet we're
- // back in the prestub (who called us). Ask the appropriate rejit manager if that's because of a rejit request. If so, the
- // ReJitManager will take care of the rejit now
- return pMD->GetReJitManager()->DoReJitIfNecessaryWorker(pMD);
-}
-
-//---------------------------------------------------------------------------------------
//
// ICorProfilerInfo4::RequestReJIT calls into this guy to do most of the
// work. Takes care of finding the appropriate ReJitManager instances to
@@ -579,6 +469,18 @@ HRESULT ReJitManager::RequestReJIT(
ModuleID rgModuleIDs[],
mdMethodDef rgMethodDefs[])
{
+ return ReJitManager::UpdateActiveILVersions(cFunctions, rgModuleIDs, rgMethodDefs, NULL, FALSE);
+}
+
+
+ // static
+HRESULT ReJitManager::UpdateActiveILVersions(
+ ULONG cFunctions,
+ ModuleID rgModuleIDs[],
+ mdMethodDef rgMethodDefs[],
+ HRESULT rgHrStatuses[],
+ BOOL fIsRevert)
+{
CONTRACTL
{
NOTHROW;
@@ -599,20 +501,12 @@ HRESULT ReJitManager::RequestReJIT(
// Temporary storage to batch up all the ReJitInfos that will get jump stamped
// later when the runtime is suspended.
//
- //BUGBUG: Its not clear to me why it is safe to hold ReJitInfo* lists
- // outside the table locks. If an AppDomain unload occurred I don't see anything
- // that prevents them from being deleted. If this is a bug it is a pre-existing
- // condition and nobody has reported it as an issue yet. AppDomainExit probably
- // needs to synchronize with something.
- // Jan also pointed out the ModuleIDs have the same issue, in order to use this
- // function safely the profiler needs prevent the AppDomain which contains the
- // modules from being unloaded. I doubt any profilers are doing this intentionally
- // but calling from within typical callbacks like ModuleLoadFinished or
- // JIT events would do it for the current domain I think. Of course RequestRejit
- // could always be called with ModuleIDs in some other AppDomain.
- //END BUGBUG
- SHash<ReJitManagerJumpStampBatchTraits> mgrToJumpStampBatch;
- CDynArray<ReJitReportErrorWorkItem> errorRecords;
+ //DESKTOP WARNING: On CoreCLR we are safe but if this code ever gets ported back
+ //there aren't any protections against domain unload. Any of these moduleIDs
+ //code version managers, or code versions would become invalid if the domain which
+ //contains them was unloaded.
+ SHash<CodeActivationBatchTraits> mgrToCodeActivationBatch;
+ CDynArray<CodeVersionManager::CodePublishError> errorRecords;
for (ULONG i = 0; i < cFunctions; i++)
{
Module * pModule = reinterpret_cast< Module * >(rgModuleIDs[i]);
@@ -660,13 +554,13 @@ HRESULT ReJitManager::RequestReJIT(
}
}
- ReJitManager * pReJitMgr = pModule->GetReJitManager();
- _ASSERTE(pReJitMgr != NULL);
- ReJitManagerJumpStampBatch * pJumpStampBatch = mgrToJumpStampBatch.Lookup(pReJitMgr);
- if (pJumpStampBatch == NULL)
+ CodeVersionManager * pCodeVersionManager = pModule->GetCodeVersionManager();
+ _ASSERTE(pCodeVersionManager != NULL);
+ CodeActivationBatch * pCodeActivationBatch = mgrToCodeActivationBatch.Lookup(pCodeVersionManager);
+ if (pCodeActivationBatch == NULL)
{
- pJumpStampBatch = new (nothrow)ReJitManagerJumpStampBatch(pReJitMgr);
- if (pJumpStampBatch == NULL)
+ pCodeActivationBatch = new (nothrow)CodeActivationBatch(pCodeVersionManager);
+ if (pCodeActivationBatch == NULL)
{
return E_OUTOFMEMORY;
}
@@ -676,7 +570,7 @@ HRESULT ReJitManager::RequestReJIT(
{
// This guy throws when out of memory, but remains internally
// consistent (without adding the new element)
- mgrToJumpStampBatch.Add(pJumpStampBatch);
+ mgrToCodeActivationBatch.Add(pCodeActivationBatch);
}
EX_CATCH_HRESULT(hr);
@@ -687,133 +581,24 @@ HRESULT ReJitManager::RequestReJIT(
}
}
-
- // At this stage, pMD may be NULL or non-NULL, and the specified function may or
- // may not be a generic (or a function on a generic class). The operations
- // below depend on these conditions as follows:
- //
- // (1) If pMD == NULL || PMD has no code || pMD is generic
- // Do a "PRE-REJIT" (add a placeholder ReJitInfo that points to module/token;
- // there's nothing to jump-stamp)
- //
- // (2) IF pMD != NULL, but not generic (or function on generic class)
- // Do a REAL REJIT (add a real ReJitInfo that points to pMD and jump-stamp)
- //
- // (3) IF pMD != NULL, and is a generic (or function on generic class)
- // Do a real rejit (including jump-stamp) for all already-jitted instantiations.
-
- BaseDomain * pBaseDomainFromModule = pModule->GetDomain();
- SharedReJitInfo * pSharedInfo = NULL;
{
- CrstHolder ch(&(pReJitMgr->m_crstTable));
-
- // Do a PRE-rejit
- if (pMD == NULL || !pMD->HasNativeCode() || pMD->HasClassOrMethodInstantiation())
- {
- hr = pReJitMgr->MarkForReJit(
- pModule,
- rgMethodDefs[i],
- pJumpStampBatch,
- &errorRecords,
- &pSharedInfo);
- if (FAILED(hr))
- {
- _ASSERTE(hr == E_OUTOFMEMORY);
- return hr;
- }
- }
-
- if (pMD == NULL)
- {
- // nothing is loaded yet so only the pre-rejit placeholder is needed. We're done for this method.
- continue;
- }
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
- if (!pMD->HasClassOrMethodInstantiation() && pMD->HasNativeCode())
+ // Bind the il code version
+ ILCodeVersion* pILCodeVersion = pCodeActivationBatch->m_methodsToActivate.Append();
+ if (pILCodeVersion == NULL)
{
- // We have a JITted non-generic. Easy case. Just mark the JITted method
- // desc as needing to be rejitted
- hr = pReJitMgr->MarkForReJit(
- pMD,
- pSharedInfo,
- pJumpStampBatch,
- &errorRecords,
- NULL); // Don't need the SharedReJitInfo to be returned
-
- if (FAILED(hr))
- {
- _ASSERTE(hr == E_OUTOFMEMORY);
- return hr;
- }
+ return E_OUTOFMEMORY;
}
-
- if (!pMD->HasClassOrMethodInstantiation())
+ if (fIsRevert)
{
- // not generic, we're done for this method
- continue;
- }
-
- // Ok, now the case of a generic function (or function on generic class), which
- // is loaded, and may thus have compiled instantiations.
- // It's impossible to get to any other kind of domain from the profiling API
- _ASSERTE(pBaseDomainFromModule->IsAppDomain() ||
- pBaseDomainFromModule->IsSharedDomain());
-
- if (pBaseDomainFromModule->IsSharedDomain())
- {
- // Iterate through all modules loaded into the shared domain, to
- // find all instantiations living in the shared domain. This will
- // include orphaned code (i.e., shared code used by ADs that have
- // all unloaded), which is good, because orphaned code could get
- // re-adopted if a new AD is created that can use that shared code
- hr = pReJitMgr->MarkAllInstantiationsForReJit(
- pSharedInfo,
- NULL, // NULL means to search SharedDomain instead of an AD
- pModule,
- rgMethodDefs[i],
- pJumpStampBatch,
- &errorRecords);
+ // activate the original version
+ *pILCodeVersion = ILCodeVersion(pModule, rgMethodDefs[i]);
}
else
{
- // Module is unshared, so just use the module's domain to find instantiations.
- hr = pReJitMgr->MarkAllInstantiationsForReJit(
- pSharedInfo,
- pBaseDomainFromModule->AsAppDomain(),
- pModule,
- rgMethodDefs[i],
- pJumpStampBatch,
- &errorRecords);
- }
- if (FAILED(hr))
- {
- _ASSERTE(hr == E_OUTOFMEMORY);
- return hr;
- }
- }
-
- // We want to iterate through all compilations of existing instantiations to
- // ensure they get marked for rejit. Note: There may be zero instantiations,
- // but we won't know until we try.
- if (pBaseDomainFromModule->IsSharedDomain())
- {
- // Iterate through all real domains, to find shared instantiations.
- AppDomainIterator appDomainIterator(TRUE);
- while (appDomainIterator.Next())
- {
- AppDomain * pAppDomain = appDomainIterator.GetDomain();
- if (pAppDomain->IsUnloading())
- {
- continue;
- }
- CrstHolder ch(&(pReJitMgr->m_crstTable));
- hr = pReJitMgr->MarkAllInstantiationsForReJit(
- pSharedInfo,
- pAppDomain,
- pModule,
- rgMethodDefs[i],
- pJumpStampBatch,
- &errorRecords);
+ // activate an unused or new IL version
+ hr = ReJitManager::BindILVersion(pCodeVersionManager, pModule, rgMethodDefs[i], pILCodeVersion);
if (FAILED(hr))
{
_ASSERTE(hr == E_OUTOFMEMORY);
@@ -823,18 +608,18 @@ HRESULT ReJitManager::RequestReJIT(
}
} // for (ULONG i = 0; i < cFunctions; i++)
- // For each rejit mgr, if there's work to do, suspend EE if needed,
- // enter the rejit mgr's crst, and do the batched work.
+ // For each code versioning mgr, if there's work to do, suspend EE if needed,
+ // enter the code versioning mgr's crst, and do the batched work.
BOOL fEESuspended = FALSE;
- SHash<ReJitManagerJumpStampBatchTraits>::Iterator beginIter = mgrToJumpStampBatch.Begin();
- SHash<ReJitManagerJumpStampBatchTraits>::Iterator endIter = mgrToJumpStampBatch.End();
- for (SHash<ReJitManagerJumpStampBatchTraits>::Iterator iter = beginIter; iter != endIter; iter++)
+ SHash<CodeActivationBatchTraits>::Iterator beginIter = mgrToCodeActivationBatch.Begin();
+ SHash<CodeActivationBatchTraits>::Iterator endIter = mgrToCodeActivationBatch.End();
+ for (SHash<CodeActivationBatchTraits>::Iterator iter = beginIter; iter != endIter; iter++)
{
- ReJitManagerJumpStampBatch * pJumpStampBatch = *iter;
- ReJitManager * pMgr = pJumpStampBatch->pReJitManager;
+ CodeActivationBatch * pCodeActivationBatch = *iter;
+ CodeVersionManager * pCodeVersionManager = pCodeActivationBatch->m_pCodeVersionManager;
- int cBatchedPreStubMethods = pJumpStampBatch->preStubMethods.Count();
- if (cBatchedPreStubMethods == 0)
+ int cMethodsToActivate = pCodeActivationBatch->m_methodsToActivate.Count();
+ if (cMethodsToActivate == 0)
{
continue;
}
@@ -842,14 +627,12 @@ HRESULT ReJitManager::RequestReJIT(
{
// As a potential future optimization we could speculatively try to update the jump stamps without
// suspending the runtime. That needs to be plumbed through BatchUpdateJumpStamps though.
-
ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT);
fEESuspended = TRUE;
}
- CrstHolder ch(&(pMgr->m_crstTable));
_ASSERTE(ThreadStore::HoldingThreadStore());
- hr = pMgr->BatchUpdateJumpStamps(&(pJumpStampBatch->undoMethods), &(pJumpStampBatch->preStubMethods), &errorRecords);
+ hr = pCodeVersionManager->SetActiveILCodeVersions(pCodeActivationBatch->m_methodsToActivate.Ptr(), pCodeActivationBatch->m_methodsToActivate.Count(), fEESuspended, &errorRecords);
if (FAILED(hr))
break;
}
@@ -867,702 +650,72 @@ HRESULT ReJitManager::RequestReJIT(
// Report any errors that were batched up
for (int i = 0; i < errorRecords.Count(); i++)
{
- ReportReJITError(&(errorRecords[i]));
- }
-
- INDEBUG(SharedDomain::GetDomain()->GetReJitManager()->Dump(
- "Finished RequestReJIT(). Dumping Shared ReJitManager\n"));
-
- // We got through processing everything, but profiler will need to see the individual ReJITError
- // callbacks to know what, if anything, failed.
- return S_OK;
-}
-
-//---------------------------------------------------------------------------------------
-//
-// Helper used by ReJitManager::RequestReJIT to jump stamp all the methods that were
-// specified by the caller. Also used by RejitManager::DoJumpStampForAssemblyIfNecessary
-// when rejitting a batch of generic method instantiations in a newly loaded NGEN assembly.
-//
-// This method is responsible for calling ReJITError on the profiler if anything goes
-// wrong.
-//
-// Arguments:
-// * pUndoMethods - array containing the methods that need the jump stamp removed
-// * pPreStubMethods - array containing the methods that need to be jump stamped to prestub
-// * pErrors - any errors will be appended to this array
-//
-// Returns:
-// S_OK - all methods are updated or added an error to the pErrors array
-// E_OUTOFMEMORY - some methods neither updated nor added an error to pErrors array
-// ReJitInfo state remains consistent
-//
-// Assumptions:
-// 1) Caller prevents contention by either:
-// a) Suspending the runtime
-// b) Ensuring all methods being updated haven't been published
-//
-HRESULT ReJitManager::BatchUpdateJumpStamps(CDynArray<ReJitInfo *> * pUndoMethods, CDynArray<ReJitInfo *> * pPreStubMethods, CDynArray<ReJitReportErrorWorkItem> * pErrors)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- PRECONDITION(CheckPointer(pUndoMethods));
- PRECONDITION(CheckPointer(pPreStubMethods));
- PRECONDITION(CheckPointer(pErrors));
- }
- CONTRACTL_END;
-
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
- HRESULT hr = S_OK;
-
- ReJitInfo ** ppInfoEnd = pUndoMethods->Ptr() + pUndoMethods->Count();
- for (ReJitInfo ** ppInfoCur = pUndoMethods->Ptr(); ppInfoCur < ppInfoEnd; ppInfoCur++)
- {
- // If we are undoing jumpstamps they have been published already
- // and our caller is holding the EE suspended
- _ASSERTE(ThreadStore::HoldingThreadStore());
- if (FAILED(hr = (*ppInfoCur)->UndoJumpStampNativeCode(TRUE)))
- {
- if (FAILED(hr = AddReJITError(*ppInfoCur, hr, pErrors)))
- {
- _ASSERTE(hr == E_OUTOFMEMORY);
- return hr;
- }
- }
- }
-
- ppInfoEnd = pPreStubMethods->Ptr() + pPreStubMethods->Count();
- for (ReJitInfo ** ppInfoCur = pPreStubMethods->Ptr(); ppInfoCur < ppInfoEnd; ppInfoCur++)
- {
- if (FAILED(hr = (*ppInfoCur)->JumpStampNativeCode()))
- {
- if (FAILED(hr = AddReJITError(*ppInfoCur, hr, pErrors)))
- {
- _ASSERTE(hr == E_OUTOFMEMORY);
- return hr;
- }
- }
- }
- return S_OK;
-}
-
-//---------------------------------------------------------------------------------------
-//
-// Helper used by ReJitManager::RequestReJIT to iterate through any generic
-// instantiations of a function in a given AppDomain, and to create the corresponding
-// ReJitInfos for those MethodDescs. This also adds corresponding entries to a temporary
-// dynamic array created by our caller for batching up the jump-stamping we'll need to do
-// later.
-//
-// This method is responsible for calling ReJITError on the profiler if anything goes
-// wrong.
-//
-// Arguments:
-// * pSharedForAllGenericInstantiations - The SharedReJitInfo for this mdMethodDef's
-// rejit request. This is what we must associate any newly-created ReJitInfo with.
-// * pAppDomainToSearch - AppDomain in which to search for generic instantiations
-// matching the specified methodDef. If it is NULL, then we'll search for all
-// MethodDescs whose metadata definition appears in a Module loaded into the
-// SharedDomain (regardless of which ADs--if any--are using those MethodDescs).
-// This captures the case of domain-neutral code that was in use by an AD that
-// unloaded, and may come into use again once a new AD loads that can use the
-// shared code.
-// * pModuleContainingMethodDef - Module* containing the specified methodDef token.
-// * methodDef - Token for the method for which we're searching for MethodDescs.
-// * pJumpStampBatch - Batch we're responsible for placing ReJitInfo's into, on which
-// the caller will update the jump stamps.
-// * pRejitErrors - Dynamic array we're responsible for adding error records into.
-// The caller will report them to the profiler outside the table lock
-//
-// Returns:
-// S_OK - all methods were either marked for rejit OR have appropriate error records
-// in pRejitErrors
-// E_OUTOFMEMORY - some methods weren't marked for rejit AND we didn't have enough
-// memory to create the error records
-//
-// Assumptions:
-// * This function should only be called on the ReJitManager that owns the (generic)
-// definition of methodDef
-// * If pModuleContainingMethodDef is loaded into the SharedDomain, then
-// pAppDomainToSearch may be NULL (to search all instantiations loaded shared),
-// or may be non-NULL (to search all instantiations loaded into
-// pAppDomainToSearch)
-// * If pModuleContainingMethodDef is not loaded domain-neutral, then
-// pAppDomainToSearch must be non-NULL (and, indeed, must be the very AD that
-// pModuleContainingMethodDef is loaded into).
-//
-
-HRESULT ReJitManager::MarkAllInstantiationsForReJit(
- SharedReJitInfo * pSharedForAllGenericInstantiations,
- AppDomain * pAppDomainToSearch,
- PTR_Module pModuleContainingMethodDef,
- mdMethodDef methodDef,
- ReJitManagerJumpStampBatch* pJumpStampBatch,
- CDynArray<ReJitReportErrorWorkItem> * pRejitErrors)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- CAN_TAKE_LOCK;
- PRECONDITION(CheckPointer(pSharedForAllGenericInstantiations));
- PRECONDITION(CheckPointer(pAppDomainToSearch, NULL_OK));
- PRECONDITION(CheckPointer(pModuleContainingMethodDef));
- PRECONDITION(CheckPointer(pJumpStampBatch));
- }
- CONTRACTL_END;
-
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
- _ASSERTE(methodDef != mdTokenNil);
- _ASSERTE(pJumpStampBatch->pReJitManager == this);
-
- HRESULT hr;
-
- BaseDomain * pDomainContainingGenericDefinition = pModuleContainingMethodDef->GetDomain();
-
-#ifdef _DEBUG
- // This function should only be called on the ReJitManager that owns the (generic)
- // definition of methodDef
- _ASSERTE(this == pDomainContainingGenericDefinition->GetReJitManager());
-
- // If the generic definition is not loaded domain-neutral, then all its
- // instantiations will also be non-domain-neutral and loaded into the same
- // domain as the generic definition. So the caller may only pass the
- // domain containing the generic definition as pAppDomainToSearch
- if (!pDomainContainingGenericDefinition->IsSharedDomain())
- {
- _ASSERTE(pDomainContainingGenericDefinition == pAppDomainToSearch);
- }
-#endif //_DEBUG
-
- // If pAppDomainToSearch is NULL, iterate through all existing
- // instantiations loaded into the SharedDomain. If pAppDomainToSearch is non-NULL,
- // iterate through all existing instantiations in pAppDomainToSearch, and only consider
- // instantiations in non-domain-neutral assemblies (as we already covered domain
- // neutral assemblies when we searched the SharedDomain).
- LoadedMethodDescIterator::AssemblyIterationMode mode = LoadedMethodDescIterator::kModeSharedDomainAssemblies;
- // these are the default flags which won't actually be used in shared mode other than
- // asserting they were specified with their default values
- AssemblyIterationFlags assemFlags = (AssemblyIterationFlags) (kIncludeLoaded | kIncludeExecution);
- ModuleIterationOption moduleFlags = (ModuleIterationOption) kModIterIncludeLoaded;
- if (pAppDomainToSearch != NULL)
- {
- mode = LoadedMethodDescIterator::kModeUnsharedADAssemblies;
- assemFlags = (AssemblyIterationFlags)(kIncludeAvailableToProfilers | kIncludeExecution);
- moduleFlags = (ModuleIterationOption)kModIterIncludeAvailableToProfilers;
- }
- LoadedMethodDescIterator it(
- pAppDomainToSearch,
- pModuleContainingMethodDef,
- methodDef,
- mode,
- assemFlags,
- moduleFlags);
- CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
- while (it.Next(pDomainAssembly.This()))
- {
- MethodDesc * pLoadedMD = it.Current();
-
- if (!pLoadedMD->HasNativeCode())
- {
- // Skip uninstantiated MethodDescs. The placeholder added by our caller
- // is sufficient to ensure they'll eventually be rejitted when they get
- // compiled.
- continue;
- }
-
- if (FAILED(hr = IsMethodSafeForReJit(pLoadedMD)))
- {
- if (FAILED(hr = AddReJITError(pModuleContainingMethodDef, methodDef, pLoadedMD, hr, pRejitErrors)))
- {
- _ASSERTE(hr == E_OUTOFMEMORY);
- return hr;
- }
- continue;
- }
-
-#ifdef _DEBUG
- if (!pDomainContainingGenericDefinition->IsSharedDomain())
- {
- // Method is defined outside of the shared domain, so its instantiation must
- // be defined in the AD we're iterating over (pAppDomainToSearch, which, as
- // asserted above, must be the same domain as the generic's definition)
- _ASSERTE(pLoadedMD->GetDomain() == pAppDomainToSearch);
- }
-#endif // _DEBUG
-
- // This will queue up the MethodDesc for rejitting and create all the
- // look-aside tables needed.
- SharedReJitInfo * pSharedUsed = NULL;
- hr = MarkForReJit(
- pLoadedMD,
- pSharedForAllGenericInstantiations,
- pJumpStampBatch,
- pRejitErrors,
- &pSharedUsed);
- if (FAILED(hr))
- {
- _ASSERTE(hr == E_OUTOFMEMORY);
- return hr;
- }
- }
-
- return S_OK;
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Helper used by ReJitManager::MarkAllInstantiationsForReJit and
-// ReJitManager::RequestReJIT to do the actual ReJitInfo allocation and
-// placement inside m_table. Note that callers don't use MarkForReJitHelper
-// directly. Instead, callers actually use the inlined overloaded wrappers
-// ReJitManager::MarkForReJit (one for placeholder (i.e., methodDef pre-rejit)
-// ReJitInfos and one for regular (i.e., MethodDesc) ReJitInfos). When the
-// overloaded MarkForReJit wrappers call this, they ensure that either pMD is
-// valid XOR (pModule, methodDef) is valid.
-//
-// Arguments:
-// * pMD - MethodDesc for which to find / create ReJitInfo. Only used if
-// we're creating a regular ReJitInfo
-// * pModule - Module for which to find / create ReJitInfo. Only used if
-// we're creating a placeholder ReJitInfo
-// * methodDef - methodDef for which to find / create ReJitInfo. Only used
-// if we're creating a placeholder ReJitInfo
-// * pSharedToReuse - SharedReJitInfo to associate any newly created
-// ReJitInfo with. If NULL, we'll create a new one.
-// * pJumpStampBatch - a batch of methods that need to have jump stamps added
-// or removed. This method will add new ReJitInfos to the batch as needed.
-// * pRejitErrors - An array of rejit errors that this call will append to
-// if there is an error marking
-// * ppSharedUsed - [out]: SharedReJitInfo used for this request. If
-// pSharedToReuse is non-NULL, *ppSharedUsed == pSharedToReuse. Else,
-// *ppSharedUsed is the SharedReJitInfo newly-created to associate with
-// the ReJitInfo used for this request.
-//
-// Return Value:
-// * S_OK: Successfully created a new ReJitInfo to manage this request
-// * S_FALSE: An existing ReJitInfo was already available to manage this
-// request, so we didn't need to create a new one.
-// * E_OUTOFMEMORY
-// * Else, a failure HRESULT indicating what went wrong.
-//
-
-HRESULT ReJitManager::MarkForReJitHelper(
- PTR_MethodDesc pMD,
- PTR_Module pModule,
- mdMethodDef methodDef,
- SharedReJitInfo * pSharedToReuse,
- ReJitManagerJumpStampBatch* pJumpStampBatch,
- CDynArray<ReJitReportErrorWorkItem> * pRejitErrors,
- /* out */ SharedReJitInfo ** ppSharedUsed)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- CAN_TAKE_LOCK;
- PRECONDITION(CheckPointer(pMD, NULL_OK));
- PRECONDITION(CheckPointer(pModule, NULL_OK));
- PRECONDITION(CheckPointer(pJumpStampBatch));
- PRECONDITION(CheckPointer(pRejitErrors));
- PRECONDITION(CheckPointer(ppSharedUsed, NULL_OK));
- }
- CONTRACTL_END;
-
- CrstHolder ch(&m_crstTable);
-
- // Either pMD is valid, xor (pModule,methodDef) is valid
- _ASSERTE(
- ((pMD != NULL) && (pModule == NULL) && (methodDef == mdTokenNil)) ||
- ((pMD == NULL) && (pModule != NULL) && (methodDef != mdTokenNil)));
- _ASSERTE(pJumpStampBatch->pReJitManager == this);
-
- if (ppSharedUsed != NULL)
- *ppSharedUsed = NULL;
- HRESULT hr = S_OK;
-
- // Check if there was there a previous rejit request for pMD
-
- ReJitInfoHash::KeyIterator beginIter(&m_table, TRUE /* begin */);
- ReJitInfoHash::KeyIterator endIter(&m_table, FALSE /* begin */);
-
- if (pMD != NULL)
- {
- beginIter = GetBeginIterator(pMD);
- endIter = GetEndIterator(pMD);
- }
- else
- {
- beginIter = GetBeginIterator(pModule, methodDef);
- endIter = GetEndIterator(pModule, methodDef);
- }
-
- for (ReJitInfoHash::KeyIterator iter = beginIter;
- iter != endIter;
- iter++)
- {
- ReJitInfo * pInfo = *iter;
- _ASSERTE(pInfo->m_pShared != NULL);
-
-#ifdef _DEBUG
- if (pMD != NULL)
- {
- _ASSERTE(pInfo->GetMethodDesc() == pMD);
- }
- else
- {
- Module * pModuleTest = NULL;
- mdMethodDef methodDefTest = mdTokenNil;
- pInfo->GetModuleAndToken(&pModuleTest, &methodDefTest);
- _ASSERTE((pModule == pModuleTest) && (methodDef == methodDefTest));
- }
-#endif //_DEBUG
-
- SharedReJitInfo * pShared = pInfo->m_pShared;
-
- switch (pShared->GetState())
- {
- case SharedReJitInfo::kStateRequested:
- // We can 'reuse' this instance because the profiler doesn't know about
- // it yet. (This likely happened because a profiler called RequestReJIT
- // twice in a row, without us having a chance to jmp-stamp the code yet OR
- // while iterating through instantiations of a generic, the iterator found
- // duplicate entries for the same instantiation.)
- _ASSERTE(pShared->m_pbIL == NULL);
- _ASSERTE(pInfo->m_pCode == NULL);
-
- if (ppSharedUsed != NULL)
- *ppSharedUsed = pShared;
-
- INDEBUG(AssertRestOfEntriesAreReverted(iter, endIter));
- return S_FALSE;
-
- case SharedReJitInfo::kStateGettingReJITParameters:
- case SharedReJitInfo::kStateActive:
+ if (rgHrStatuses != NULL)
{
- // Profiler has already requested to rejit this guy, AND we've already
- // at least started getting the rejit parameters from the profiler. We need to revert this
- // instance (this will put back the original code)
-
- INDEBUG(AssertRestOfEntriesAreReverted(iter, endIter));
- hr = Revert(pShared, pJumpStampBatch);
- if (FAILED(hr))
+ for (DWORD j = 0; j < cFunctions; j++)
{
- _ASSERTE(hr == E_OUTOFMEMORY);
- return hr;
+ if (rgMethodDefs[j] == errorRecords[i].methodDef &&
+ reinterpret_cast<Module*>(rgModuleIDs[j]) == errorRecords[i].pModule)
+ {
+ rgHrStatuses[j] = errorRecords[i].hrStatus;
+ }
}
- _ASSERTE(pShared->GetState() == SharedReJitInfo::kStateReverted);
-
- // No need to continue looping. Break out of loop to create a new
- // ReJitInfo to service the request.
- goto EXIT_LOOP;
- }
- case SharedReJitInfo::kStateReverted:
- // just ignore this guy
- continue;
-
- default:
- UNREACHABLE();
- }
- }
-EXIT_LOOP:
-
- // Either there was no ReJitInfo yet for this MethodDesc OR whatever we've found
- // couldn't be reused (and needed to be reverted). Create a new ReJitInfo to return
- // to the caller.
- //
- // If the caller gave us a pMD that is a new generic instantiation, then the caller
- // may also have provided a pSharedToReuse for the generic. Use that instead of
- // creating a new one.
-
- SharedReJitInfo * pShared = NULL;
-
- if (pSharedToReuse != NULL)
- {
- pShared = pSharedToReuse;
- }
- else
- {
- PTR_LoaderHeap pHeap = NULL;
- if (pModule != NULL)
- {
- pHeap = pModule->GetLoaderAllocator()->GetLowFrequencyHeap();
}
else
{
- pHeap = pMD->GetLoaderAllocator()->GetLowFrequencyHeap();
- }
- pShared = new (pHeap, nothrow) SharedReJitInfo;
- if (pShared == NULL)
- {
- return E_OUTOFMEMORY;
- }
- }
-
- _ASSERTE(pShared != NULL);
-
- // ReJitInfos with MethodDesc's need to be jump-stamped,
- // ReJitInfos with Module/MethodDef are placeholders that don't need a stamp
- ReJitInfo * pInfo = NULL;
- ReJitInfo ** ppInfo = &pInfo;
- if (pMD != NULL)
- {
- ppInfo = pJumpStampBatch->preStubMethods.Append();
- if (ppInfo == NULL)
- {
- return E_OUTOFMEMORY;
+ ReportReJITError(&(errorRecords[i]));
}
+
}
- hr = AddNewReJitInfo(pMD, pModule, methodDef, pShared, ppInfo);
- if (FAILED(hr))
- {
- // NOTE: We could consider using an AllocMemTracker or AllocMemHolder
- // here to back out the allocation of pShared, but it probably
- // wouldn't make much of a difference. We'll only get here if we ran
- // out of memory allocating the pInfo, so our memory has already been
- // blown. We can't cause much leaking due to this error path.
- _ASSERTE(hr == E_OUTOFMEMORY);
- return hr;
- }
-
- _ASSERTE(*ppInfo != NULL);
-
- if (ppSharedUsed != NULL)
- *ppSharedUsed = pShared;
+ // We got through processing everything, but profiler will need to see the individual ReJITError
+ // callbacks to know what, if anything, failed.
return S_OK;
}
-//---------------------------------------------------------------------------------------
-//
-// Helper used by the above helpers (and also during jump-stamping) to
-// allocate and store a new ReJitInfo.
-//
-// Arguments:
-// * pMD - MethodDesc for which to create ReJitInfo. Only used if we're
-// creating a regular ReJitInfo
-// * pModule - Module for which create ReJitInfo. Only used if we're
-// creating a placeholder ReJitInfo
-// * methodDef - methodDef for which to create ReJitInfo. Only used if
-// we're creating a placeholder ReJitInfo
-// * pShared - SharedReJitInfo to associate the newly created ReJitInfo
-// with.
-// * ppInfo - [out]: ReJitInfo created
-//
-// Return Value:
-// * S_OK: ReJitInfo successfully created & stored.
-// * Else, failure indicating the problem. Currently only E_OUTOFMEMORY.
-//
-// Assumptions:
-// * Caller should be holding this ReJitManager's table crst.
-//
-
-HRESULT ReJitManager::AddNewReJitInfo(
- PTR_MethodDesc pMD,
+// static
+HRESULT ReJitManager::BindILVersion(
+ CodeVersionManager* pCodeVersionManager,
PTR_Module pModule,
mdMethodDef methodDef,
- SharedReJitInfo * pShared,
- ReJitInfo ** ppInfo)
+ ILCodeVersion *pILCodeVersion)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
- MODE_ANY;
- CAN_TAKE_LOCK;
- PRECONDITION(CheckPointer(pMD, NULL_OK));
- PRECONDITION(CheckPointer(pModule, NULL_OK));
- PRECONDITION(CheckPointer(pShared));
- PRECONDITION(CheckPointer(ppInfo));
- }
- CONTRACTL_END;
-
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
- _ASSERTE(pShared->GetState() != SharedReJitInfo::kStateReverted);
-
- // Either pMD is valid, xor (pModule,methodDef) is valid
- _ASSERTE(
- ((pMD != NULL) && (pModule == NULL) && (methodDef == mdTokenNil)) ||
- ((pMD == NULL) && (pModule != NULL) && (methodDef != mdTokenNil)));
-
- HRESULT hr;
- ReJitInfo * pInfo = NULL;
-
- if (pMD != NULL)
- {
- PTR_LoaderHeap pHeap = pMD->GetLoaderAllocator()->GetLowFrequencyHeap();
- pInfo = new (pHeap, nothrow) ReJitInfo(pMD, pShared);
- }
- else
- {
- PTR_LoaderHeap pHeap = pModule->GetLoaderAllocator()->GetLowFrequencyHeap();
- pInfo = new (pHeap, nothrow) ReJitInfo(pModule, methodDef, pShared);
- }
- if (pInfo == NULL)
- {
- return E_OUTOFMEMORY;
- }
-
- hr = S_OK;
- EX_TRY
- {
- // This guy throws when out of memory, but remains internally
- // consistent (without adding the new element)
- m_table.Add(pInfo);
- }
- EX_CATCH_HRESULT(hr);
-
- _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY);
- if (FAILED(hr))
- {
- pInfo = NULL;
- return hr;
- }
-
- *ppInfo = pInfo;
- return S_OK;
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Given a MethodDesc, call ReJitInfo::JumpStampNativeCode to stamp the top of its
-// originally-jitted-code with a jmp that goes to the prestub. This is called by the
-// prestub worker after jitting the original code of a function (i.e., the "pre-rejit"
-// scenario). In this case, the EE is not suspended. But that's ok, because the PCODE has
-// not yet been published to the MethodDesc, and no thread can be executing inside the
-// originally JITted function yet.
-//
-// Arguments:
-// * pMD - MethodDesc to jmp-stamp
-// * pCode - Top of the code that was just jitted (using original IL).
-//
-//
-// Return value:
-// * S_OK: Either we successfully did the jmp-stamp, or we didn't have to (e.g., there
-// was no outstanding pre-rejit request for this MethodDesc, or a racing thread
-// took care of it for us).
-// * Else, HRESULT indicating failure.
-
-// Assumptions:
-// The caller has not yet published pCode to the MethodDesc, so no threads can be
-// executing inside pMD's code yet. Thus, we don't need to suspend the runtime while
-// applying the jump-stamp like we usually do for rejit requests that are made after
-// a function has been JITted.
-//
-
-HRESULT ReJitManager::DoJumpStampIfNecessary(MethodDesc* pMD, PCODE pCode)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
+ MODE_PREEMPTIVE;
CAN_TAKE_LOCK;
- PRECONDITION(CheckPointer(pMD));
- PRECONDITION(pCode != NULL);
+ PRECONDITION(CheckPointer(pCodeVersionManager));
+ PRECONDITION(CheckPointer(pModule));
+ PRECONDITION(CheckPointer(pILCodeVersion));
}
CONTRACTL_END;
- HRESULT hr;
-
- _ASSERTE(IsTableCrstOwnedByCurrentThread());
-
- ReJitInfo * pInfoToJumpStamp = NULL;
-
- // First, try looking up ReJitInfo by MethodDesc. A "regular" MethodDesc-based
- // ReJitInfo already exists for "case 1" (see comment above
- // code:ReJitInfo::JumpStampNativeCode), and could even exist for "case 2"
- // (pre-rejit), if either:
- // * The pre-rejit was requested after the MD had already been loaded (though
- // before it had been jitted) OR
- // * there was a race to JIT the original code for the MD, and another thread got
- // here before us and already added the ReJitInfo for that MD.
-
- ReJitInfoHash::KeyIterator beginIter = GetBeginIterator(pMD);
- ReJitInfoHash::KeyIterator endIter = GetEndIterator(pMD);
-
- pInfoToJumpStamp = FindPreReJittedReJitInfo(beginIter, endIter);
- if (pInfoToJumpStamp != NULL)
- {
- _ASSERTE(pInfoToJumpStamp->GetMethodDesc() == pMD);
- // does it need to be jump-stamped?
- if (pInfoToJumpStamp->GetState() != ReJitInfo::kJumpNone)
- {
- return S_OK;
- }
- else
- {
- return pInfoToJumpStamp->JumpStampNativeCode(pCode);
- }
- }
-
- // In this case, try looking up by module / metadata token. This is the case where
- // the pre-rejit request occurred before the MD was loaded.
-
- Module * pModule = pMD->GetModule();
- _ASSERTE(pModule != NULL);
- mdMethodDef methodDef = pMD->GetMemberDef();
-
- beginIter = GetBeginIterator(pModule, methodDef);
- endIter = GetEndIterator(pModule, methodDef);
- ReJitInfo * pInfoPlaceholder = NULL;
-
- pInfoPlaceholder = FindPreReJittedReJitInfo(beginIter, endIter);
- if (pInfoPlaceholder == NULL)
- {
- // No jump stamping to do.
- return S_OK;
- }
+ _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread());
+ _ASSERTE((pModule != NULL) && (methodDef != mdTokenNil));
- // The placeholder may already have a rejit info for this MD, in which
- // case we don't need to do any additional work
- for (ReJitInfo * pInfo = pInfoPlaceholder->m_pShared->GetMethods(); pInfo != NULL; pInfo = pInfo->m_pNext)
- {
- if ((pInfo->GetKey().m_keyType == ReJitInfo::Key::kMethodDesc) &&
- (pInfo->GetMethodDesc() == pMD))
- {
- // Any rejit info we find should already be jumpstamped
- _ASSERTE(pInfo->GetState() != ReJitInfo::kJumpNone);
- return S_OK;
- }
- }
+ // Check if there was there a previous rejit request for this method that hasn't been exposed back
+ // to the profiler yet
+ ILCodeVersion ilCodeVersion = pCodeVersionManager->GetActiveILCodeVersion(pModule, methodDef);
-#ifdef _DEBUG
+ if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateRequested)
{
- Module * pModuleTest = NULL;
- mdMethodDef methodDefTest = mdTokenNil;
- INDEBUG(pInfoPlaceholder->GetModuleAndToken(&pModuleTest, &methodDefTest));
- _ASSERTE((pModule == pModuleTest) && (methodDef == methodDefTest));
- }
-#endif //_DEBUG
+ // We can 'reuse' this instance because the profiler doesn't know about
+ // it yet. (This likely happened because a profiler called RequestReJIT
+ // twice in a row, without us having a chance to jmp-stamp the code yet OR
+ // while iterating through instantiations of a generic, the iterator found
+ // duplicate entries for the same instantiation.)
+ _ASSERTE(ilCodeVersion.GetILNoThrow() == NULL);
- // We have finished JITting the original code for a function that had been
- // "pre-rejitted" (i.e., requested to be rejitted before it was first compiled). So
- // now is the first time where we know the MethodDesc of the request.
- if (FAILED(hr = IsMethodSafeForReJit(pMD)))
- {
- // No jump stamping to do.
- return hr;
+ *pILCodeVersion = ilCodeVersion;
+ return S_FALSE;
}
- // Create the ReJitInfo associated with the MethodDesc now (pInfoToJumpStamp), and
- // jump-stamp the original code.
- pInfoToJumpStamp = NULL;
- hr = AddNewReJitInfo(pMD, NULL /*pModule*/, NULL /*methodDef*/, pInfoPlaceholder->m_pShared, &pInfoToJumpStamp);
- if (FAILED(hr))
- {
- return hr;
- }
-
- _ASSERTE(pInfoToJumpStamp != NULL);
- return pInfoToJumpStamp->JumpStampNativeCode(pCode);
+ // Either there was no ILCodeVersion yet for this MethodDesc OR whatever we've found
+ // couldn't be reused (and needed to be reverted). Create a new ILCodeVersion to return
+ // to the caller.
+ return pCodeVersionManager->AddILCodeVersion(pModule, methodDef, InterlockedIncrement(reinterpret_cast<LONG*>(&s_GlobalReJitId)), pILCodeVersion);
}
//---------------------------------------------------------------------------------------
@@ -1601,395 +754,41 @@ HRESULT ReJitManager::RequestRevert(
}
CONTRACTL_END;
- // Serialize all RequestReJIT() and Revert() calls against each other (even across AppDomains)
- CrstHolder ch(&(s_csGlobalRequest));
-
- // Request at least 1 method to revert!
- _ASSERTE ((cFunctions != 0) && (rgModuleIDs != NULL) && (rgMethodDefs != NULL));
-
- ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT);
- for (ULONG i = 0; i < cFunctions; i++)
- {
- HRESULT hr = E_UNEXPECTED;
- Module * pModule = reinterpret_cast< Module * >(rgModuleIDs[i]);
- if (pModule == NULL || TypeFromToken(rgMethodDefs[i]) != mdtMethodDef)
- {
- hr = E_INVALIDARG;
- }
- else if (pModule->IsBeingUnloaded())
- {
- hr = CORPROF_E_DATAINCOMPLETE;
- }
- else if (pModule->IsReflection())
- {
- hr = CORPROF_E_MODULE_IS_DYNAMIC;
- }
- else
- {
- hr = pModule->GetReJitManager()->RequestRevertByToken(pModule, rgMethodDefs[i]);
- }
-
- if (rgHrStatuses != NULL)
- {
- rgHrStatuses[i] = hr;
- }
- }
-
- ThreadSuspend::RestartEE(FALSE /* bFinishedGC */, TRUE /* SuspendSucceded */);
-
- return S_OK;
+ return UpdateActiveILVersions(cFunctions, rgModuleIDs, rgMethodDefs, rgHrStatuses, TRUE);
}
-//---------------------------------------------------------------------------------------
-//
-// Called by AppDomain::Exit() to notify the SharedDomain's ReJitManager that this
-// AppDomain is exiting. The SharedDomain's ReJitManager will then remove any
-// ReJitInfos relating to MDs owned by AppDomain. This is how we remove
-// non-domain-neutral instantiations of domain-neutral generics from the SharedDomain's
-// ReJitManager.
-//
-// Arguments:
-// pAppDomain - AppDomain that is exiting.
-//
-
// static
-void ReJitManager::OnAppDomainExit(AppDomain * pAppDomain)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- CAN_TAKE_LOCK;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- // All ReJitInfos and SharedReJitInfos for this AD's ReJitManager automatically get
- // cleaned up as they're allocated on the AD's loader heap.
-
- // We explicitly clean up the SHash here, as its entries get allocated using regular
- // "new"
- pAppDomain->GetReJitManager()->m_table.RemoveAll();
-
- // We need to ensure that any MethodDescs from pAppDomain that are stored on the
- // SharedDomain's ReJitManager get removed from the SharedDomain's ReJitManager's
- // hash table, and from the linked lists tied to their owning SharedReJitInfo. (This
- // covers the case of non-domain-neutral instantiations of domain-neutral generics.)
- SharedDomain::GetDomain()->GetReJitManager()->RemoveReJitInfosFromDomain(pAppDomain);
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Small helper to determine whether a given (possibly instantiated generic) MethodDesc
-// is safe to rejit. If not, this function is responsible for calling into the
-// profiler's ReJITError()
-//
-// Arguments:
-// pMD - MethodDesc to test
-// Return Value:
-// S_OK iff pMD is safe to rejit
-// CORPROF_E_FUNCTION_IS_COLLECTIBLE - function can't be rejitted because it is collectible
-//
-
-// static
-HRESULT ReJitManager::IsMethodSafeForReJit(PTR_MethodDesc pMD)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- CAN_TAKE_LOCK;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(pMD != NULL);
-
- // Weird, non-user functions were already weeded out in RequestReJIT(), and will
- // also never be passed to us by the prestub worker (for the pre-rejit case).
- _ASSERTE(pMD->IsIL());
-
- // Any MethodDescs that could be collected are not currently supported. Although we
- // rule out all Ref.Emit modules in RequestReJIT(), there can still exist types defined
- // in a non-reflection module and instantiated into a collectible assembly
- // (e.g., List<MyCollectibleStruct>). In the future we may lift this
- // restriction by updating the ReJitManager when the collectible assemblies
- // owning the instantiations get collected.
- if (pMD->GetLoaderAllocator()->IsCollectible())
- {
- return CORPROF_E_FUNCTION_IS_COLLECTIBLE;
- }
-
- return S_OK;
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Simple wrapper around GetCurrentReJitWorker. See
-// code:ReJitManager::GetCurrentReJitWorker for information about parameters, return
-// values, etc.
-
-// static
-DWORD ReJitManager::GetCurrentReJitFlags(PTR_MethodDesc pMD)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- PRECONDITION(CheckPointer(pMD));
- }
- CONTRACTL_END;
-
- return pMD->GetReJitManager()->GetCurrentReJitFlagsWorker(pMD);
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Given a methodDef token, finds the corresponding ReJitInfo, and asks the
-// ReJitInfo to perform a revert.
-//
-// Arguments:
-// * pModule - Module to revert
-// * methodDef - methodDef token to revert
-//
-// Return Value:
-// HRESULT indicating success or failure. If the method was never
-// rejitted in the first place, this method returns a special error code
-// (CORPROF_E_ACTIVE_REJIT_REQUEST_NOT_FOUND).
-// E_OUTOFMEMORY
-//
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4702) // Disable bogus unreachable code warning
-#endif // _MSC_VER
-HRESULT ReJitManager::RequestRevertByToken(PTR_Module pModule, mdMethodDef methodDef)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- CAN_TAKE_LOCK;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- _ASSERTE(ThreadStore::HoldingThreadStore());
- CrstHolder ch(&m_crstTable);
-
- _ASSERTE(pModule != NULL);
- _ASSERTE(methodDef != mdTokenNil);
-
- ReJitInfo * pInfo = NULL;
- MethodDesc * pMD = NULL;
-
- pInfo = FindNonRevertedReJitInfo(pModule, methodDef);
- if (pInfo == NULL)
- {
- pMD = pModule->LookupMethodDef(methodDef);
- pInfo = FindNonRevertedReJitInfo(pMD);
- if (pInfo == NULL)
- return CORPROF_E_ACTIVE_REJIT_REQUEST_NOT_FOUND;
- }
-
- _ASSERTE (pInfo != NULL);
- _ASSERTE (pInfo->m_pShared != NULL);
- _ASSERTE (pInfo->m_pShared->GetState() != SharedReJitInfo::kStateReverted);
- ReJitManagerJumpStampBatch batch(this);
- HRESULT hr = Revert(pInfo->m_pShared, &batch);
- if (FAILED(hr))
- {
- _ASSERTE(hr == E_OUTOFMEMORY);
- return hr;
- }
- CDynArray<ReJitReportErrorWorkItem> errorRecords;
- hr = BatchUpdateJumpStamps(&(batch.undoMethods), &(batch.preStubMethods), &errorRecords);
- if (FAILED(hr))
- {
- _ASSERTE(hr == E_OUTOFMEMORY);
- return hr;
- }
-
- // If there were any errors, return the first one. This matches previous error handling
- // behavior that only returned the first error encountered within Revert().
- for (int i = 0; i < errorRecords.Count(); i++)
- {
- _ASSERTE(FAILED(errorRecords[i].hrStatus));
- return errorRecords[i].hrStatus;
- }
- return S_OK;
-}
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif // _MSC_VER
-
-
-
-//---------------------------------------------------------------------------------------
-//
-// Called by the prestub worker, this function decides if the MethodDesc needs to be
-// rejitted, and if so, this will call the profiler to get the rejit parameters (if they
-// are not yet stored), and then perform the actual re-JIT (by calling, indirectly,
-// UnsafeJitFunction).
-//
-// In order to allow the re-JIT to occur outside of any locks, the following sequence is
-// performed:
-//
-// * Enter this ReJitManager's table crst
-// * Find the single ReJitInfo (if any) in the table matching the input pMD. This
-// represents the outstanding rejit request against thie pMD
-// * If necessary, ask profiler for IL & codegen flags (by calling
-// GetReJITParameters()), thus transitioning the corresponding SharedReJitInfo
-// state kStateRequested-->kStateActive
-// * Exit this ReJitManager's table crst
-// * (following steps occur when DoReJitIfNecessary() calls DoReJit())
-// * Call profiler's ReJitCompilationStarted()
-// * Call UnsafeJitFunction with the IL / codegen flags provided by profiler, as stored
-// on the SharedReJitInfo. Note that if another Rejit request came in, then we would
-// create new SharedReJitInfo & ReJitInfo structures to track it, rather than
-// modifying the ReJitInfo / SharedReJitInfo we found above. So the ReJitInfo we're
-// using here (outside the lock), is "fixed" in the sense that its IL / codegen flags
-// will not change.
-// * (below is where we handle any races that might have occurred between threads
-// simultaneously rejitting this function)
-// * Enter this ReJitManager's table crst
-// * Check to see if another thread has already published the rejitted PCODE to
-// ReJitInfo::m_pCode. If so, bail.
-// * If we're the winner, publish our rejitted PCODE to ReJitInfo::m_pCode...
-// * ...and update the jump-stamp at the top of the originally JITted code so that it
-// now points to our rejitted code (instead of the prestub)
-// * Exit this ReJitManager's table crst
-// * Call profiler's ReJitCompilationFinished()
-// * Fire relevant ETW events
-//
-// Arguments:
-// pMD - MethodDesc to decide whether to rejit
-//
-// Return Value:
-// * If a rejit was performed, the PCODE of the generated code.
-// * If the ReJitManager changed its mind and chose not to do a rejit (e.g., a
-// revert request raced with this rejit request, and the revert won), just
-// return the PCODE of the originally JITted code (pMD->GetNativeCode())
-// * Else, NULL (which means the ReJitManager doesn't know or care about this
-// MethodDesc)
-//
-
-PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD)
+HRESULT ReJitManager::ConfigureILCodeVersion(ILCodeVersion ilCodeVersion)
{
STANDARD_VM_CONTRACT;
- _ASSERTE(!IsTableCrstOwnedByCurrentThread());
+ CodeVersionManager* pCodeVersionManager = ilCodeVersion.GetModule()->GetCodeVersionManager();
+ _ASSERTE(!pCodeVersionManager->LockOwnedByCurrentThread());
- // Fast-path: If the rejit map is empty, no need to look up anything. Do this outside
- // of a lock to impact our caller (the prestub worker) as little as possible. If the
- // map is nonempty, we'll acquire the lock at that point and do the lookup for real.
- if (m_table.GetCount() == 0)
- {
- return NULL;
- }
HRESULT hr = S_OK;
- ReJitInfo * pInfoToRejit = NULL;
- Module* pModule = NULL;
- mdMethodDef methodDef = mdTokenNil;
+ Module* pModule = ilCodeVersion.GetModule();
+ mdMethodDef methodDef = ilCodeVersion.GetMethodDef();
BOOL fNeedsParameters = FALSE;
BOOL fWaitForParameters = FALSE;
{
- // Serialize access to the rejit table. Though once we find the ReJitInfo we want,
- // exit the Crst so we can ReJIT the method without holding a lock.
- CrstHolder ch(&m_crstTable);
-
- ReJitInfoHash::KeyIterator iter = GetBeginIterator(pMD);
- ReJitInfoHash::KeyIterator end = GetEndIterator(pMD);
-
- if (iter == end)
- {
- // No rejit actions necessary
- return NULL;
- }
-
-
- for (; iter != end; iter++)
+ // Serialize access to the rejit state
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
+ switch (ilCodeVersion.GetRejitState())
{
- ReJitInfo * pInfo = *iter;
- _ASSERTE(pInfo->GetMethodDesc() == pMD);
- _ASSERTE(pInfo->m_pShared != NULL);
- SharedReJitInfo * pShared = pInfo->m_pShared;
-
- switch (pShared->GetState())
- {
- case SharedReJitInfo::kStateRequested:
- if (pInfo->GetState() == ReJitInfo::kJumpNone)
- {
- // We haven't actually suspended threads and jump-stamped the
- // method's prolog so just ignore this guy
- INDEBUG(AssertRestOfEntriesAreReverted(iter, end));
- return NULL;
- }
- // When the SharedReJitInfo is still in the requested state, we haven't
- // gathered IL & codegen flags from the profiler yet. So, we can't be
- // pointing to rejitted code already. So we must be pointing to the prestub
- _ASSERTE(pInfo->GetState() == ReJitInfo::kJumpToPrestub);
-
- pInfo->GetModuleAndTokenRegardlessOfKeyType(&pModule, &methodDef);
- pShared->m_dwInternalFlags &= ~SharedReJitInfo::kStateMask;
- pShared->m_dwInternalFlags |= SharedReJitInfo::kStateGettingReJITParameters;
- pInfoToRejit = pInfo;
- fNeedsParameters = TRUE;
- break;
-
- case SharedReJitInfo::kStateGettingReJITParameters:
- if (pInfo->GetState() == ReJitInfo::kJumpNone)
- {
- // We haven't actually suspended threads and jump-stamped the
- // method's prolog so just ignore this guy
- INDEBUG(AssertRestOfEntriesAreReverted(iter, end));
- return NULL;
- }
- pInfoToRejit = pInfo;
- fWaitForParameters = TRUE;
- break;
-
- case SharedReJitInfo::kStateActive:
- INDEBUG(AssertRestOfEntriesAreReverted(iter, end));
- if (pInfo->GetState() == ReJitInfo::kJumpNone)
- {
- // We haven't actually suspended threads and jump-stamped the
- // method's prolog so just ignore this guy
- return NULL;
- }
- if (pInfo->GetState() == ReJitInfo::kJumpToRejittedCode)
- {
- // Looks like another thread has beat us in a race to rejit, so ignore.
- return NULL;
- }
-
- // Found a ReJitInfo to actually rejit.
- _ASSERTE(pInfo->GetState() == ReJitInfo::kJumpToPrestub);
- pInfoToRejit = pInfo;
- goto ExitLoop;
+ case ILCodeVersion::kStateRequested:
+ ilCodeVersion.SetRejitState(ILCodeVersion::kStateGettingReJITParameters);
+ fNeedsParameters = TRUE;
+ break;
- case SharedReJitInfo::kStateReverted:
- // just ignore this guy
- continue;
+ case ILCodeVersion::kStateGettingReJITParameters:
+ fWaitForParameters = TRUE;
+ break;
- default:
- UNREACHABLE();
- }
+ default:
+ return S_OK;
}
- ExitLoop:
- ;
- }
-
- if (pInfoToRejit == NULL)
- {
- // Didn't find the requested MD to rejit.
- return NULL;
}
if (fNeedsParameters)
@@ -2021,33 +820,39 @@ PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD)
if (FAILED(hr))
{
{
- CrstHolder ch(&m_crstTable);
- if (pInfoToRejit->m_pShared->m_dwInternalFlags == SharedReJitInfo::kStateGettingReJITParameters)
+ // Historically on failure we would revert to the kRequested state and fall-back
+ // to the initial code gen. The next time the method ran it would try again.
+ //
+ // Preserving that behavior is possible, but a bit awkward now that we have
+ // Precode swapping as well. Instead of doing that I am acting as if GetReJITParameters
+ // had succeeded, using the original IL, no jit flags, and no modified IL mapping.
+ // This is similar to a fallback except the profiler won't get any further attempts
+ // to provide the parameters correctly. If the profiler wants another attempt it would
+ // need to call RequestRejit again.
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
+ if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateGettingReJITParameters)
{
- pInfoToRejit->m_pShared->m_dwInternalFlags &= ~SharedReJitInfo::kStateMask;
- pInfoToRejit->m_pShared->m_dwInternalFlags |= SharedReJitInfo::kStateRequested;
+ ilCodeVersion.SetRejitState(ILCodeVersion::kStateActive);
+ ilCodeVersion.SetIL(ILCodeVersion(pModule, methodDef).GetIL());
}
}
- ReportReJITError(pModule, methodDef, pMD, hr);
- return NULL;
+ ReportReJITError(pModule, methodDef, pModule->LookupMethodDef(methodDef), hr);
+ return S_OK;
}
-
+ else
{
- CrstHolder ch(&m_crstTable);
- if (pInfoToRejit->m_pShared->m_dwInternalFlags == SharedReJitInfo::kStateGettingReJITParameters)
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
+ if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateGettingReJITParameters)
{
// Inside the above call to ICorProfilerCallback4::GetReJITParameters, the profiler
// will have used the specified pFuncControl to provide its IL and codegen flags.
// So now we transfer it out to the SharedReJitInfo.
- pInfoToRejit->m_pShared->m_dwCodegenFlags = pFuncControl->GetCodegenFlags();
- pInfoToRejit->m_pShared->m_pbIL = pFuncControl->GetIL();
- // pShared is now the owner of the memory for the IL buffer
- pInfoToRejit->m_pShared->m_instrumentedILMap.SetMappingInfo(pFuncControl->GetInstrumentedMapEntryCount(),
+ ilCodeVersion.SetJitFlags(pFuncControl->GetCodegenFlags());
+ ilCodeVersion.SetIL((COR_ILMETHOD*)pFuncControl->GetIL());
+ // ilCodeVersion is now the owner of the memory for the IL buffer
+ ilCodeVersion.SetInstrumentedILMap(pFuncControl->GetInstrumentedMapEntryCount(),
pFuncControl->GetInstrumentedMapEntries());
- pInfoToRejit->m_pShared->m_dwInternalFlags &= ~SharedReJitInfo::kStateMask;
- pInfoToRejit->m_pShared->m_dwInternalFlags |= SharedReJitInfo::kStateActive;
- _ASSERTE(pInfoToRejit->m_pCode == NULL);
- _ASSERTE(pInfoToRejit->GetState() == ReJitInfo::kJumpToPrestub);
+ ilCodeVersion.SetRejitState(ILCodeVersion::kStateActive);
}
}
}
@@ -2077,568 +882,22 @@ PCODE ReJitManager::DoReJitIfNecessaryWorker(PTR_MethodDesc pMD)
while (true)
{
{
- CrstHolder ch(&m_crstTable);
- if (pInfoToRejit->m_pShared->GetState() == SharedReJitInfo::kStateActive)
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
+ if (ilCodeVersion.GetRejitState() == ILCodeVersion::kStateActive)
{
break; // the other thread got the parameters succesfully, go race to rejit
}
- else if (pInfoToRejit->m_pShared->GetState() == SharedReJitInfo::kStateRequested)
- {
- return NULL; // the other thread had an error getting parameters and went
- // back to requested
- }
- else if (pInfoToRejit->m_pShared->GetState() == SharedReJitInfo::kStateReverted)
- {
- break; // we got reverted, enter DoReJit anyways and it will detect this and
- // bail out.
- }
}
ClrSleepEx(1, FALSE);
}
}
-
- // We've got the info from the profiler, so JIT the method. This is also
- // responsible for updating the jump target from the prestub to the newly
- // rejitted code AND for publishing the top of the newly rejitted code to
- // pInfoToRejit->m_pCode. If two threads race to rejit, DoReJit handles the
- // race, and ensures the winner publishes his result to pInfoToRejit->m_pCode.
- return DoReJit(pInfoToRejit);
-
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Called by DoReJitIfNecessaryWorker(), this function assumes the IL & codegen flags have
-// already been gathered from the profiler, and then calls UnsafeJitFunction to perform
-// the re-JIT (bracketing that with profiler callbacks to announce the start/finish of
-// the rejit).
-//
-// This is also responsible for handling any races between multiple threads
-// simultaneously rejitting a function. See the comment at the top of
-// code:ReJitManager::DoReJitIfNecessaryWorker for details.
-//
-// Arguments:
-// pInfo - ReJitInfo tracking this MethodDesc's rejit request
-//
-// Return Value:
-// * Generally, return the PCODE of the start of the rejitted code. However,
-// depending on the result of races determined by DoReJit(), the return value
-// can be different:
-// * If the current thread races with another thread to do the rejit, return the
-// PCODE generated by the winner.
-// * If the current thread races with another thread doing a revert, and the revert
-// wins, then return the PCODE of the start of the originally JITted code
-// (i.e., pInfo->GetMethodDesc()->GetNativeCode())
-//
-
-PCODE ReJitManager::DoReJit(ReJitInfo * pInfo)
-{
- STANDARD_VM_CONTRACT;
-
-#ifdef PROFILING_SUPPORTED
-
- INDEBUG(Dump("Inside DoRejit(). Dumping this ReJitManager\n"));
-
- _ASSERTE(!pInfo->GetMethodDesc()->IsNoMetadata());
- {
- BEGIN_PIN_PROFILER(CORProfilerTrackJITInfo());
- g_profControlBlock.pProfInterface->ReJITCompilationStarted((FunctionID)pInfo->GetMethodDesc(),
- pInfo->m_pShared->GetId(),
- TRUE);
- END_PIN_PROFILER();
- }
-
- COR_ILMETHOD_DECODER ILHeader(pInfo->GetIL(), pInfo->GetMethodDesc()->GetMDImport(), NULL);
- PCODE pCodeOfRejittedCode = NULL;
-
- // Note that we're intentionally not enclosing UnsafeJitFunction in a try block
- // to swallow exceptions. It's expected that any exception thrown is fatal and
- // should pass through. This is in contrast to MethodDesc::MakeJitWorker, which
- // does enclose UnsafeJitFunction in a try block, and attempts to swallow an
- // exception that occurs on the current thread when another thread has
- // simultaneously attempted (and provably succeeded in) the JITting of the same
- // function. This is a very unusual case (likely due to an out of memory error
- // encountered on the current thread and not on the competing thread), which is
- // not worth attempting to cover.
- pCodeOfRejittedCode = UnsafeJitFunction(
- pInfo->GetMethodDesc(),
- &ILHeader,
- JitFlagsFromProfCodegenFlags(pInfo->m_pShared->m_dwCodegenFlags));
-
- _ASSERTE(pCodeOfRejittedCode != NULL);
-
- // This atomically updates the jmp target (from prestub to top of rejitted code) and publishes
- // the top of rejitted code into pInfo, all inside the same acquisition of this
- // ReJitManager's table Crst.
- HRESULT hr = S_OK;
- BOOL fEESuspended = FALSE;
- BOOL fNotify = FALSE;
- PCODE ret = NULL;
- while (true)
- {
- if (fEESuspended)
- {
- ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT);
- }
- CrstHolder ch(&m_crstTable);
-
- // Now that we're under the lock, recheck whether pInfo->m_pCode has been filled
- // in...
- if (pInfo->m_pCode != NULL)
- {
- // Yup, another thread rejitted this request at the same time as us, and beat
- // us to publishing the result. Intentionally skip the rest of this, and do
- // not issue a ReJITCompilationFinished from this thread.
- ret = pInfo->m_pCode;
- break;
- }
-
- // BUGBUG: This revert check below appears to introduce behavior we probably don't want.
- // This is a pre-existing issue and I don't have time to create a test for this right now,
- // but wanted to capture the issue in a comment for future work.
- // Imagine the profiler has one thread which is calling RequestReJIT periodically
- // updating the method's IL:
- // 1) RequestReJit (table lock keeps these atomic)
- // 1.1) Revert old shared rejit info
- // 1.2) Create new shared rejit info
- // 2) RequestReJit (table lock keeps these atomic)
- // 2.1) Revert old shared rejit info
- // 2.2) Create new shared rejit info
- // ...
- // On a second thread we keep calling the method which needs to periodically rejit
- // to update to the newest version:
- // a) [DoReJitIfNecessaryWorker] detects active rejit request
- // b) [DoReJit] if shared rejit info is reverted, execute original method code.
- //
- // Because (a) and (b) are not under the same lock acquisition this ordering is possible:
- // (1), (a), (2), (b)
- // The result is that (b) sees the shared rejit is reverted and the method executes its
- // original code. As a profiler using rejit I would expect either the IL specified in
- // (1) or the IL specified in (2) would be used, but never the original IL.
- //
- // I think the correct behavior is to bind a method execution to the current rejit
- // version at some point, and from then on we guarantee to execute that version of the
- // code, regardless of reverts or re-rejit request.
- //
- // There is also a related issue with GetCurrentReJitFlagsWorker which assumes jitting
- // always corresponds to the most recent version of the method. If we start pinning
- // method invocations to particular versions then that method can't be allowed to
- // float forward to the newest version, nor can it abort if the most recent version
- // is reverted.
- // END BUGBUG
- //
- // And recheck whether some other thread tried to revert this method in the
- // meantime (this check would also include an attempt to re-rejit the method
- // (i.e., calling RequestReJIT on the method multiple times), which would revert
- // this pInfo before creating a new one to track the latest rejit request).
- if (pInfo->m_pShared->GetState() == SharedReJitInfo::kStateReverted)
- {
- // Yes, we've been reverted, so the jmp-to-prestub has already been removed,
- // and we should certainly not attempt to redirect that nonexistent jmp to
- // the code we just rejitted
- _ASSERTE(pInfo->GetMethodDesc()->GetNativeCode() != NULL);
- ret = pInfo->GetMethodDesc()->GetNativeCode();
- break;
- }
-
-#ifdef DEBUGGING_SUPPORTED
- // Notify the debugger of the rejitted function, so it can generate
- // DebuggerMethodInfo / DebugJitInfo for it. Normally this is done inside
- // UnsafeJitFunction (via CallCompileMethodWithSEHWrapper), but it skips this
- // when it detects the MethodDesc was already jitted. Since we know here that
- // we're rejitting it (and this is not just some sort of multi-thread JIT race),
- // now is a good place to notify the debugger.
- if (g_pDebugInterface != NULL)
- {
- g_pDebugInterface->JITComplete(pInfo->GetMethodDesc(), pCodeOfRejittedCode);
- }
-
-#endif // DEBUGGING_SUPPORTED
-
- _ASSERTE(pInfo->m_pShared->GetState() == SharedReJitInfo::kStateActive);
- _ASSERTE(pInfo->GetState() == ReJitInfo::kJumpToPrestub);
-
- // Atomically publish the PCODE and update the jmp stamp (to go to the rejitted
- // code) under the lock
- hr = pInfo->UpdateJumpTarget(fEESuspended, pCodeOfRejittedCode);
- if (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED)
- {
- _ASSERTE(!fEESuspended);
- fEESuspended = TRUE;
- continue;
- }
- if (FAILED(hr))
- {
- break;
- }
- pInfo->m_pCode = pCodeOfRejittedCode;
- fNotify = TRUE;
- ret = pCodeOfRejittedCode;
-
- _ASSERTE(pInfo->m_pShared->GetState() == SharedReJitInfo::kStateActive);
- _ASSERTE(pInfo->GetState() == ReJitInfo::kJumpToRejittedCode);
- break;
- }
-
- if (fEESuspended)
- {
- ThreadSuspend::RestartEE(FALSE /* bFinishedGC */, TRUE /* SuspendSucceded */);
- fEESuspended = FALSE;
- }
-
- if (FAILED(hr))
- {
- Module* pModule = NULL;
- mdMethodDef methodDef = mdTokenNil;
- pInfo->GetModuleAndTokenRegardlessOfKeyType(&pModule, &methodDef);
- ReportReJITError(pModule, methodDef, pInfo->GetMethodDesc(), hr);
- }
-
- // Notify the profiler that JIT completed.
- if (fNotify)
- {
- BEGIN_PIN_PROFILER(CORProfilerTrackJITInfo());
- g_profControlBlock.pProfInterface->ReJITCompilationFinished((FunctionID)pInfo->GetMethodDesc(),
- pInfo->m_pShared->GetId(),
- S_OK,
- TRUE);
- END_PIN_PROFILER();
- }
-#endif // PROFILING_SUPPORTED
-
- // Fire relevant ETW events
- if (fNotify)
- {
- ETW::MethodLog::MethodJitted(
- pInfo->GetMethodDesc(),
- NULL, // namespaceOrClassName
- NULL, // methodName
- NULL, // methodSignature
- pCodeOfRejittedCode,
- pInfo->m_pShared->GetId());
- }
- return ret;
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Transition SharedReJitInfo to Reverted state and add all associated ReJitInfos to the
-// undo list in the method batch
-//
-// Arguments:
-// pShared - SharedReJitInfo to revert
-// pJumpStampBatch - a batch of methods that need their jump stamps reverted. This method
-// is responsible for adding additional ReJitInfos to the list.
-//
-// Return Value:
-// S_OK if all MDs are batched and the SharedReJitInfo is marked reverted
-// E_OUTOFMEMORY (MDs couldn't be added to batch, SharedReJitInfo is not reverted)
-//
-// Assumptions:
-// Caller must be holding this ReJitManager's table crst.
-//
-
-HRESULT ReJitManager::Revert(SharedReJitInfo * pShared, ReJitManagerJumpStampBatch* pJumpStampBatch)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
- _ASSERTE((pShared->GetState() == SharedReJitInfo::kStateRequested) ||
- (pShared->GetState() == SharedReJitInfo::kStateGettingReJITParameters) ||
- (pShared->GetState() == SharedReJitInfo::kStateActive));
- _ASSERTE(pShared->GetMethods() != NULL);
- _ASSERTE(pJumpStampBatch->pReJitManager == this);
-
- HRESULT hrReturn = S_OK;
- for (ReJitInfo * pInfo = pShared->GetMethods(); pInfo != NULL; pInfo = pInfo->m_pNext)
- {
- if (pInfo->GetState() == ReJitInfo::kJumpNone)
- {
- // Nothing to revert for this MethodDesc / instantiation.
- continue;
- }
-
- ReJitInfo** ppInfo = pJumpStampBatch->undoMethods.Append();
- if (ppInfo == NULL)
- {
- return E_OUTOFMEMORY;
- }
- *ppInfo = pInfo;
- }
-
- pShared->m_dwInternalFlags &= ~SharedReJitInfo::kStateMask;
- pShared->m_dwInternalFlags |= SharedReJitInfo::kStateReverted;
+
return S_OK;
}
-
-//---------------------------------------------------------------------------------------
-//
-// Removes any ReJitInfos relating to MDs for the specified AppDomain from this
-// ReJitManager. This is used to remove non-domain-neutral instantiations of
-// domain-neutral generics from the SharedDomain's ReJitManager, when the AppDomain
-// containing those non-domain-neutral instantiations is unloaded.
-//
-// Arguments:
-// * pAppDomain - AppDomain that is exiting, and is thus the one for which we should
-// find ReJitInfos to remove
-//
-//
-
-void ReJitManager::RemoveReJitInfosFromDomain(AppDomain * pAppDomain)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- CAN_TAKE_LOCK;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- CrstHolder ch(&m_crstTable);
-
- INDEBUG(Dump("Dumping SharedDomain rejit manager BEFORE AD Unload"));
-
- for (ReJitInfoHash::Iterator iterCur = m_table.Begin(), iterEnd = m_table.End();
- iterCur != iterEnd;
- iterCur++)
- {
- ReJitInfo * pInfo = *iterCur;
-
- if (pInfo->m_key.m_keyType != ReJitInfo::Key::kMethodDesc)
- {
- // Skip all "placeholder" ReJitInfos--they'll always be allocated on a
- // loader heap for the shared domain.
- _ASSERTE(pInfo->m_key.m_keyType == ReJitInfo::Key::kMetadataToken);
- _ASSERTE(PTR_Module(pInfo->m_key.m_pModule)->GetDomain()->IsSharedDomain());
- continue;
- }
-
- if (pInfo->GetMethodDesc()->GetDomain() != pAppDomain)
- {
- // We only care about non-domain-neutral instantiations that live in
- // pAppDomain.
- continue;
- }
-
- // Remove this ReJitInfo from the linked-list of ReJitInfos associated with its
- // SharedReJitInfo.
- pInfo->m_pShared->RemoveMethod(pInfo);
-
- // Remove this ReJitInfo from the ReJitManager's hash table.
- m_table.Remove(iterCur);
-
- // pInfo is not deallocated yet. That will happen when pAppDomain finishes
- // unloading and its loader heaps get freed.
- }
- INDEBUG(Dump("Dumping SharedDomain rejit manager AFTER AD Unload"));
-}
-
#endif // DACCESS_COMPILE
// The rest of the ReJitManager methods are safe to compile for DAC
-
-//---------------------------------------------------------------------------------------
-//
-// Helper to iterate through m_table, finding the single matching non-reverted ReJitInfo.
-// The caller may search either by MethodDesc * XOR by (Module *, methodDef) pair.
-//
-// Arguments:
-// * pMD - MethodDesc * to search for. (NULL if caller is searching by (Module *,
-// methodDef)
-// * pModule - Module * to search for. (NULL if caller is searching by MethodDesc *)
-// * methodDef - methodDef to search for. (NULL if caller is searching by MethodDesc
-// *)
-//
-// Return Value:
-// ReJitInfo * requested, or NULL if none is found
-//
-// Assumptions:
-// Caller should be holding this ReJitManager's table crst.
-//
-
-PTR_ReJitInfo ReJitManager::FindNonRevertedReJitInfoHelper(
- PTR_MethodDesc pMD,
- PTR_Module pModule,
- mdMethodDef methodDef)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- }
- CONTRACTL_END;
-
- // Either pMD is valid, xor (pModule,methodDef) is valid
- _ASSERTE(
- ((pMD != NULL) && (pModule == NULL) && (methodDef == mdTokenNil)) ||
- ((pMD == NULL) && (pModule != NULL) && (methodDef != mdTokenNil)));
-
- // Caller should hold the Crst around calling this function and using the ReJitInfo.
-#ifndef DACCESS_COMPILE
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
-#endif
-
- ReJitInfoHash::KeyIterator beginIter(&m_table, TRUE /* begin */);
- ReJitInfoHash::KeyIterator endIter(&m_table, FALSE /* begin */);
-
- if (pMD != NULL)
- {
- beginIter = GetBeginIterator(pMD);
- endIter = GetEndIterator(pMD);
- }
- else
- {
- beginIter = GetBeginIterator(pModule, methodDef);
- endIter = GetEndIterator(pModule, methodDef);
- }
-
- for (ReJitInfoHash::KeyIterator iter = beginIter;
- iter != endIter;
- iter++)
- {
- PTR_ReJitInfo pInfo = *iter;
- _ASSERTE(pInfo->m_pShared != NULL);
-
- if (pInfo->m_pShared->GetState() == SharedReJitInfo::kStateReverted)
- continue;
-
- INDEBUG(AssertRestOfEntriesAreReverted(iter, endIter));
- return pInfo;
- }
-
- return NULL;
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// ReJitManager instance constructor--for now, does nothing
-//
-
-ReJitManager::ReJitManager()
-{
- LIMITED_METHOD_DAC_CONTRACT;
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Called from BaseDomain::BaseDomain to do any constructor-time initialization.
-// Presently, this takes care of initializing the Crst, choosing the type based on
-// whether this ReJitManager belongs to the SharedDomain.
-//
-// Arguments:
-// * fSharedDomain - nonzero iff this ReJitManager belongs to the SharedDomain.
-//
-
-void ReJitManager::PreInit(BOOL fSharedDomain)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- CAN_TAKE_LOCK;
- MODE_ANY;
- }
- CONTRACTL_END;
-
-#ifndef DACCESS_COMPILE
- m_crstTable.Init(
- fSharedDomain ? CrstReJITSharedDomainTable : CrstReJITDomainTable,
- CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN));
-#endif // DACCESS_COMPILE
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Finds the ReJitInfo tracking a pre-rejit request.
-//
-// Arguments:
-// * beginIter - Iterator to start search
-// * endIter - Iterator to end search
-//
-// Return Value:
-// NULL if no such ReJitInfo exists. This can occur if two thread race
-// to JIT the original code and we're the loser. Else, the ReJitInfo * found.
-//
-// Assumptions:
-// Caller must be holding this ReJitManager's table lock.
-//
-
-ReJitInfo * ReJitManager::FindPreReJittedReJitInfo(
- ReJitInfoHash::KeyIterator beginIter,
- ReJitInfoHash::KeyIterator endIter)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- // Caller shouldn't be handing out iterators unless he's already locking the table.
-#ifndef DACCESS_COMPILE
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
-#endif
-
- for (ReJitInfoHash::KeyIterator iter = beginIter;
- iter != endIter;
- iter++)
- {
- ReJitInfo * pInfo = *iter;
- SharedReJitInfo * pShared = pInfo->m_pShared;
- _ASSERTE(pShared != NULL);
-
- switch (pShared->GetState())
- {
- case SharedReJitInfo::kStateRequested:
- case SharedReJitInfo::kStateGettingReJITParameters:
- case SharedReJitInfo::kStateActive:
- if (pInfo->GetState() == ReJitInfo::kJumpToRejittedCode)
- {
- // There was a race for the original JIT, and we're the loser. (The winner
- // has already published the original JIT's pcode, jump-stamped, and begun
- // the rejit!)
- return NULL;
- }
-
- // Otherwise, either we have a rejit request that has not yet been
- // jump-stamped, or there was a race for the original JIT, and another
- // thread jump-stamped its copy of the originally JITted code already. In
- // that case, we still don't know who the winner or loser will be (PCODE may
- // not yet be published), so we'll have to jump-stamp our copy just in case
- // we win.
- _ASSERTE((pInfo->GetState() == ReJitInfo::kJumpNone) ||
- (pInfo->GetState() == ReJitInfo::kJumpToPrestub));
- INDEBUG(AssertRestOfEntriesAreReverted(iter, endIter));
- return pInfo;
-
-
- case SharedReJitInfo::kStateReverted:
- // just ignore this guy
- continue;
-
- default:
- UNREACHABLE();
- }
- }
-
- return NULL;
-}
-
//---------------------------------------------------------------------------------------
//
// Used by profiler to get the ReJITID corrseponding to a (MethodDesc *, PCODE) pair.
@@ -2654,7 +913,7 @@ ReJitInfo * ReJitManager::FindPreReJittedReJitInfo(
// 0 if no such ReJITID found (e.g., PCODE is from a JIT and not a rejit), else the
// ReJITID requested.
//
-
+// static
ReJITID ReJitManager::GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart)
{
CONTRACTL
@@ -2662,7 +921,6 @@ ReJITID ReJitManager::GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart)
NOTHROW;
CAN_TAKE_LOCK;
GC_TRIGGERS;
- INSTANCE_CHECK;
PRECONDITION(CheckPointer(pMD));
PRECONDITION(pCodeStart != NULL);
}
@@ -2671,14 +929,14 @@ ReJITID ReJitManager::GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart)
// Fast-path: If the rejit map is empty, no need to look up anything. Do this outside
// of a lock to impact our caller (the prestub worker) as little as possible. If the
// map is nonempty, we'll acquire the lock at that point and do the lookup for real.
- if (m_table.GetCount() == 0)
+ CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
+ if (pCodeVersionManager->GetNonDefaultILVersionCount() == 0)
{
return 0;
}
- CrstHolder ch(&m_crstTable);
-
- return GetReJitIdNoLock(pMD, pCodeStart);
+ CodeVersionManager::TableLockHolder ch(pCodeVersionManager);
+ return ReJitManager::GetReJitIdNoLock(pMD, pCodeStart);
}
//---------------------------------------------------------------------------------------
@@ -2699,221 +957,21 @@ ReJITID ReJitManager::GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart)
NOTHROW;
CANNOT_TAKE_LOCK;
GC_NOTRIGGER;
- INSTANCE_CHECK;
PRECONDITION(CheckPointer(pMD));
PRECONDITION(pCodeStart != NULL);
}
CONTRACTL_END;
// Caller must ensure this lock is taken!
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
+ CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
+ _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread());
- ReJitInfo * pInfo = FindReJitInfo(pMD, pCodeStart, 0);
- if (pInfo == NULL)
+ NativeCodeVersion nativeCodeVersion = pCodeVersionManager->GetNativeCodeVersion(pMD, pCodeStart);
+ if (nativeCodeVersion.IsNull())
{
return 0;
}
-
- _ASSERTE(pInfo->m_pShared->GetState() == SharedReJitInfo::kStateActive ||
- pInfo->m_pShared->GetState() == SharedReJitInfo::kStateReverted);
- return pInfo->m_pShared->GetId();
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Used by profilers to map a (MethodDesc *, ReJITID) pair to the corresponding PCODE for
-// that rejit attempt. This can also be used for reverted methods, as the PCODE may still
-// be available and in use even after a rejitted function has been reverted.
-//
-// Arguments:
-// * pMD - MethodDesc * of interest
-// * reJitId - ReJITID of interest
-//
-// Return Value:
-// Corresponding PCODE of the rejit attempt, or NULL if no such rejit attempt can be
-// found.
-//
-
-PCODE ReJitManager::GetCodeStart(PTR_MethodDesc pMD, ReJITID reJitId)
-{
- CONTRACTL
- {
- NOTHROW;
- CAN_TAKE_LOCK;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- PRECONDITION(CheckPointer(pMD));
- PRECONDITION(reJitId != 0);
- }
- CONTRACTL_END;
-
- // Fast-path: If the rejit map is empty, no need to look up anything. Do this outside
- // of a lock to impact our caller (the prestub worker) as little as possible. If the
- // map is nonempty, we'll acquire the lock at that point and do the lookup for real.
- if (m_table.GetCount() == 0)
- {
- return NULL;
- }
-
- CrstHolder ch(&m_crstTable);
-
- ReJitInfo * pInfo = FindReJitInfo(pMD, NULL, reJitId);
- if (pInfo == NULL)
- {
- return NULL;
- }
-
- _ASSERTE(pInfo->m_pShared->GetState() == SharedReJitInfo::kStateActive ||
- pInfo->m_pShared->GetState() == SharedReJitInfo::kStateReverted);
-
- return pInfo->m_pCode;
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// If a function has been requested to be rejitted, finds the one current
-// SharedReJitInfo (ignoring all that are in the reverted state) and returns the codegen
-// flags recorded on it (which were thus used to rejit the MD). CEEInfo::canInline() calls
-// this as part of its calculation of whether it may inline a given method. (Profilers
-// may specify on a per-rejit-request basis whether the rejit of a method may inline
-// callees.)
-//
-// Arguments:
-// * pMD - MethodDesc * of interest.
-//
-// Return Value:
-// Returns the requested codegen flags, or 0 (i.e., no flags set) if no rejit attempt
-// can be found for the MD.
-//
-
-DWORD ReJitManager::GetCurrentReJitFlagsWorker(PTR_MethodDesc pMD)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- PRECONDITION(CheckPointer(pMD));
- }
- CONTRACTL_END;
-
- // Fast-path: If the rejit map is empty, no need to look up anything. Do this outside
- // of a lock to impact our caller (e.g., the JIT asking if it can inline) as little as possible. If the
- // map is nonempty, we'll acquire the lock at that point and do the lookup for real.
- if (m_table.GetCount() == 0)
- {
- return 0;
- }
-
- CrstHolder ch(&m_crstTable);
-
- for (ReJitInfoHash::KeyIterator iter = GetBeginIterator(pMD), end = GetEndIterator(pMD);
- iter != end;
- iter++)
- {
- ReJitInfo * pInfo = *iter;
- _ASSERTE(pInfo->GetMethodDesc() == pMD);
- _ASSERTE(pInfo->m_pShared != NULL);
-
- DWORD dwState = pInfo->m_pShared->GetState();
-
- if (dwState != SharedReJitInfo::kStateActive)
- {
- // Not active means we never asked profiler for the codegen flags OR the
- // rejit request has been reverted. So this one is useless.
- continue;
- }
-
- // Found it!
-#ifdef _DEBUG
- // This must be the only such ReJitInfo for this MethodDesc. Check the rest and
- // assert otherwise.
- {
- ReJitInfoHash::KeyIterator iterTest = iter;
- iterTest++;
-
- while(iterTest != end)
- {
- ReJitInfo * pInfoTest = *iterTest;
- _ASSERTE(pInfoTest->GetMethodDesc() == pMD);
- _ASSERTE(pInfoTest->m_pShared != NULL);
-
- DWORD dwStateTest = pInfoTest->m_pShared->GetState();
-
- if (dwStateTest == SharedReJitInfo::kStateActive)
- {
- _ASSERTE(!"Multiple active ReJitInfos for same MethodDesc");
- break;
- }
- iterTest++;
- }
- }
-#endif //_DEBUG
- return pInfo->m_pShared->m_dwCodegenFlags;
- }
-
- return 0;
-}
-
-//---------------------------------------------------------------------------------------
-//
-// Helper to find the matching ReJitInfo by methoddesc paired with either pCodeStart or
-// reJitId (exactly one should be non-zero, and will be used as the key for the lookup)
-//
-// Arguments:
-// * pMD - MethodDesc * to look up
-// * pCodeStart - PCODE of the particular rejit attempt to look up. NULL if looking
-// up by ReJITID.
-// * reJitId - ReJITID of the particular rejit attempt to look up. NULL if looking
-// up by PCODE.
-//
-// Return Value:
-// ReJitInfo * matching input parameters, or NULL if no such ReJitInfo could be
-// found.
-//
-// Assumptions:
-// Caller must be holding this ReJitManager's table lock.
-//
-
-PTR_ReJitInfo ReJitManager::FindReJitInfo(PTR_MethodDesc pMD, PCODE pCodeStart, ReJITID reJitId)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- INSTANCE_CHECK;
- PRECONDITION(CheckPointer(pMD));
- }
- CONTRACTL_END;
-
- // Caller should hold the Crst around calling this function and using the ReJitInfo.
-#ifndef DACCESS_COMPILE
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
-#endif
-
- // One of these two keys should be used, but not both!
- _ASSERTE(
- ((pCodeStart != NULL) || (reJitId != 0)) &&
- !((pCodeStart != NULL) && (reJitId != 0)));
-
- for (ReJitInfoHash::KeyIterator iter = GetBeginIterator(pMD), end = GetEndIterator(pMD);
- iter != end;
- iter++)
- {
- PTR_ReJitInfo pInfo = *iter;
- _ASSERTE(pInfo->GetMethodDesc() == pMD);
- _ASSERTE(pInfo->m_pShared != NULL);
-
- if ((pCodeStart != NULL && pInfo->m_pCode == pCodeStart) || // pCodeStart is key
- (reJitId != 0 && pInfo->m_pShared->GetId() == reJitId)) // reJitId is key
- {
- return pInfo;
- }
- }
-
- return NULL;
+ return nativeCodeVersion.GetILCodeVersion().GetVersionId();
}
//---------------------------------------------------------------------------------------
@@ -2934,7 +992,7 @@ PTR_ReJitInfo ReJitManager::FindReJitInfo(PTR_MethodDesc pMD, PCODE pCodeStart,
// cReJitIds were returned and cReJitIds < *pcReJitId (latter being the total
// number of ReJITIDs available).
//
-
+// static
HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[])
{
CONTRACTL
@@ -2942,31 +1000,29 @@ HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * p
NOTHROW;
CAN_TAKE_LOCK;
GC_NOTRIGGER;
- INSTANCE_CHECK;
PRECONDITION(CheckPointer(pMD));
PRECONDITION(pcReJitIds != NULL);
PRECONDITION(reJitIds != NULL);
}
CONTRACTL_END;
- CrstHolder ch(&m_crstTable);
+ CodeVersionManager* pCodeVersionManager = pMD->GetCodeVersionManager();
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
ULONG cnt = 0;
- for (ReJitInfoHash::KeyIterator iter = GetBeginIterator(pMD), end = GetEndIterator(pMD);
+ ILCodeVersionCollection ilCodeVersions = pCodeVersionManager->GetILCodeVersions(pMD);
+ for (ILCodeVersionIterator iter = ilCodeVersions.Begin(), end = ilCodeVersions.End();
iter != end;
iter++)
{
- ReJitInfo * pInfo = *iter;
- _ASSERTE(pInfo->GetMethodDesc() == pMD);
- _ASSERTE(pInfo->m_pShared != NULL);
+ ILCodeVersion curILVersion = *iter;
- if (pInfo->m_pShared->GetState() == SharedReJitInfo::kStateActive ||
- pInfo->m_pShared->GetState() == SharedReJitInfo::kStateReverted)
+ if (curILVersion.GetRejitState() == ILCodeVersion::kStateActive)
{
if (cnt < cReJitIds)
{
- reJitIds[cnt] = pInfo->m_pShared->GetId();
+ reJitIds[cnt] = curILVersion.GetVersionId();
}
++cnt;
@@ -2979,975 +1035,7 @@ HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * p
return (cnt > cReJitIds) ? S_FALSE : S_OK;
}
-//---------------------------------------------------------------------------------------
-//
-// Helper that inits a new ReJitReportErrorWorkItem and adds it to the pErrors array
-//
-// Arguments:
-// * pModule - The module in the module/MethodDef identifier pair for the method which
-// had an error during rejit
-// * methodDef - The MethodDef in the module/MethodDef identifier pair for the method which
-// had an error during rejit
-// * pMD - If available, the specific method instance which had an error during rejit
-// * hrStatus - HRESULT for the rejit error that occurred
-// * pErrors - the list of error records that this method will append to
-//
-// Return Value:
-// * S_OK: error was appended
-// * E_OUTOFMEMORY: Not enough memory to create the new error item. The array is unchanged.
-//
-
-//static
-HRESULT ReJitManager::AddReJITError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus, CDynArray<ReJitReportErrorWorkItem> * pErrors)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- ReJitReportErrorWorkItem* pError = pErrors->Append();
- if (pError == NULL)
- {
- return E_OUTOFMEMORY;
- }
- pError->pModule = pModule;
- pError->methodDef = methodDef;
- pError->pMethodDesc = pMD;
- pError->hrStatus = hrStatus;
- return S_OK;
-}
-
-//---------------------------------------------------------------------------------------
-//
-// Helper that inits a new ReJitReportErrorWorkItem and adds it to the pErrors array
-//
-// Arguments:
-// * pReJitInfo - The method which had an error during rejit
-// * hrStatus - HRESULT for the rejit error that occurred
-// * pErrors - the list of error records that this method will append to
-//
-// Return Value:
-// * S_OK: error was appended
-// * E_OUTOFMEMORY: Not enough memory to create the new error item. The array is unchanged.
-//
-
-//static
-HRESULT ReJitManager::AddReJITError(ReJitInfo* pReJitInfo, HRESULT hrStatus, CDynArray<ReJitReportErrorWorkItem> * pErrors)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- Module * pModule = NULL;
- mdMethodDef methodDef = mdTokenNil;
- pReJitInfo->GetModuleAndTokenRegardlessOfKeyType(&pModule, &methodDef);
- return AddReJITError(pModule, methodDef, pReJitInfo->GetMethodDesc(), hrStatus, pErrors);
-}
-
-#ifdef _DEBUG
-//---------------------------------------------------------------------------------------
-//
-// Debug-only helper used while iterating through the hash table of
-// ReJitInfos to verify that all entries between the specified iterators are
-// reverted. Asserts if it finds any non-reverted entries.
-//
-// Arguments:
-// * iter - Iterator to start verifying at
-// * end - Iterator to stop verifying at
-//
-//
-
-void ReJitManager::AssertRestOfEntriesAreReverted(
- ReJitInfoHash::KeyIterator iter,
- ReJitInfoHash::KeyIterator end)
-{
- LIMITED_METHOD_CONTRACT;
-
- // All other rejits should be in the reverted state
- while (++iter != end)
- {
- _ASSERTE((*iter)->m_pShared->GetState() == SharedReJitInfo::kStateReverted);
- }
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Debug-only helper to dump ReJitManager contents to stdout. Only used if
-// COMPlus_ProfAPI_EnableRejitDiagnostics is set.
-//
-// Arguments:
-// * szIntroText - Intro text passed by caller to be output before this ReJitManager
-// is dumped.
-//
-//
-
-void ReJitManager::Dump(LPCSTR szIntroText)
-{
- if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ProfAPI_EnableRejitDiagnostics) == 0)
- return;
-
- printf(szIntroText);
- fflush(stdout);
-
- CrstHolder ch(&m_crstTable);
-
- printf("BEGIN ReJitManager::Dump: 0x%p\n", this);
-
- for (ReJitInfoHash::Iterator iterCur = m_table.Begin(), iterEnd = m_table.End();
- iterCur != iterEnd;
- iterCur++)
- {
- ReJitInfo * pInfo = *iterCur;
- printf(
- "\tInfo 0x%p: State=0x%x, Next=0x%p, Shared=%p, SharedState=0x%x\n",
- pInfo,
- pInfo->GetState(),
- (void*)pInfo->m_pNext,
- (void*)pInfo->m_pShared,
- pInfo->m_pShared->GetState());
-
- switch(pInfo->m_key.m_keyType)
- {
- case ReJitInfo::Key::kMethodDesc:
- printf(
- "\t\tMD=0x%p, %s.%s (%s)\n",
- (void*)pInfo->GetMethodDesc(),
- pInfo->GetMethodDesc()->m_pszDebugClassName,
- pInfo->GetMethodDesc()->m_pszDebugMethodName,
- pInfo->GetMethodDesc()->m_pszDebugMethodSignature);
- break;
-
- case ReJitInfo::Key::kMetadataToken:
- Module * pModule;
- mdMethodDef methodDef;
- pInfo->GetModuleAndToken(&pModule, &methodDef);
- printf(
- "\t\tModule=0x%p, Token=0x%x\n",
- pModule,
- methodDef);
- break;
-
- case ReJitInfo::Key::kUninitialized:
- printf("\t\tUNINITIALIZED\n");
- break;
-
- default:
- _ASSERTE(!"Unrecognized pInfo key type");
- }
- fflush(stdout);
- }
- printf("END ReJitManager::Dump: 0x%p\n", this);
- fflush(stdout);
-}
-
-#endif // _DEBUG
-
-//---------------------------------------------------------------------------------------
-// ReJitInfo implementation
-
-// All the state-changey stuff is kept up here in the !DACCESS_COMPILE block.
-// The more read-only inspection-y stuff follows the block.
-
-
-#ifndef DACCESS_COMPILE
-
-//---------------------------------------------------------------------------------------
-//
-// Do the actual work of stamping the top of originally-jitted-code with a jmp that goes
-// to the prestub. This can be called in one of three ways:
-// * Case 1: By RequestReJIT against an already-jitted function, in which case the
-// PCODE may be inferred by the MethodDesc, and our caller will have suspended
-// the EE for us, OR
-// * Case 2: By the prestub worker after jitting the original code of a function
-// (i.e., the "pre-rejit" scenario). In this case, the EE is not suspended. But
-// that's ok, because the PCODE has not yet been published to the MethodDesc, and
-// no thread can be executing inside the originally JITted function yet.
-// * Case 3: At type/method restore time for an NGEN'ed assembly. This is also the pre-rejit
-// scenario because we are guaranteed to do this before the code in the module
-// is executable. EE suspend is not required.
-//
-// Arguments:
-// * pCode - Case 1 (above): will be NULL, and we can infer the PCODE from the
-// MethodDesc; Case 2+3 (above, pre-rejit): will be non-NULL, and we'll need to use
-// this to find the code to stamp on top of.
-//
-// Return Value:
-// * S_OK: Either we successfully did the jmp-stamp, or a racing thread took care of
-// it for us.
-// * Else, HRESULT indicating failure.
-//
-// Assumptions:
-// The caller will have suspended the EE if necessary (case 1), before this is
-// called.
-//
-HRESULT ReJitInfo::JumpStampNativeCode(PCODE pCode /* = NULL */)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
-
- // It may seem dangerous to be stamping jumps over code while a GC is going on,
- // but we're actually safe. As we assert below, either we're holding the thread
- // store lock (and thus preventing a GC) OR we're stamping code that has not yet
- // been published (and will thus not be executed by managed therads or examined
- // by the GC).
- MODE_ANY;
- }
- CONTRACTL_END;
-
- PCODE pCodePublished = GetMethodDesc()->GetNativeCode();
-
- _ASSERTE((pCode != NULL) || (pCodePublished != NULL));
- _ASSERTE(GetMethodDesc()->GetReJitManager()->IsTableCrstOwnedByCurrentThread());
-
- HRESULT hr = S_OK;
-
- // We'll jump-stamp over pCode, or if pCode is NULL, jump-stamp over the published
- // code for this's MethodDesc.
- LPBYTE pbCode = (LPBYTE) pCode;
- if (pbCode == NULL)
- {
- // If caller didn't specify a pCode, just use the one that was published after
- // the original JIT. (A specific pCode would be passed in the pre-rejit case,
- // to jump-stamp the original code BEFORE the PCODE gets published.)
- pbCode = (LPBYTE) pCodePublished;
- }
- _ASSERTE (pbCode != NULL);
-
- // The debugging API may also try to write to the very top of this function (though
- // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know
- // whether we can safely patch the actual code, or instead write to the debugger's
- // buffer.
- DebuggerController::ControllerLockHolder lockController;
-
- // We could be in a race. Either two threads simultaneously JITting the same
- // method for the first time or two threads restoring NGEN'ed code.
- // Another thread may (or may not) have jump-stamped its copy of the code already
- _ASSERTE((GetState() == kJumpNone) || (GetState() == kJumpToPrestub));
-
- if (GetState() == kJumpToPrestub)
- {
- // The method has already been jump stamped so nothing left to do
- _ASSERTE(CodeIsSaved());
- return S_OK;
- }
-
- // Remember what we're stamping our jump on top of, so we can replace it during a
- // revert.
- for (int i = 0; i < sizeof(m_rgSavedCode); i++)
- {
- m_rgSavedCode[i] = *FirstCodeByteAddr(pbCode+i, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)(pbCode+i)));
- }
-
- EX_TRY
- {
- AllocMemTracker amt;
-
- // This guy might throw on out-of-memory, so rely on the tracker to clean-up
- Precode * pPrecode = Precode::Allocate(PRECODE_STUB, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator(), &amt);
- PCODE target = pPrecode->GetEntryPoint();
-
-#if defined(_X86_) || defined(_AMD64_)
-
- // Normal unpatched code never starts with a jump
- // so make sure this code isn't already patched
- _ASSERTE(*FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) != X86_INSTR_JMP_REL32);
-
- INT64 i64OldCode = *(INT64*)pbCode;
- INT64 i64NewCode = i64OldCode;
- LPBYTE pbNewValue = (LPBYTE)&i64NewCode;
- *pbNewValue = X86_INSTR_JMP_REL32;
- INT32 UNALIGNED * pOffset = reinterpret_cast<INT32 UNALIGNED *>(&pbNewValue[1]);
- // This will throw for out-of-memory, so don't write anything until
- // after he succeeds
- // This guy will leak/cache/reuse the jumpstub
- *pOffset = rel32UsingJumpStub(reinterpret_cast<INT32 UNALIGNED *>(pbCode + 1), target, GetMethodDesc(), GetMethodDesc()->GetLoaderAllocator());
-
- // If we have the EE suspended or the code is unpublished there won't be contention on this code
- hr = UpdateJumpStampHelper(pbCode, i64OldCode, i64NewCode, FALSE);
- if (FAILED(hr))
- {
- ThrowHR(hr);
- }
-
- //
- // No failure point after this!
- //
- amt.SuppressRelease();
-
-#else // _X86_ || _AMD64_
-#error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
-
-#endif // _X86_ || _AMD64_
-
- m_dwInternalFlags &= ~kStateMask;
- m_dwInternalFlags |= kJumpToPrestub;
- }
- EX_CATCH_HRESULT(hr);
- _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY);
-
- if (SUCCEEDED(hr))
- {
- _ASSERTE(GetState() == kJumpToPrestub);
- _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0
- }
-
- return hr;
-}
-
-//---------------------------------------------------------------------------------------
-//
-// Poke the JITted code to satsify a revert request (or to perform an implicit revert as
-// part of a second, third, etc. rejit request). Reinstates the originally JITted code
-// that had been jump-stamped over to perform a prior rejit.
-//
-// Arguments
-// fEESuspended - TRUE if the caller keeps the EE suspended during this call
-//
-//
-// Return Value:
-// S_OK to indicate the revert succeeded,
-// CORPROF_E_RUNTIME_SUSPEND_REQUIRED to indicate the jumpstamp hasn't been reverted
-// and EE suspension will be needed for success
-// other failure HRESULT indicating what went wrong.
-//
-// Assumptions:
-// Caller must be holding the owning ReJitManager's table crst.
-//
-
-HRESULT ReJitInfo::UndoJumpStampNativeCode(BOOL fEESuspended)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- _ASSERTE(GetMethodDesc()->GetReJitManager()->IsTableCrstOwnedByCurrentThread());
- _ASSERTE((m_pShared->GetState() == SharedReJitInfo::kStateReverted));
- _ASSERTE((GetState() == kJumpToPrestub) || (GetState() == kJumpToRejittedCode));
- _ASSERTE(m_rgSavedCode[0] != 0); // saved code should not start with 0 (see above test)
-
- BYTE * pbCode = (BYTE*)GetMethodDesc()->GetNativeCode();
- DebuggerController::ControllerLockHolder lockController;
-
-#if defined(_X86_) || defined(_AMD64_)
- _ASSERTE(m_rgSavedCode[0] != X86_INSTR_JMP_REL32);
- _ASSERTE(*FirstCodeByteAddr(pbCode, DebuggerController::GetPatchTable()->GetPatch((CORDB_ADDRESS_TYPE *)pbCode)) == X86_INSTR_JMP_REL32);
-#else
-#error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
-#endif // _X86_ || _AMD64_
-
- // For the interlocked compare, remember what pbCode is right now
- INT64 i64OldValue = *(INT64 *)pbCode;
- // Assemble the INT64 of the new code bytes to write. Start with what's there now
- INT64 i64NewValue = i64OldValue;
- memcpy(LPBYTE(&i64NewValue), m_rgSavedCode, sizeof(m_rgSavedCode));
- HRESULT hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended);
- _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended));
- if (hr != S_OK)
- return hr;
-
- // Transition state of this ReJitInfo to indicate the MD no longer has any jump stamp
- m_dwInternalFlags &= ~kStateMask;
- m_dwInternalFlags |= kJumpNone;
- return S_OK;
-}
-
-//---------------------------------------------------------------------------------------
-//
-// After code has been rejitted, this is called to update the jump-stamp to go from
-// pointing to the prestub, to pointing to the newly rejitted code.
-//
-// Arguments:
-// fEESuspended - TRUE if the caller keeps the EE suspended during this call
-// pRejittedCode - jitted code for the updated IL this method should execute
-//
-// Assumptions:
-// This rejit manager's table crst should be held by the caller
-//
-// Returns - S_OK if the jump target is updated
-// CORPROF_E_RUNTIME_SUSPEND_REQUIRED if the ee isn't suspended and it
-// will need to be in order to do the update safely
-HRESULT ReJitInfo::UpdateJumpTarget(BOOL fEESuspended, PCODE pRejittedCode)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_PREEMPTIVE;
- }
- CONTRACTL_END;
-
- MethodDesc * pMD = GetMethodDesc();
- _ASSERTE(pMD->GetReJitManager()->IsTableCrstOwnedByCurrentThread());
- _ASSERTE(m_pShared->GetState() == SharedReJitInfo::kStateActive);
- _ASSERTE(GetState() == kJumpToPrestub);
- _ASSERTE(m_pCode == NULL);
-
- // Beginning of originally JITted code containing the jmp that we will redirect.
- BYTE * pbCode = (BYTE*)pMD->GetNativeCode();
-
-#if defined(_X86_) || defined(_AMD64_)
-
- HRESULT hr = S_OK;
- {
- DebuggerController::ControllerLockHolder lockController;
-
- // This will throw for out-of-memory, so don't write anything until
- // after he succeeds
- // This guy will leak/cache/reuse the jumpstub
- INT32 offset = 0;
- EX_TRY
- {
- offset = rel32UsingJumpStub(
- reinterpret_cast<INT32 UNALIGNED *>(&pbCode[1]), // base of offset
- pRejittedCode, // target of jump
- pMD,
- pMD->GetLoaderAllocator());
- }
- EX_CATCH_HRESULT(hr);
- _ASSERT(hr == S_OK || hr == E_OUTOFMEMORY);
- if (FAILED(hr))
- {
- return hr;
- }
- // For validation later, remember what pbCode is right now
- INT64 i64OldValue = *(INT64 *)pbCode;
-
- // Assemble the INT64 of the new code bytes to write. Start with what's there now
- INT64 i64NewValue = i64OldValue;
- LPBYTE pbNewValue = (LPBYTE)&i64NewValue;
-
- // First byte becomes a rel32 jmp instruction (should be a no-op as asserted
- // above, but can't hurt)
- *pbNewValue = X86_INSTR_JMP_REL32;
- // Next 4 bytes are the jmp target (offset to jmp stub)
- INT32 UNALIGNED * pnOffset = reinterpret_cast<INT32 UNALIGNED *>(&pbNewValue[1]);
- *pnOffset = offset;
-
- hr = UpdateJumpStampHelper(pbCode, i64OldValue, i64NewValue, !fEESuspended);
- _ASSERTE(hr == S_OK || (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED && !fEESuspended));
- }
- if (FAILED(hr))
- {
- return hr;
- }
-
-#else // _X86_ || _AMD64_
-#error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
-#endif // _X86_ || _AMD64_
-
- // State transition
- m_dwInternalFlags &= ~kStateMask;
- m_dwInternalFlags |= kJumpToRejittedCode;
- return S_OK;
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// This is called to modify the jump-stamp area, the first ReJitInfo::JumpStubSize bytes
-// in the method's code.
-//
-// Notes:
-// Callers use this method in a variety of circumstances:
-// a) when the code is unpublished (fContentionPossible == FALSE)
-// b) when the caller has taken the ThreadStoreLock and suspended the EE
-// (fContentionPossible == FALSE)
-// c) when the code is published, the EE isn't suspended, and the jumpstamp
-// area consists of a single 5 byte long jump instruction
-// (fContentionPossible == TRUE)
-// This method will attempt to alter the jump-stamp even if the caller has not prevented
-// contention, but there is no guarantee it will be succesful. When the caller has prevented
-// contention, then success is assured. Callers may oportunistically try without
-// EE suspension, and then upgrade to EE suspension if the first attempt fails.
-//
-// Assumptions:
-// This rejit manager's table crst should be held by the caller or fContentionPossible==FALSE
-// The debugger patch table lock should be held by the caller
-//
-// Arguments:
-// pbCode - pointer to the code where the jump stamp is placed
-// i64OldValue - the bytes which should currently be at the start of the method code
-// i64NewValue - the new bytes which should be written at the start of the method code
-// fContentionPossible - See the Notes section above.
-//
-// Returns:
-// S_OK => the jumpstamp has been succesfully updated.
-// CORPROF_E_RUNTIME_SUSPEND_REQUIRED => the jumpstamp remains unchanged (preventing contention will be necessary)
-// other failing HR => VirtualProtect failed, the jumpstamp remains unchanged
-//
-HRESULT ReJitInfo::UpdateJumpStampHelper(BYTE* pbCode, INT64 i64OldValue, INT64 i64NewValue, BOOL fContentionPossible)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- MethodDesc * pMD = GetMethodDesc();
- _ASSERTE(pMD->GetReJitManager()->IsTableCrstOwnedByCurrentThread() || !fContentionPossible);
-
- // When ReJIT is enabled, method entrypoints are always at least 8-byte aligned (see
- // code:EEJitManager::allocCode), so we can do a single 64-bit interlocked operation
- // to update the jump target. However, some code may have gotten compiled before
- // the profiler had a chance to enable ReJIT (e.g., NGENd code, or code JITted
- // before a profiler attaches). In such cases, we cannot rely on a simple
- // interlocked operation, and instead must suspend the runtime to ensure we can
- // safely update the jmp instruction.
- //
- // This method doesn't verify that the method is actually safe to rejit, we expect
- // callers to do that. At the moment NGEN'ed code is safe to rejit even if
- // it is unaligned, but code generated before the profiler attaches is not.
- if (fContentionPossible && !(IS_ALIGNED(pbCode, sizeof(INT64))))
- {
- return CORPROF_E_RUNTIME_SUSPEND_REQUIRED;
- }
-
- // The debugging API may also try to write to this function (though
- // with an int 3 for breakpoint purposes). Coordinate with the debugger so we know
- // whether we can safely patch the actual code, or instead write to the debugger's
- // buffer.
- if (fContentionPossible)
- {
- for (CORDB_ADDRESS_TYPE* pbProbeAddr = pbCode; pbProbeAddr < pbCode + ReJitInfo::JumpStubSize; pbProbeAddr++)
- {
- if (NULL != DebuggerController::GetPatchTable()->GetPatch(pbProbeAddr))
- {
- return CORPROF_E_RUNTIME_SUSPEND_REQUIRED;
- }
- }
- }
-
-#if defined(_X86_) || defined(_AMD64_)
-
- DWORD oldProt;
- if (!ClrVirtualProtect((LPVOID)pbCode, 8, PAGE_EXECUTE_READWRITE, &oldProt))
- {
- return HRESULT_FROM_WIN32(GetLastError());
- }
-
- if (fContentionPossible)
- {
- INT64 i64InterlockReportedOldValue = FastInterlockCompareExchangeLong((INT64 *)pbCode, i64NewValue, i64OldValue);
- // Since changes to these bytes are protected by this rejitmgr's m_crstTable, we
- // shouldn't have two writers conflicting.
- _ASSERTE(i64InterlockReportedOldValue == i64OldValue);
- }
- else
- {
- // In this path the caller ensures:
- // a) no thread will execute through the prologue area we are modifying
- // b) no thread is stopped in a prologue such that it resumes in the middle of code we are modifying
- // c) no thread is doing a debugger patch skip operation in which an unmodified copy of the method's
- // code could be executed from a patch skip buffer.
-
- // PERF: we might still want a faster path through here if we aren't debugging that doesn't do
- // all the patch checks
- for (int i = 0; i < ReJitInfo::JumpStubSize; i++)
- {
- *FirstCodeByteAddr(pbCode+i, DebuggerController::GetPatchTable()->GetPatch(pbCode+i)) = ((BYTE*)&i64NewValue)[i];
- }
- }
-
- if (oldProt != PAGE_EXECUTE_READWRITE)
- {
- // The CLR codebase in many locations simply ignores failures to restore the page protections
- // Its true that it isn't a problem functionally, but it seems a bit sketchy?
- // I am following the convention for now.
- ClrVirtualProtect((LPVOID)pbCode, 8, oldProt, &oldProt);
- }
-
- FlushInstructionCache(GetCurrentProcess(), pbCode, ReJitInfo::JumpStubSize);
- return S_OK;
-
-#else // _X86_ || _AMD64_
-#error "Need to define a way to jump-stamp the prolog in a safe way for this platform"
-#endif // _X86_ || _AMD64_
-}
-
-
-#endif // DACCESS_COMPILE
-// The rest of the ReJitInfo methods are safe to compile for DAC
-
-
-
-//---------------------------------------------------------------------------------------
-//
-// ReJitInfos can be constructed in two ways: As a "regular" ReJitInfo indexed by
-// MethodDesc *, or as a "placeholder" ReJitInfo (to satisfy pre-rejit requests) indexed
-// by (Module *, methodDef). Both constructors call this helper to do all the common
-// code for initializing the ReJitInfo.
-//
-
-void ReJitInfo::CommonInit()
-{
- LIMITED_METHOD_CONTRACT;
-
- m_pCode = NULL;
- m_pNext = NULL;
- m_dwInternalFlags = kJumpNone;
- m_pShared->AddMethod(this);
- ZeroMemory(m_rgSavedCode, sizeof(m_rgSavedCode));
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Regardless of which kind of ReJitInfo this is, this will always return its
-// corresponding Module * & methodDef
-//
-// Arguments:
-// * ppModule - [out] Module * related to this ReJitInfo (which contains the
-// returned methodDef)
-// * pMethodDef - [out] methodDef related to this ReJitInfo
-//
-
-void ReJitInfo::GetModuleAndTokenRegardlessOfKeyType(Module ** ppModule, mdMethodDef * pMethodDef)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- SO_NOT_MAINLINE;
- }
- CONTRACTL_END;
-
- _ASSERTE(ppModule != NULL);
- _ASSERTE(pMethodDef != NULL);
-
- if (m_key.m_keyType == Key::kMetadataToken)
- {
- GetModuleAndToken(ppModule, pMethodDef);
- }
- else
- {
- MethodDesc * pMD = GetMethodDesc();
- _ASSERTE(pMD != NULL);
- _ASSERTE(pMD->IsRestored());
-
- *ppModule = pMD->GetModule();
- *pMethodDef = pMD->GetMemberDef();
- }
-
- _ASSERTE(*ppModule != NULL);
- _ASSERTE(*pMethodDef != mdTokenNil);
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Used as part of the hash table implementation in the containing ReJitManager, this
-// hashes a ReJitInfo by MethodDesc * when available, else by (Module *, methodDef)
-//
-// Arguments:
-// key - Key representing the ReJitInfo to hash
-//
-// Return Value:
-// Hash value of the ReJitInfo represented by the specified key
-//
-
-// static
-COUNT_T ReJitInfo::Hash(Key key)
-{
- LIMITED_METHOD_CONTRACT;
-
- if (key.m_keyType == Key::kMethodDesc)
- {
- return HashPtr(0, PTR_MethodDesc(key.m_pMD));
- }
-
- _ASSERTE (key.m_keyType == Key::kMetadataToken);
-
- return HashPtr(key.m_methodDef, PTR_Module(key.m_pModule));
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Return the IL to compile for a given ReJitInfo
-//
-// Return Value:
-// Pointer to IL buffer to compile. If the profiler has specified IL to rejit,
-// this will be our copy of the IL buffer specified by the profiler. Else, this
-// points to the original IL for the method from its module's metadata.
-//
-// Notes:
-// IL memory is managed by us, not the caller. Caller must not free the buffer.
-//
-
-COR_ILMETHOD * ReJitInfo::GetIL()
-{
- CONTRACTL
- {
- THROWS; // Getting original IL via PEFile::GetIL can throw
- CAN_TAKE_LOCK; // Looking up dynamically overridden IL takes a lock
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if (m_pShared->m_pbIL != NULL)
- {
- return reinterpret_cast<COR_ILMETHOD *>(m_pShared->m_pbIL);
- }
-
- // If the user hasn't overriden us, get whatever the original IL had
- return GetMethodDesc()->GetILHeader(TRUE);
-}
-
-
-//---------------------------------------------------------------------------------------
-// SharedReJitInfo implementation
-
-
-SharedReJitInfo::SharedReJitInfo()
- : m_dwInternalFlags(kStateRequested),
- m_pbIL(NULL),
- m_dwCodegenFlags(0),
- m_reJitId(InterlockedIncrement(reinterpret_cast<LONG*>(&s_GlobalReJitId))),
- m_pInfoList(NULL)
-{
- LIMITED_METHOD_CONTRACT;
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Link in the specified ReJitInfo to the list maintained by this SharedReJitInfo
-//
-// Arguments:
-// pInfo - ReJitInfo being added
-//
-
-void SharedReJitInfo::AddMethod(ReJitInfo * pInfo)
-{
- LIMITED_METHOD_CONTRACT;
-
- _ASSERTE(pInfo->m_pShared == this);
-
- // Push it on the head of our list
- _ASSERTE(pInfo->m_pNext == NULL);
- pInfo->m_pNext = PTR_ReJitInfo(m_pInfoList);
- m_pInfoList = pInfo;
-}
-
-
-//---------------------------------------------------------------------------------------
-//
-// Unlink the specified ReJitInfo from the list maintained by this SharedReJitInfo.
-// Currently this is only used on AD unload to remove ReJitInfos of non-domain-neutral instantiations
-// of domain-neutral generics (which are tracked in the SharedDomain's ReJitManager).
-// This may be used in the future once we implement memory reclamation on revert().
-//
-// Arguments:
-// pInfo - ReJitInfo being removed
-//
-
-void SharedReJitInfo::RemoveMethod(ReJitInfo * pInfo)
-{
- LIMITED_METHOD_CONTRACT;
-
-#ifndef DACCESS_COMPILE
-
- // Find it
- ReJitInfo ** ppEntry = &m_pInfoList;
- while (*ppEntry != pInfo)
- {
- ppEntry = &(*ppEntry)->m_pNext;
- _ASSERTE(*ppEntry != NULL);
- }
-
- // Remove it
- _ASSERTE((*ppEntry)->m_pShared == this);
- *ppEntry = (*ppEntry)->m_pNext;
-
-#endif // DACCESS_COMPILE
-}
-
-//---------------------------------------------------------------------------------------
-//
-// MethodDesc::MakeJitWorker() calls this to determine if there's an outstanding
-// "pre-rejit" request for a MethodDesc that has just been jitted for the first time.
-// This is also called when methods are being restored in NGEN images. The sequence looks like:
-// *Enter holder
-// Enter Rejit table lock
-// DoJumpStampIfNecessary
-// *Runtime code publishes/restores method
-// *Exit holder
-// Leave rejit table lock
-// Send rejit error callbacks if needed
-//
-// This also has a non-locking early-out if ReJIT is not enabled.
-//
-// #PublishCode:
-// Note that the runtime needs to publish/restore the PCODE while this holder is
-// on the stack, so it can happen under the ReJitManager's lock.
-// This prevents a "lost pre-rejit" race with a profiler that calls
-// RequestReJIT just as the method finishes compiling. In particular, the locking ensures
-// atomicity between this set of steps (performed in DoJumpStampIfNecessary):
-// * (1) Checking whether there is a pre-rejit request for this MD
-// * (2) If not, skip doing the pre-rejit-jmp-stamp
-// * (3) Publishing the PCODE
-//
-// with respect to these steps performed in RequestReJIT:
-// * (a) Is PCODE published yet?
-// * (b) If not, create pre-rejit (placeholder) ReJitInfo which the prestub will
-// consult when it JITs the original IL
-//
-// Without this atomicity, we could get the ordering (1), (2), (a), (b), (3), resulting
-// in the rejit request getting completely ignored (i.e., we file away the pre-rejit
-// placeholder AFTER the prestub checks for it).
-//
-// A similar race is possible for code being restored. In that case the restoring thread
-// does:
-// * (1) Check if there is a pre-rejit request for this MD
-// * (2) If not, no need to jmp-stamp
-// * (3) Restore the MD
-
-// And RequestRejit does:
-// * (a) [In LoadedMethodDescIterator] Is a potential MD restored yet?
-// * (b) [In MarkInstantiationsForReJit] If not, don't queue it for jump-stamping
-//
-// Same ordering (1), (2), (a), (b), (3) results in missing both opportunities to jump
-// stamp.
-
-#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
-ReJitPublishMethodHolder::ReJitPublishMethodHolder(MethodDesc* pMethodDesc, PCODE pCode) :
-m_pMD(NULL), m_hr(S_OK)
-{
- // This method can't have a contract because entering the table lock
- // below increments GCNoTrigger count. Contracts always revert these changes
- // at the end of the method but we need the incremented count to flow out of the
- // method. The balancing decrement occurs in the destructor.
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_CAN_TAKE_LOCK;
- STATIC_CONTRACT_MODE_ANY;
-
- // We come here from the PreStub and from MethodDesc::CheckRestore
- // The method should be effectively restored, but we haven't yet
- // cleared the unrestored bit so we can't assert pMethodDesc->IsRestored()
- // We can assert:
- _ASSERTE(pMethodDesc->GetMethodTable()->IsRestored());
-
- if (ReJitManager::IsReJITEnabled() && (pCode != NULL))
- {
- m_pMD = pMethodDesc;
- ReJitManager* pReJitManager = pMethodDesc->GetReJitManager();
- pReJitManager->m_crstTable.Enter();
- m_hr = pReJitManager->DoJumpStampIfNecessary(pMethodDesc, pCode);
- }
-}
-
-
-ReJitPublishMethodHolder::~ReJitPublishMethodHolder()
-{
- // This method can't have a contract because leaving the table lock
- // below decrements GCNoTrigger count. Contracts always revert these changes
- // at the end of the method but we need the decremented count to flow out of the
- // method. The balancing increment occurred in the constructor.
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_TRIGGERS; // NOTRIGGER until we leave the lock
- STATIC_CONTRACT_CAN_TAKE_LOCK;
- STATIC_CONTRACT_MODE_ANY;
-
- if (m_pMD)
- {
- ReJitManager* pReJitManager = m_pMD->GetReJitManager();
- pReJitManager->m_crstTable.Leave();
- if (FAILED(m_hr))
- {
- ReJitManager::ReportReJITError(m_pMD->GetModule(), m_pMD->GetMemberDef(), m_pMD, m_hr);
- }
- }
-}
-
-ReJitPublishMethodTableHolder::ReJitPublishMethodTableHolder(MethodTable* pMethodTable) :
-m_pMethodTable(NULL)
-{
- // This method can't have a contract because entering the table lock
- // below increments GCNoTrigger count. Contracts always revert these changes
- // at the end of the method but we need the incremented count to flow out of the
- // method. The balancing decrement occurs in the destructor.
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_CAN_TAKE_LOCK;
- STATIC_CONTRACT_MODE_ANY;
-
- // We come here from MethodTable::SetIsRestored
- // The method table should be effectively restored, but we haven't yet
- // cleared the unrestored bit so we can't assert pMethodTable->IsRestored()
-
- if (ReJitManager::IsReJITEnabled())
- {
- m_pMethodTable = pMethodTable;
- ReJitManager* pReJitManager = pMethodTable->GetModule()->GetReJitManager();
- pReJitManager->m_crstTable.Enter();
- MethodTable::IntroducedMethodIterator itMethods(pMethodTable, FALSE);
- for (; itMethods.IsValid(); itMethods.Next())
- {
- // Although the MethodTable is restored, the methods might not be.
- // We need to be careful to only query portions of the MethodDesc
- // that work in a partially restored state. The only methods that need
- // further restoration are IL stubs (which aren't rejittable) and
- // generic methods. The only generic methods directly accesible from
- // the MethodTable are definitions. GetNativeCode() on generic defs
- // will run succesfully and return NULL which short circuits the
- // rest of the logic.
- MethodDesc * pMD = itMethods.GetMethodDesc();
- PCODE pCode = pMD->GetNativeCode();
- if (pCode != NULL)
- {
- HRESULT hr = pReJitManager->DoJumpStampIfNecessary(pMD, pCode);
- if (FAILED(hr))
- {
- ReJitManager::AddReJITError(pMD->GetModule(), pMD->GetMemberDef(), pMD, hr, &m_errors);
- }
- }
- }
- }
-}
-
-
-ReJitPublishMethodTableHolder::~ReJitPublishMethodTableHolder()
-{
- // This method can't have a contract because leaving the table lock
- // below decrements GCNoTrigger count. Contracts always revert these changes
- // at the end of the method but we need the decremented count to flow out of the
- // method. The balancing increment occurred in the constructor.
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_TRIGGERS; // NOTRIGGER until we leave the lock
- STATIC_CONTRACT_CAN_TAKE_LOCK;
- STATIC_CONTRACT_MODE_ANY;
-
- if (m_pMethodTable)
- {
- ReJitManager* pReJitManager = m_pMethodTable->GetModule()->GetReJitManager();
- pReJitManager->m_crstTable.Leave();
- for (int i = 0; i < m_errors.Count(); i++)
- {
- ReJitManager::ReportReJITError(&(m_errors[i]));
- }
- }
-}
-#endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
-
+#endif // FEATURE_CODE_VERSIONING
#else // FEATURE_REJIT
// On architectures that don't support rejit, just keep around some do-nothing
@@ -3972,19 +1060,6 @@ HRESULT ReJitManager::RequestRevert(
return E_NOTIMPL;
}
-// static
-void ReJitManager::OnAppDomainExit(AppDomain * pAppDomain)
-{
-}
-
-ReJitManager::ReJitManager()
-{
-}
-
-void ReJitManager::PreInit(BOOL fSharedDomain)
-{
-}
-
ReJITID ReJitManager::GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart)
{
return 0;
@@ -3995,11 +1070,6 @@ ReJITID ReJitManager::GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart)
return 0;
}
-PCODE ReJitManager::GetCodeStart(PTR_MethodDesc pMD, ReJITID reJitId)
-{
- return NULL;
-}
-
HRESULT ReJitManager::GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[])
{
return E_NOTIMPL;
diff --git a/src/vm/rejit.h b/src/vm/rejit.h
index 3c8bfd66b2..8401ecb960 100644
--- a/src/vm/rejit.h
+++ b/src/vm/rejit.h
@@ -19,9 +19,8 @@
#include "contractimpl.h"
#include "shash.h"
#include "corprof.h"
+#include "codeversion.h"
-struct ReJitInfo;
-struct SharedReJitInfo;
class ReJitManager;
class MethodDesc;
class ClrDataAccess;
@@ -68,347 +67,9 @@ protected:
COR_IL_MAP * m_rgInstrumentedMapEntries;
};
-//---------------------------------------------------------------------------------------
-// Helper base class used by the structures below to enforce that their
-// pieces get allocated on the appropriate loader heaps
-//
-struct LoaderHeapAllocatedRejitStructure
-{
-public:
- void * operator new (size_t size, LoaderHeap * pHeap, const NoThrow&);
- void * operator new (size_t size, LoaderHeap * pHeap);
-};
-
-//---------------------------------------------------------------------------------------
-// One instance of this per rejit request for each mdMethodDef. Contains IL and
-// compilation flags. This is used primarily as a structure, so most of its
-// members are left public.
-//
-struct SharedReJitInfo : public LoaderHeapAllocatedRejitStructure
-{
-private:
- // This determines what to use next as the value of the profiling API's ReJITID.
- static ReJITID s_GlobalReJitId;
-
-public:
- // These represent the various states a SharedReJitInfo can be in.
- enum InternalFlags
- {
- // The profiler has requested a ReJit, so we've allocated stuff, but we haven't
- // called back to the profiler to get any info or indicate that the ReJit has
- // started. (This Info can be 'reused' for a new ReJit if the
- // profiler calls RequestRejit again before we transition to the next state.)
- kStateRequested = 0x00000000,
-
- // The CLR has initiated the call to the profiler's GetReJITParameters() callback
- // but it hasn't completed yet. At this point we have to assume the profiler has
- // commited to a specific IL body, even if the CLR doesn't know what it is yet.
- // If the profiler calls RequestRejit we need to allocate a new SharedReJitInfo
- // and call GetReJITParameters() again.
- kStateGettingReJITParameters = 0x00000001,
-
- // We have asked the profiler about this method via ICorProfilerFunctionControl,
- // and have thus stored the IL and codegen flags the profiler specified. Can only
- // transition to kStateReverted from this state.
- kStateActive = 0x00000002,
-
- // The methoddef has been reverted, but not freed yet. It (or its instantiations
- // for generics) *MAY* still be active on the stack someplace or have outstanding
- // memory references.
- kStateReverted = 0x00000003,
-
-
- kStateMask = 0x0000000F,
- };
-
- DWORD m_dwInternalFlags;
-
- // Data
- LPBYTE m_pbIL;
- DWORD m_dwCodegenFlags;
- InstrumentedILOffsetMapping m_instrumentedILMap;
-
-private:
- // This is the value of the profiling API's ReJITID for this particular
- // rejit request.
- const ReJITID m_reJitId;
-
- // Children
- ReJitInfo * m_pInfoList;
-
-public:
- // Constructor
- SharedReJitInfo();
-
- // Intentionally no destructor. SharedReJitInfo and its contents are
- // allocated on a loader heap, so SharedReJitInfo and its contents will be
- // freed when the AD is unloaded.
-
- // Read-Only Identifcation
- ReJITID GetId() { return m_reJitId; }
-
- void AddMethod(ReJitInfo * pInfo);
-
- void RemoveMethod(ReJitInfo * pInfo);
-
- ReJitInfo * GetMethods() { return m_pInfoList; }
-
- InternalFlags GetState();
-};
-
-//---------------------------------------------------------------------------------------
-// One instance of this per rejit request for each MethodDesc*. One SharedReJitInfo
-// corresponds to many ReJitInfos, as the SharedReJitInfo tracks the rejit request for
-// the methodDef token whereas the ReJitInfo tracks the rejit request for each correspond
-// MethodDesc* (instantiation). Points to actual generated code.
-//
-// In the case of "pre-rejit" (see comment at top of rejit.cpp), a special "placeholder"
-// instance of ReJitInfo is used to "remember" to jmp-stamp a not-yet-jitted-method once
-// it finally gets jitted the first time.
-//
-// Each ReJitManager contains a hash table of ReJitInfo instances, keyed by
-// ReJitManager::m_key.
-//
-// This is used primarily as a structure, so most of its members are left public.
-//
-struct ReJitInfo : public LoaderHeapAllocatedRejitStructure
-{
-public:
- // The size of the code used to jump stamp the prolog
- static const size_t JumpStubSize =
-#if defined(_X86_) || defined(_AMD64_)
- 5;
-#else
-#error "Need to define size of rejit jump-stamp for this platform"
- 1;
-#endif
-
- // Used by PtrSHash template as the key for this ReJitInfo. For regular
- // ReJitInfos, the key is the MethodDesc*. For placeholder ReJitInfos
- // (to facilitate pre-rejit), the key is (Module*, mdMethodDef).
- struct Key
- {
- public:
- enum
- {
- // The key has not yet had its values initialized
- kUninitialized = 0x0,
-
- // The key represents a loaded MethodDesc, and is identified by the m_pMD
- // field
- kMethodDesc = 0x1,
-
- // The key represents a "placeholder" ReJitInfo identified not by loaded
- // MethodDesc, but by the module and metadata token (m_pModule,
- // m_methodDef).
- kMetadataToken = 0x2,
- };
-
- // Storage consists of a discriminated union between MethodDesc* or
- // (Module*, mdMethodDef), with the key type as the discriminator.
- union
- {
- TADDR m_pMD;
- TADDR m_pModule;
- };
- ULONG32 m_methodDef : 28;
- ULONG32 m_keyType : 2;
-
- Key();
- Key(PTR_MethodDesc pMD);
- Key(PTR_Module pModule, mdMethodDef methodDef);
- };
-
- static COUNT_T Hash(Key key);
-
- enum InternalFlags
- {
- // This ReJitInfo is either a placeholder (identified by module and
- // metadata token, rather than loaded MethodDesc) OR this ReJitInfo is
- // identified by a loaded MethodDesc that has been reverted OR not yet
- // been jump-stamped. In the last case, the time window where this
- // ReJitInfo would stay in kJumpNone is rather small, as
- // RequestReJIT() will immediately cause the originally JITted code to
- // be jump-stamped.
- kJumpNone = 0x00000000,
-
- // This ReJitInfo is identified by a loaded MethodDesc that has been compiled and
- // jump-stamped, with the target being the prestub. The MethodDesc has not yet
- // been rejitted
- kJumpToPrestub = 0x00000001,
-
- // This ReJitInfo is identified by a loaded MethodDesc that has been compiled AND
- // rejitted. The top of the originally JITted code has been jump-stamped, with
- // the target being the latest version of the rejitted code.
- kJumpToRejittedCode = 0x00000002,
-
- kStateMask = 0x0000000F,
- };
-
- Key m_key;
- DWORD m_dwInternalFlags;
-
- // The beginning of the rejitted code
- PCODE m_pCode;
-
- // The parent SharedReJitInfo, which manages the rejit request for all
- // instantiations.
- PTR_SharedReJitInfo const m_pShared;
-
- // My next sibling ReJitInfo for this rejit request (e.g., another
- // generic instantiation of the same method)
- PTR_ReJitInfo m_pNext;
-
- // The originally JITted code that was overwritten with the jmp stamp.
- BYTE m_rgSavedCode[JumpStubSize];
-
-
- ReJitInfo(PTR_MethodDesc pMD, SharedReJitInfo * pShared);
- ReJitInfo(PTR_Module pModule, mdMethodDef methodDef, SharedReJitInfo * pShared);
-
- // Intentionally no destructor. ReJitInfo is allocated on a loader heap,
- // and will be freed (along with its associated SharedReJitInfo) when the
- // AD is unloaded.
-
- Key GetKey();
- PTR_MethodDesc GetMethodDesc();
- void GetModuleAndToken(Module ** ppModule, mdMethodDef * pMethodDef);
- void GetModuleAndTokenRegardlessOfKeyType(Module ** ppModule, mdMethodDef * pMethodDef);
- InternalFlags GetState();
-
- COR_ILMETHOD * GetIL();
-
- HRESULT JumpStampNativeCode(PCODE pCode = NULL);
- HRESULT UndoJumpStampNativeCode(BOOL fEESuspended);
- HRESULT UpdateJumpTarget(BOOL fEESuspended, PCODE pRejittedCode);
- HRESULT UpdateJumpStampHelper(BYTE* pbCode, INT64 i64OldValue, INT64 i64newValue, BOOL fContentionPossible);
-
-
-protected:
- void CommonInit();
- INDEBUG(BOOL CodeIsSaved();)
-};
-
-//---------------------------------------------------------------------------------------
-// Used by the SHash inside ReJitManager which maintains the set of ReJitInfo instances.
-//
-class ReJitInfoTraits : public DefaultSHashTraits<PTR_ReJitInfo>
-{
-public:
-
- // explicitly declare local typedefs for these traits types, otherwise
- // the compiler may get confused
- typedef DefaultSHashTraits<PTR_ReJitInfo> PARENT;
- typedef PARENT::element_t element_t;
- typedef PARENT::count_t count_t;
-
- typedef ReJitInfo::Key key_t;
-
- static key_t GetKey(const element_t &e);
- static BOOL Equals(key_t k1, key_t k2);
- static count_t Hash(key_t k);
- static bool IsNull(const element_t &e);
-};
-
-// RequestRejit and RequestRevert use these batches to accumulate ReJitInfos that need their
-// jump stamps updated
-class ReJitManager;
-struct ReJitManagerJumpStampBatch
-{
- ReJitManagerJumpStampBatch(ReJitManager * pReJitManager) : undoMethods(), preStubMethods()
- {
- LIMITED_METHOD_CONTRACT;
- this->pReJitManager = pReJitManager;
- }
-
- ReJitManager* pReJitManager;
- CDynArray<ReJitInfo *> undoMethods;
- CDynArray<ReJitInfo *> preStubMethods;
-};
-
-class ReJitManagerJumpStampBatchTraits : public DefaultSHashTraits<ReJitManagerJumpStampBatch *>
-{
-public:
-
- // explicitly declare local typedefs for these traits types, otherwise
- // the compiler may get confused
- typedef DefaultSHashTraits<ReJitManagerJumpStampBatch *> PARENT;
- typedef PARENT::element_t element_t;
- typedef PARENT::count_t count_t;
-
- typedef ReJitManager * key_t;
-
- static key_t GetKey(const element_t &e)
- {
- return e->pReJitManager;
- }
-
- static BOOL Equals(key_t k1, key_t k2)
- {
- return (k1 == k2);
- }
-
- static count_t Hash(key_t k)
- {
- return (count_t)k;
- }
-
- static bool IsNull(const element_t &e)
- {
- return (e == NULL);
- }
-};
-
-struct ReJitReportErrorWorkItem
-{
- Module* pModule;
- mdMethodDef methodDef;
- MethodDesc* pMethodDesc;
- HRESULT hrStatus;
-};
-
-
#endif // FEATURE_REJIT
-//
-// These holders are used by runtime code that is making new code
-// available for execution, either by publishing jitted code
-// or restoring NGEN code. It ensures the publishing is synchronized
-// with rejit requests
-//
-class ReJitPublishMethodHolder
-{
-public:
-#if !defined(FEATURE_REJIT) || defined(DACCESS_COMPILE) || defined(CROSSGEN_COMPILE)
- ReJitPublishMethodHolder(MethodDesc* pMethod, PCODE pCode) { }
-#else
- ReJitPublishMethodHolder(MethodDesc* pMethod, PCODE pCode);
- ~ReJitPublishMethodHolder();
-#endif
-
-private:
-#if defined(FEATURE_REJIT)
- MethodDesc * m_pMD;
- HRESULT m_hr;
-#endif
-};
-class ReJitPublishMethodTableHolder
-{
-public:
-#if !defined(FEATURE_REJIT) || defined(DACCESS_COMPILE) || defined(CROSSGEN_COMPILE)
- ReJitPublishMethodTableHolder(MethodTable* pMethodTable) { }
-#else
- ReJitPublishMethodTableHolder(MethodTable* pMethodTable);
- ~ReJitPublishMethodTableHolder();
-#endif
-
-private:
-#if defined(FEATURE_REJIT)
- MethodTable* m_pMethodTable;
- CDynArray<ReJitReportErrorWorkItem> m_errors;
-#endif
-};
//---------------------------------------------------------------------------------------
// The big honcho. One of these per AppDomain, plus one for the
@@ -420,55 +81,23 @@ class ReJitManager
friend class ClrDataAccess;
friend class DacDbiInterfaceImpl;
- //I would have prefered to make these inner classes, but
- //then I can't friend them from crst easily.
- friend class ReJitPublishMethodHolder;
- friend class ReJitPublishMethodTableHolder;
-
private:
#ifdef FEATURE_REJIT
- // Hash table mapping MethodDesc* (or (ModuleID, mdMethodDef)) to its
- // ReJitInfos. One key may map to multiple ReJitInfos if there have been
- // multiple rejit requests made for the same MD. See
- // code:ReJitManager::ReJitManager#Invariants for more information.
- typedef SHash<ReJitInfoTraits> ReJitInfoHash;
-
// One global crst (for the entire CLR instance) to synchronize
// cross-ReJitManager operations, such as batch calls to RequestRejit and
// RequestRevert (which modify multiple ReJitManager instances).
static CrstStatic s_csGlobalRequest;
- // All The ReJitInfos (and their linked SharedReJitInfos) for this domain.
- ReJitInfoHash m_table;
-
- // The crst that synchronizes the data in m_table, including
- // adding/removing to m_table, as well as state changes made to
- // individual ReJitInfos & SharedReJitInfos in m_table.
- CrstExplicitInit m_crstTable;
-
#endif //FEATURE_REJIT
public:
- // The ReJITManager takes care of grabbing its m_crstTable when necessary. However,
- // for clients who need to do this explicitly (like ETW rundown), this holder may be
- // used.
- class TableLockHolder
-#ifdef FEATURE_REJIT
- : public CrstHolder
-#endif
- {
- public:
- TableLockHolder(ReJitManager * pReJitManager);
- };
static void InitStatic();
static BOOL IsReJITEnabled();
- static void OnAppDomainExit(AppDomain * pAppDomain);
-
static HRESULT RequestReJIT(
ULONG cFunctions,
ModuleID rgModuleIDs[],
@@ -480,85 +109,56 @@ public:
mdMethodDef rgMethodDefs[],
HRESULT rgHrStatuses[]);
- static PCODE DoReJitIfNecessary(PTR_MethodDesc pMD); // Invokes the jit, or returns previously rejitted code
-
- static void DoJumpStampForAssemblyIfNecessary(Assembly* pAssemblyToSearch);
-
- static DWORD GetCurrentReJitFlags(PTR_MethodDesc pMD);
-
- ReJitManager();
-
- void PreInit(BOOL fSharedDomain);
-
- ReJITID GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart);
-
- ReJITID GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart);
+ static HRESULT ConfigureILCodeVersion(ILCodeVersion ilCodeVersion);
+ static CORJIT_FLAGS JitFlagsFromProfCodegenFlags(DWORD dwCodegenFlags);
- PCODE GetCodeStart(PTR_MethodDesc pMD, ReJITID reJitId);
-
- HRESULT GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[]);
+ static ReJITID GetReJitId(PTR_MethodDesc pMD, PCODE pCodeStart);
+ static ReJITID GetReJitIdNoLock(PTR_MethodDesc pMD, PCODE pCodeStart);
+ static HRESULT GetReJITIDs(PTR_MethodDesc pMD, ULONG cReJitIds, ULONG * pcReJitIds, ReJITID reJitIds[]);
#ifdef FEATURE_REJIT
-
- INDEBUG(BOOL IsTableCrstOwnedByCurrentThread());
+#ifndef DACCESS_COMPILE
+ static void ReportReJITError(CodeVersionManager::CodePublishError* pErrorRecord);
+ static void ReportReJITError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus);
+#endif
private:
- static HRESULT IsMethodSafeForReJit(PTR_MethodDesc pMD);
- static void ReportReJITError(ReJitReportErrorWorkItem* pErrorRecord);
- static void ReportReJITError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus);
- static HRESULT AddReJITError(ReJitInfo* pReJitInfo, HRESULT hrStatus, CDynArray<ReJitReportErrorWorkItem> * pErrors);
- static HRESULT AddReJITError(Module* pModule, mdMethodDef methodDef, MethodDesc* pMD, HRESULT hrStatus, CDynArray<ReJitReportErrorWorkItem> * pErrors);
- HRESULT BatchUpdateJumpStamps(CDynArray<ReJitInfo *> * pUndoMethods, CDynArray<ReJitInfo *> * pPreStubMethods, CDynArray<ReJitReportErrorWorkItem> * pErrors);
- PCODE DoReJitIfNecessaryWorker(PTR_MethodDesc pMD); // Invokes the jit, or returns previously rejitted code
- DWORD GetCurrentReJitFlagsWorker(PTR_MethodDesc pMD);
+ static HRESULT UpdateActiveILVersions(
+ ULONG cFunctions,
+ ModuleID rgModuleIDs[],
+ mdMethodDef rgMethodDefs[],
+ HRESULT rgHrStatuses[],
+ BOOL fIsRevert);
- HRESULT MarkAllInstantiationsForReJit(
- SharedReJitInfo * pSharedForAllGenericInstantiations,
- AppDomain * pAppDomainToSearch,
- PTR_Module pModuleContainingGenericDefinition,
- mdMethodDef methodDef,
- ReJitManagerJumpStampBatch* pJumpStampBatch,
- CDynArray<ReJitReportErrorWorkItem> * pRejitErrors);
-
- INDEBUG(BaseDomain * m_pDomain;)
- INDEBUG(void Dump(LPCSTR szIntroText);)
- INDEBUG(void AssertRestOfEntriesAreReverted(
- ReJitInfoHash::KeyIterator iter,
- ReJitInfoHash::KeyIterator end);)
-
-
- HRESULT DoJumpStampIfNecessary(MethodDesc* pMD, PCODE pCode);
- HRESULT MarkForReJit(PTR_MethodDesc pMD, SharedReJitInfo * pSharedToReuse, ReJitManagerJumpStampBatch* pJumpStampBatch, CDynArray<ReJitReportErrorWorkItem> * pRejitErrors, SharedReJitInfo ** ppSharedUsed);
- HRESULT MarkForReJit(PTR_Module pModule, mdMethodDef methodDef, ReJitManagerJumpStampBatch* pJumpStampBatch, CDynArray<ReJitReportErrorWorkItem> * pRejitErrors, SharedReJitInfo ** ppSharedUsed);
- HRESULT MarkForReJitHelper(
- PTR_MethodDesc pMD,
- PTR_Module pModule,
- mdMethodDef methodDef,
- SharedReJitInfo * pSharedToReuse,
- ReJitManagerJumpStampBatch* pJumpStampBatch,
- CDynArray<ReJitReportErrorWorkItem> * pRejitErrors,
- /* out */ SharedReJitInfo ** ppSharedUsed);
- HRESULT AddNewReJitInfo(
- PTR_MethodDesc pMD,
+ struct CodeActivationBatch
+ {
+ CodeActivationBatch(CodeVersionManager * pCodeVersionManager) :
+ m_pCodeVersionManager(pCodeVersionManager)
+ {}
+ CodeVersionManager* m_pCodeVersionManager;
+ CDynArray<ILCodeVersion> m_methodsToActivate;
+ };
+
+ class CodeActivationBatchTraits : public DefaultSHashTraits<CodeActivationBatch *>
+ {
+ public:
+ typedef DefaultSHashTraits<CodeActivationBatch *> PARENT;
+ typedef PARENT::element_t element_t;
+ typedef PARENT::count_t count_t;
+ typedef CodeVersionManager * key_t;
+ static key_t GetKey(const element_t &e) { return e->m_pCodeVersionManager; }
+ static BOOL Equals(key_t k1, key_t k2) { return (k1 == k2); }
+ static count_t Hash(key_t k) { return (count_t)k; }
+ static bool IsNull(const element_t &e) { return (e == NULL); }
+ };
+
+ static HRESULT BindILVersion(
+ CodeVersionManager* pCodeVersionManager,
PTR_Module pModule,
mdMethodDef methodDef,
- SharedReJitInfo * pShared,
- ReJitInfo ** ppInfo);
- HRESULT RequestRevertByToken(PTR_Module pModule, mdMethodDef methodDef);
- PTR_ReJitInfo FindReJitInfo(PTR_MethodDesc pMD, PCODE pCodeStart, ReJITID reJitId);
- PTR_ReJitInfo FindNonRevertedReJitInfo(PTR_Module pModule, mdMethodDef methodDef);
- PTR_ReJitInfo FindNonRevertedReJitInfo(PTR_MethodDesc pMD);
- PTR_ReJitInfo FindNonRevertedReJitInfoHelper(PTR_MethodDesc pMD, PTR_Module pModule, mdMethodDef methodDef);
- ReJitInfo* FindPreReJittedReJitInfo(ReJitInfoHash::KeyIterator beginIter, ReJitInfoHash::KeyIterator endIter);
- HRESULT Revert(SharedReJitInfo * pShared, ReJitManagerJumpStampBatch* pJumpStampBatch);
- PCODE DoReJit(ReJitInfo * pInfo);
- ReJitInfoHash::KeyIterator GetBeginIterator(PTR_MethodDesc pMD);
- ReJitInfoHash::KeyIterator GetEndIterator(PTR_MethodDesc pMD);
- ReJitInfoHash::KeyIterator GetBeginIterator(PTR_Module pModule, mdMethodDef methodDef);
- ReJitInfoHash::KeyIterator GetEndIterator(PTR_Module pModule, mdMethodDef methodDef);
- void RemoveReJitInfosFromDomain(AppDomain * pAppDomain);
+ ILCodeVersion *pILCodeVersion);
#endif // FEATURE_REJIT
diff --git a/src/vm/rejit.inl b/src/vm/rejit.inl
index 8662eeaedf..3c42bcea00 100644
--- a/src/vm/rejit.inl
+++ b/src/vm/rejit.inl
@@ -13,149 +13,6 @@
#ifdef FEATURE_REJIT
-inline SharedReJitInfo::InternalFlags SharedReJitInfo::GetState()
-{
- LIMITED_METHOD_CONTRACT;
-
- return (InternalFlags)(m_dwInternalFlags & kStateMask);
-}
-
-inline ReJitInfo::ReJitInfo(PTR_MethodDesc pMD, SharedReJitInfo * pShared) :
- m_key(pMD),
- m_pShared(pShared)
-{
- LIMITED_METHOD_CONTRACT;
-
- CommonInit();
-}
-
-inline ReJitInfo::ReJitInfo(PTR_Module pModule, mdMethodDef methodDef, SharedReJitInfo * pShared) :
- m_key(pModule, methodDef),
- m_pShared(pShared)
-{
- LIMITED_METHOD_CONTRACT;
-
- CommonInit();
-}
-
-inline ReJitInfo::Key::Key() :
- m_pMD(NULL),
- m_methodDef(mdTokenNil),
- m_keyType(kUninitialized)
-{
- LIMITED_METHOD_CONTRACT;
-}
-
-inline ReJitInfo::Key::Key(PTR_MethodDesc pMD) :
- m_pMD(dac_cast<TADDR>(pMD)),
- m_methodDef(mdTokenNil),
- m_keyType(kMethodDesc)
-{
- LIMITED_METHOD_CONTRACT;
-}
-
-inline ReJitInfo::Key::Key(PTR_Module pModule, mdMethodDef methodDef) :
- m_pModule(dac_cast<TADDR>(pModule)),
- m_methodDef(methodDef),
- m_keyType(kMetadataToken)
-{
- LIMITED_METHOD_CONTRACT;
-}
-
-inline ReJitInfo::Key ReJitInfo::GetKey()
-{
- LIMITED_METHOD_CONTRACT;
-
- return m_key;
-}
-
-inline ReJitInfo::InternalFlags ReJitInfo::GetState()
-{
- LIMITED_METHOD_CONTRACT;
-
- return (InternalFlags)(m_dwInternalFlags & kStateMask);
-}
-
-inline PTR_MethodDesc ReJitInfo::GetMethodDesc()
-{
- LIMITED_METHOD_CONTRACT;
-
- _ASSERTE(m_key.m_keyType == Key::kMethodDesc);
- return PTR_MethodDesc(m_key.m_pMD);
-}
-
-inline void ReJitInfo::GetModuleAndToken(Module ** ppModule, mdMethodDef * pMethodDef)
-{
- LIMITED_METHOD_CONTRACT;
-
- _ASSERTE(ppModule != NULL);
- _ASSERTE(pMethodDef != NULL);
- _ASSERTE(m_key.m_keyType == Key::kMetadataToken);
-
- *ppModule = PTR_Module(m_key.m_pModule);
- *pMethodDef = (mdMethodDef) m_key.m_methodDef;
-}
-
-#ifdef _DEBUG
-inline BOOL ReJitInfo::CodeIsSaved()
-{
- LIMITED_METHOD_CONTRACT;
-
- for (size_t i=0; i < sizeof(m_rgSavedCode); i++)
- {
- if (m_rgSavedCode[i] != 0)
- return TRUE;
- }
- return FALSE;
-}
-#endif //_DEBUG
-
-// static
-inline ReJitInfoTraits::key_t ReJitInfoTraits::GetKey(const element_t &e)
-{
- LIMITED_METHOD_CONTRACT;
-
- return e->GetKey();
-}
-
-// static
-inline BOOL ReJitInfoTraits::Equals(key_t k1, key_t k2)
-{
- LIMITED_METHOD_CONTRACT;
-
- // Always use the values of the TADDRs of the MethodDesc * and Module * when treating
- // them as lookup keys into the SHash.
-
- if (k1.m_keyType == ReJitInfo::Key::kMethodDesc)
- {
- return ((k2.m_keyType == ReJitInfo::Key::kMethodDesc) &&
- (dac_cast<TADDR>(PTR_MethodDesc(k1.m_pMD)) ==
- dac_cast<TADDR>(PTR_MethodDesc(k2.m_pMD))));
- }
-
- _ASSERTE(k1.m_keyType == ReJitInfo::Key::kMetadataToken);
- return ((k2.m_keyType == ReJitInfo::Key::kMetadataToken) &&
- (dac_cast<TADDR>(PTR_Module(k1.m_pModule)) ==
- dac_cast<TADDR>(PTR_Module(k2.m_pModule))) &&
- (k1.m_methodDef == k2.m_methodDef));
-}
-
-// static
-inline ReJitInfoTraits::count_t ReJitInfoTraits::Hash(key_t k)
-{
- LIMITED_METHOD_CONTRACT;
-
- return ReJitInfo::Hash(k);
-}
-
-// static
-inline bool ReJitInfoTraits::IsNull(const element_t &e)
-{
- LIMITED_METHOD_CONTRACT;
-
- return e == NULL;
-}
-
// static
inline void ReJitManager::InitStatic()
{
@@ -172,92 +29,9 @@ inline BOOL ReJitManager::IsReJITEnabled()
return CORProfilerEnableRejit();
}
-inline ReJitManager::ReJitInfoHash::KeyIterator ReJitManager::GetBeginIterator(PTR_MethodDesc pMD)
-{
- LIMITED_METHOD_CONTRACT;
-#ifndef DACCESS_COMPILE
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
-#endif
- return m_table.Begin(ReJitInfo::Key(pMD));
-}
-
-inline ReJitManager::ReJitInfoHash::KeyIterator ReJitManager::GetEndIterator(PTR_MethodDesc pMD)
-{
- LIMITED_METHOD_CONTRACT;
-#ifndef DACCESS_COMPILE
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
-#endif
- return m_table.End(ReJitInfo::Key(pMD));
-}
-
-inline ReJitManager::ReJitInfoHash::KeyIterator ReJitManager::GetBeginIterator(PTR_Module pModule, mdMethodDef methodDef)
-{
- LIMITED_METHOD_CONTRACT;
#ifndef DACCESS_COMPILE
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
-#endif
- return m_table.Begin(ReJitInfo::Key(pModule, methodDef));
-}
-
-inline ReJitManager::ReJitInfoHash::KeyIterator ReJitManager::GetEndIterator(PTR_Module pModule, mdMethodDef methodDef)
-{
- LIMITED_METHOD_CONTRACT;
-#ifndef DACCESS_COMPILE
- _ASSERTE(m_crstTable.OwnedByCurrentThread());
-#endif
- return m_table.End(ReJitInfo::Key(pModule, methodDef));
-}
-
-#ifdef _DEBUG
-inline BOOL ReJitManager::IsTableCrstOwnedByCurrentThread()
-{
- LIMITED_METHOD_CONTRACT;
-
- return m_crstTable.OwnedByCurrentThread();
-}
-#endif //_DEBUG
-
-
-inline HRESULT ReJitManager::MarkForReJit(
- PTR_MethodDesc pMD,
- SharedReJitInfo * pSharedToReuse,
- ReJitManagerJumpStampBatch* pJumpStampBatch,
- CDynArray<ReJitReportErrorWorkItem> * pRejitErrors,
- /* out */ SharedReJitInfo ** ppSharedUsed)
-{
- WRAPPER_NO_CONTRACT;
-
- return MarkForReJitHelper(pMD, NULL, mdTokenNil, pSharedToReuse, pJumpStampBatch, pRejitErrors, ppSharedUsed);
-}
-
-inline HRESULT ReJitManager::MarkForReJit(
- PTR_Module pModule,
- mdMethodDef methodDef,
- ReJitManagerJumpStampBatch* pJumpStampBatch,
- CDynArray<ReJitReportErrorWorkItem> * pRejitErrors,
- /* out */ SharedReJitInfo ** ppSharedUsed)
-{
- WRAPPER_NO_CONTRACT;
-
- return MarkForReJitHelper(NULL, pModule, methodDef, NULL, pJumpStampBatch, pRejitErrors, ppSharedUsed);
-}
-
-inline PTR_ReJitInfo ReJitManager::FindNonRevertedReJitInfo(PTR_Module pModule, mdMethodDef methodDef)
-{
- WRAPPER_NO_CONTRACT;
-
- return FindNonRevertedReJitInfoHelper(NULL, pModule, methodDef);
-}
-
-inline PTR_ReJitInfo ReJitManager::FindNonRevertedReJitInfo(PTR_MethodDesc pMD)
-{
- WRAPPER_NO_CONTRACT;
-
- return FindNonRevertedReJitInfoHelper(pMD, NULL, NULL);
-}
-
//static
-inline void ReJitManager::ReportReJITError(ReJitReportErrorWorkItem* pErrorRecord)
+inline void ReJitManager::ReportReJITError(CodeVersionManager::CodePublishError* pErrorRecord)
{
CONTRACTL
{
@@ -298,14 +72,7 @@ inline void ReJitManager::ReportReJITError(Module* pModule, mdMethodDef methodDe
}
#endif // PROFILING_SUPPORTED
}
-
-inline ReJitManager::TableLockHolder::TableLockHolder(ReJitManager * pReJitManager)
-#ifdef FEATURE_REJIT
- : CrstHolder(&pReJitManager->m_crstTable)
-#endif // FEATURE_REJIT
-{
- WRAPPER_NO_CONTRACT;
-}
+#endif // DACCESS_COMPILE
#else // FEATURE_REJIT
@@ -313,32 +80,16 @@ inline ReJitManager::TableLockHolder::TableLockHolder(ReJitManager * pReJitManag
// stubs so the rest of the VM doesn't have to be littered with #ifdef FEATURE_REJIT
// static
-inline PCODE ReJitManager::DoReJitIfNecessary(PTR_MethodDesc)
-{
- return NULL;
-}
-
-// static
inline BOOL ReJitManager::IsReJITEnabled()
{
return FALSE;
}
-// static
-inline DWORD ReJitManager::GetCurrentReJitFlags(PTR_MethodDesc)
-{
- return 0;
-}
-
// static
inline void ReJitManager::InitStatic()
{
}
-inline ReJitManager::TableLockHolder::TableLockHolder(ReJitManager *)
-{
-}
-
#endif // FEATURE_REJIT
diff --git a/src/vm/runtimehandles.cpp b/src/vm/runtimehandles.cpp
index d3dad5a596..952be6206e 100644
--- a/src/vm/runtimehandles.cpp
+++ b/src/vm/runtimehandles.cpp
@@ -28,7 +28,6 @@
#include "contractimpl.h"
#include "dynamicmethod.h"
#include "peimagelayout.inl"
-#include "security.h"
#include "eventtrace.h"
#include "invokeutil.h"
@@ -135,9 +134,7 @@ static BOOL CheckCAVisibilityFromDecoratedType(MethodTable* pCAMT, MethodDesc* p
dwAttr,
pCACtor,
NULL,
- *AccessCheckOptions::s_pNormalAccessChecks,
- FALSE,
- FALSE);
+ *AccessCheckOptions::s_pNormalAccessChecks);
}
BOOL QCALLTYPE RuntimeMethodHandle::IsCAVisibleFromDecoratedType(
@@ -970,6 +967,24 @@ FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::IsInterface, ReflectClassBaseObject *pTy
}
FCIMPLEND;
+
+FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::IsByRefLike, ReflectClassBaseObject *pTypeUNSAFE)
+{
+ CONTRACTL {
+ FCALL_CHECK;
+ }
+ CONTRACTL_END;
+
+ REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
+
+ _ASSERTE(refType != NULL);
+
+ TypeHandle typeHandle = refType->GetType();
+
+ FC_RETURN_BOOL(typeHandle.IsByRefLike());
+}
+FCIMPLEND
+
BOOL
QCALLTYPE
RuntimeTypeHandle::IsVisible(
@@ -996,37 +1011,6 @@ RuntimeTypeHandle::IsVisible(
return fIsExternallyVisible;
} // RuntimeTypeHandle::IsVisible
-FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::HasProxyAttribute, ReflectClassBaseObject *pTypeUNSAFE) {
- CONTRACTL {
- FCALL_CHECK;
- }
- CONTRACTL_END;
-
- REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
-
- if (refType == NULL)
- FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle"));
-
- TypeHandle typeHandle = refType->GetType();
-
- // TODO: Justify this
- if (typeHandle.IsGenericVariable())
- FC_RETURN_BOOL(FALSE);
-
- if (typeHandle.IsTypeDesc()) {
- if (!typeHandle.IsArray())
- FC_RETURN_BOOL(FALSE);
- }
-
- MethodTable* pMT= typeHandle.GetMethodTable();
-
- if (!pMT)
- FCThrowRes(kArgumentException, W("Arg_InvalidHandle"));
-
- FC_RETURN_BOOL(pMT->GetClass()->HasRemotingProxyAttribute());
-}
-FCIMPLEND
-
FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::IsComObject, ReflectClassBaseObject *pTypeUNSAFE, CLR_BOOL isGenericCOM) {
#ifdef FEATURE_COMINTEROP
CONTRACTL {
@@ -2216,6 +2200,14 @@ FCIMPL1(FC_BOOL_RET, RuntimeMethodHandle::IsGenericMethodDefinition, MethodDesc
}
FCIMPLEND
+FCIMPL1(INT32, RuntimeMethodHandle::GetGenericParameterCount, MethodDesc * pMethod)
+{
+ FCALL_CONTRACT;
+
+ return pMethod->GetNumGenericMethodArgs();
+}
+FCIMPLEND
+
FCIMPL1(FC_BOOL_RET, RuntimeMethodHandle::IsDynamicMethod, MethodDesc * pMethod)
{
FCALL_CONTRACT;
@@ -3115,3 +3107,4 @@ void QCALLTYPE RuntimeMethodHandle::GetCallerType(QCall::StackCrawlMarkHandle pS
return;
}
+
diff --git a/src/vm/runtimehandles.h b/src/vm/runtimehandles.h
index fc18d6f65c..8978e20946 100644
--- a/src/vm/runtimehandles.h
+++ b/src/vm/runtimehandles.h
@@ -126,8 +126,9 @@ public:
// Static method on RuntimeTypeHandle
static FCDECL1(Object*, Allocate, ReflectClassBaseObject *refType) ; //A.CI work
- static FCDECL4(Object*, CreateInstance, ReflectClassBaseObject* refThisUNSAFE,
+ static FCDECL5(Object*, CreateInstance, ReflectClassBaseObject* refThisUNSAFE,
CLR_BOOL publicOnly,
+ CLR_BOOL wrapExceptions,
CLR_BOOL *pbCanBeCached,
MethodDesc** pConstructor);
@@ -193,11 +194,11 @@ public:
static FCDECL1(ReflectClassBaseObject*, GetDeclaringType, ReflectClassBaseObject* pType);
static FCDECL1(FC_BOOL_RET, IsValueType, ReflectClassBaseObject* pType);
static FCDECL1(FC_BOOL_RET, IsInterface, ReflectClassBaseObject* pType);
+ static FCDECL1(FC_BOOL_RET, IsByRefLike, ReflectClassBaseObject* pType);
static
BOOL QCALLTYPE IsVisible(EnregisteredTypeHandle pTypeHandle);
- static FCDECL1(FC_BOOL_RET, HasProxyAttribute, ReflectClassBaseObject *pType);
static FCDECL2(FC_BOOL_RET, IsComObject, ReflectClassBaseObject *pType, CLR_BOOL isGenericCOM);
static FCDECL2(FC_BOOL_RET, CanCastTo, ReflectClassBaseObject *pType, ReflectClassBaseObject *pTarget);
static FCDECL2(FC_BOOL_RET, IsInstanceOfType, ReflectClassBaseObject *pType, Object *object);
@@ -266,7 +267,7 @@ class RuntimeMethodHandle {
public:
static FCDECL1(ReflectMethodObject*, GetCurrentMethod, StackCrawlMark* stackMark);
- static FCDECL4(Object*, InvokeMethod, Object *target, PTRArray *objs, SignatureNative* pSig, CLR_BOOL fConstructor);
+ static FCDECL5(Object*, InvokeMethod, Object *target, PTRArray *objs, SignatureNative* pSig, CLR_BOOL fConstructor, CLR_BOOL fWrapExceptions);
struct StreamingContextData {
Object * additionalContext; // additionalContex was changed from OBJECTREF to Object to avoid having a
@@ -338,6 +339,9 @@ public:
static
void QCALLTYPE StripMethodInstantiation(MethodDesc * pMethod, QCall::ObjectHandleOnStack refMethod);
+ static
+ FCDECL1(INT32, GetGenericParameterCount, MethodDesc * pMethod);
+
// see comment in the cpp file
static FCDECL3(MethodDesc*, GetStubIfNeeded, MethodDesc *pMethod, ReflectClassBaseObject *pType, PtrArray* instArray);
static FCDECL2(MethodDesc*, GetMethodFromCanonical, MethodDesc *pMethod, PTR_ReflectClassBaseObject pType);
diff --git a/src/vm/sampleprofiler.cpp b/src/vm/sampleprofiler.cpp
index e4721577ae..4a858e9b4a 100644
--- a/src/vm/sampleprofiler.cpp
+++ b/src/vm/sampleprofiler.cpp
@@ -13,7 +13,7 @@
Volatile<BOOL> SampleProfiler::s_profilingEnabled = false;
Thread* SampleProfiler::s_pSamplingThread = NULL;
-const GUID SampleProfiler::s_providerID = {0x3c530d44,0x97ae,0x513a,{0x1e,0x6d,0x78,0x3e,0x8f,0x8e,0x03,0xa9}}; // {3c530d44-97ae-513a-1e6d-783e8f8e03a9}
+const WCHAR* SampleProfiler::s_providerName = W("Microsoft-DotNETCore-SampleProfiler");
EventPipeProvider* SampleProfiler::s_pEventPipeProvider = NULL;
EventPipeEvent* SampleProfiler::s_pThreadTimeEvent = NULL;
BYTE* SampleProfiler::s_pPayloadExternal = NULL;
@@ -36,7 +36,7 @@ void SampleProfiler::Enable()
if(s_pEventPipeProvider == NULL)
{
- s_pEventPipeProvider = EventPipe::CreateProvider(s_providerID);
+ s_pEventPipeProvider = EventPipe::CreateProvider(SL(s_providerName));
s_pThreadTimeEvent = s_pEventPipeProvider->AddEvent(
0, /* eventID */
0, /* keywords */
diff --git a/src/vm/sampleprofiler.h b/src/vm/sampleprofiler.h
index 02eb6b39cd..51290b4d9c 100644
--- a/src/vm/sampleprofiler.h
+++ b/src/vm/sampleprofiler.h
@@ -50,7 +50,7 @@ class SampleProfiler
static Thread *s_pSamplingThread;
// The provider and event emitted by the profiler.
- static const GUID s_providerID;
+ static const WCHAR* s_providerName;
static EventPipeProvider *s_pEventPipeProvider;
static EventPipeEvent *s_pThreadTimeEvent;
diff --git a/src/vm/security.cpp b/src/vm/security.cpp
deleted file mode 100644
index 7a6c8b82ea..0000000000
--- a/src/vm/security.cpp
+++ /dev/null
@@ -1,48 +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 "common.h"
-
-#include "security.h"
-
-//
-// The method in this file have nothing to do with security. They historically lived in security subsystem.
-// TODO: Move them to move appropriate place.
-//
-
-void Security::CopyByteArrayToEncoding(IN U1ARRAYREF* pArray, OUT PBYTE* ppbData, OUT DWORD* pcbData)
-{
- CONTRACTL {
- THROWS;
- GC_NOTRIGGER;
- MODE_COOPERATIVE;
- PRECONDITION(CheckPointer(pArray));
- PRECONDITION(CheckPointer(ppbData));
- PRECONDITION(CheckPointer(pcbData));
- PRECONDITION(*pArray != NULL);
- } CONTRACTL_END;
-
- DWORD size = (DWORD) (*pArray)->GetNumComponents();
- *ppbData = new BYTE[size];
- *pcbData = size;
-
- CopyMemory(*ppbData, (*pArray)->GetDirectPointerToNonObjectElements(), size);
-}
-
-void Security::CopyEncodingToByteArray(IN PBYTE pbData, IN DWORD cbData, IN OBJECTREF* pArray)
-{
- CONTRACTL {
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- } CONTRACTL_END;
-
- U1ARRAYREF pObj;
- _ASSERTE(pArray);
-
- pObj = (U1ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_U1,cbData);
- memcpyNoGCRefs(pObj->m_Array, pbData, cbData);
- *pArray = (OBJECTREF) pObj;
-}
diff --git a/src/vm/security.h b/src/vm/security.h
deleted file mode 100644
index fa4840998e..0000000000
--- a/src/vm/security.h
+++ /dev/null
@@ -1,93 +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.
-//
-
-#ifndef __security_h__
-#define __security_h__
-
-//
-// Stubbed out implementation of security subsystem
-// TODO: Eliminate this file
-//
-
-enum SecurityStackWalkType
-{
- SSWT_DECLARATIVE_DEMAND = 1,
- SSWT_IMPERATIVE_DEMAND = 2,
- SSWT_DEMAND_FROM_NATIVE = 3,
- SSWT_IMPERATIVE_ASSERT = 4,
- SSWT_DENY_OR_PERMITONLY = 5,
- SSWT_LATEBOUND_LINKDEMAND = 6,
- SSWT_COUNT_OVERRIDES = 7,
- SSWT_GET_ZONE_AND_URL = 8,
-};
-
-// special flags
-#define SECURITY_UNMANAGED_CODE 0
-#define SECURITY_SKIP_VER 1
-#define REFLECTION_TYPE_INFO 2
-#define SECURITY_ASSERT 3
-#define REFLECTION_MEMBER_ACCESS 4
-#define SECURITY_SERIALIZATION 5
-#define REFLECTION_RESTRICTED_MEMBER_ACCESS 6
-#define SECURITY_FULL_TRUST 7
-#define SECURITY_BINDING_REDIRECTS 8
-
-// Ultimately this will become the only interface through
-// which the VM will access security code.
-
-namespace Security
-{
- inline BOOL IsTransparencyEnforcementEnabled() { return false; }
-
- inline BOOL CanCallUnmanagedCode(Module *pModule) { return true; }
-
-#ifndef DACCESS_COMPILE
- inline BOOL CanTailCall(MethodDesc* pMD) { return true; }
- inline BOOL CanHaveRVA(Assembly * pAssembly) { return true; }
- inline BOOL CanAccessNonVerifiableExplicitField(MethodDesc* pMD) { return true; }
- inline BOOL CanSkipVerification(MethodDesc * pMethod) { return true; }
-#endif
-
- inline BOOL CanSkipVerification(DomainAssembly * pAssembly) { return true; }
-
- // ----------------------------------------
- // SecurityAttributes
- // ----------------------------------------
-
- void CopyByteArrayToEncoding(IN U1ARRAYREF* pArray, OUT PBYTE* pbData, OUT DWORD* cbData);
- void CopyEncodingToByteArray(IN PBYTE pbData, IN DWORD cbData, IN OBJECTREF* pArray);
-
- inline void SpecialDemand(SecurityStackWalkType eType, DWORD whatPermission) { }
-
- // Transparency checks
- inline BOOL IsMethodTransparent(MethodDesc * pMD) { return false; }
- inline BOOL IsMethodCritical(MethodDesc * pMD) { return true; }
- inline BOOL IsMethodSafeCritical(MethodDesc * pMD) { return false; }
-
- inline BOOL IsTypeCritical(MethodTable *pMT) { return true; }
- inline BOOL IsTypeSafeCritical(MethodTable *pMT) { return false; }
- inline BOOL IsTypeTransparent(MethodTable * pMT) { return false; }
- inline BOOL IsTypeAllTransparent(MethodTable * pMT) { return false; }
-
- inline BOOL IsFieldTransparent(FieldDesc * pFD) { return false; }
- inline BOOL IsFieldCritical(FieldDesc * pFD) { return true; }
- inline BOOL IsFieldSafeCritical(FieldDesc * pFD) { return false; }
-
- inline BOOL IsTokenTransparent(Module* pModule, mdToken token) { return false; }
-
- inline BOOL CheckCriticalAccess(AccessCheckContext* pContext,
- MethodDesc* pOptionalTargetMethod = NULL,
- FieldDesc* pOptionalTargetField = NULL,
- MethodTable * pOptionalTargetType = NULL)
- {
- return true;
- }
-
- inline void CheckLinkDemandAgainstAppDomain(MethodDesc *pMD)
- {
- }
-};
-
-#endif
diff --git a/src/vm/siginfo.cpp b/src/vm/siginfo.cpp
index 30dcf0f1ad..40a55cb6f0 100644
--- a/src/vm/siginfo.cpp
+++ b/src/vm/siginfo.cpp
@@ -18,7 +18,6 @@
#include "field.h"
#include "eeconfig.h"
#include "runtimehandles.h" // for SignatureNative
-#include "security.h" // for CanSkipVerification
#include "winwrap.h"
#include <formattype.h>
#include "sigbuilder.h"
@@ -1531,12 +1530,11 @@ TypeHandle SigPointer::GetTypeHandleThrowing(
if (typFromSigIsClass != typLoadedIsClass)
{
- if((pModule->GetMDImport()->GetMetadataStreamVersion() != MD_STREAM_VER_1X)
- || !Security::CanSkipVerification(pModule->GetDomainAssembly()))
+ if (pModule->GetMDImport()->GetMetadataStreamVersion() != MD_STREAM_VER_1X)
{
- pOrigModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(),
- typeToken,
- BFA_CLASSLOAD_VALUETYPEMISMATCH);
+ pOrigModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(),
+ typeToken,
+ BFA_CLASSLOAD_VALUETYPEMISMATCH);
}
}
}
diff --git a/src/vm/stdinterfaces.cpp b/src/vm/stdinterfaces.cpp
index 34ba39019e..fa2d2a7b23 100644
--- a/src/vm/stdinterfaces.cpp
+++ b/src/vm/stdinterfaces.cpp
@@ -1262,15 +1262,12 @@ Dispatch_GetIDsOfNames(IDispatch* pDisp, REFIID riid, __in_ecount(cNames) OLECHA
if (pCMT->HasInvisibleParent())
return E_NOTIMPL;
- // Use the right implementation based on the flags in the ComMethodTable and ComCallWrapperTemplate
- if (!pCMT->IsDefinedInUntrustedCode())
+ ComCallWrapperTemplate *pTemplate = MapIUnknownToWrapper(pDisp)->GetComCallWrapperTemplate();
+ if (pTemplate->IsUseOleAutDispatchImpl())
{
- ComCallWrapperTemplate *pTemplate = MapIUnknownToWrapper(pDisp)->GetComCallWrapperTemplate();
- if (pTemplate->IsUseOleAutDispatchImpl())
- {
- return OleAutDispatchImpl_GetIDsOfNames(pDisp, riid, rgszNames, cNames, lcid, rgdispid);
- }
+ return OleAutDispatchImpl_GetIDsOfNames(pDisp, riid, rgszNames, cNames, lcid, rgdispid);
}
+
return InternalDispatchImpl_GetIDsOfNames(pDisp, riid, rgszNames, cNames, lcid, rgdispid);
}
@@ -1305,14 +1302,10 @@ Dispatch_Invoke
if (pCMT->HasInvisibleParent())
return E_NOTIMPL;
- // Use the right implementation based on the flags in the ComMethodTable.
- if (!pCMT->IsDefinedInUntrustedCode())
+ ComCallWrapperTemplate *pTemplate = MapIUnknownToWrapper(pDisp)->GetComCallWrapperTemplate();
+ if (pTemplate->IsUseOleAutDispatchImpl())
{
- ComCallWrapperTemplate *pTemplate = MapIUnknownToWrapper(pDisp)->GetComCallWrapperTemplate();
- if (pTemplate->IsUseOleAutDispatchImpl())
- {
- return OleAutDispatchImpl_Invoke(pDisp, dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
- }
+ return OleAutDispatchImpl_Invoke(pDisp, dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
}
return InternalDispatchImpl_Invoke(pDisp, dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
diff --git a/src/vm/stubhelpers.cpp b/src/vm/stubhelpers.cpp
index 837d88f65a..ead312d042 100644
--- a/src/vm/stubhelpers.cpp
+++ b/src/vm/stubhelpers.cpp
@@ -16,7 +16,6 @@
#include "dllimport.h"
#include "fieldmarshaler.h"
#include "comdelegate.h"
-#include "security.h"
#include "eventtrace.h"
#include "comdatetime.h"
#include "gcheaputilities.h"
@@ -1691,7 +1690,7 @@ FCIMPL4(Object*, StubHelpers::GetCOMHRExceptionObject, HRESULT hr, MethodDesc *p
}
}
- GetExceptionForHR(hr, pErrInfo, fForWinRT, &oThrowable, pResErrorInfo, bHasNonCLRLanguageErrorObject);
+ GetExceptionForHR(hr, pErrInfo, !fForWinRT, &oThrowable, pResErrorInfo, bHasNonCLRLanguageErrorObject);
}
HELPER_METHOD_FRAME_END();
diff --git a/src/vm/syncblk.inl b/src/vm/syncblk.inl
index 37d6748525..cb6b280228 100644
--- a/src/vm/syncblk.inl
+++ b/src/vm/syncblk.inl
@@ -178,7 +178,7 @@ FORCEINLINE AwareLock::LeaveHelperAction AwareLock::LeaveHelper(Thread* pCurThre
_ASSERTE((size_t)m_MonitorHeld & 1);
_ASSERTE(m_Recursion >= 1);
-#if defined(_DEBUG) && defined(TRACK_SYNC)
+#if defined(_DEBUG) && defined(TRACK_SYNC) && !defined(CROSSGEN_COMPILE)
// The best place to grab this is from the ECall frame
Frame *pFrame = pCurThread->GetFrame();
int caller = (pFrame && pFrame != FRAME_TOP ? (int) pFrame->GetReturnAddress() : -1);
diff --git a/src/vm/threadpoolrequest.cpp b/src/vm/threadpoolrequest.cpp
index a1ec4b087e..523a0631d6 100644
--- a/src/vm/threadpoolrequest.cpp
+++ b/src/vm/threadpoolrequest.cpp
@@ -21,19 +21,18 @@
#include "object.h"
#include "field.h"
#include "excep.h"
-#include "security.h"
#include "eeconfig.h"
#include "corhost.h"
#include "nativeoverlapped.h"
#include "appdomain.inl"
-BYTE PerAppDomainTPCountList::s_padding[64 - sizeof(LONG)];
+BYTE PerAppDomainTPCountList::s_padding[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
// Make this point to unmanaged TP in case, no appdomains have initialized yet.
// Cacheline aligned, hot variable
-DECLSPEC_ALIGN(64) LONG PerAppDomainTPCountList::s_ADHint = -1;
+DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) LONG PerAppDomainTPCountList::s_ADHint = -1;
// Move out of from preceeding variables' cache line
-DECLSPEC_ALIGN(64) UnManagedPerAppDomainTPCount PerAppDomainTPCountList::s_unmanagedTPCount;
+DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) UnManagedPerAppDomainTPCount PerAppDomainTPCountList::s_unmanagedTPCount;
//The list of all per-appdomain work-request counts.
ArrayListStatic PerAppDomainTPCountList::s_appDomainIndexList;
diff --git a/src/vm/threadpoolrequest.h b/src/vm/threadpoolrequest.h
index 8d2c7e486a..3d2dc3da82 100644
--- a/src/vm/threadpoolrequest.h
+++ b/src/vm/threadpoolrequest.h
@@ -20,6 +20,8 @@
#ifndef _THREADPOOL_REQUEST_H
#define _THREADPOOL_REQUEST_H
+#include "util.hpp"
+
#define TP_QUANTUM 2
#define UNUSED_THREADPOOL_INDEX (DWORD)-1
@@ -181,11 +183,11 @@ public:
private:
ADID m_id;
TPIndex m_index;
- DECLSPEC_ALIGN(64) struct {
- BYTE m_padding1[64 - sizeof(LONG)];
+ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) struct {
+ BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
// Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange
LONG m_numRequestsPending;
- BYTE m_padding2[64];
+ BYTE m_padding2[MAX_CACHE_LINE_SIZE];
};
};
@@ -286,11 +288,11 @@ public:
private:
SpinLock m_lock;
ULONG m_NumRequests;
- DECLSPEC_ALIGN(64) struct {
- BYTE m_padding1[64 - sizeof(LONG)];
+ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) struct {
+ BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
// Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange
LONG m_outstandingThreadRequestCount;
- BYTE m_padding2[64];
+ BYTE m_padding2[MAX_CACHE_LINE_SIZE];
};
};
@@ -351,12 +353,12 @@ public:
private:
static DWORD FindFirstFreeTpEntry();
- static BYTE s_padding[64 - sizeof(LONG)];
- DECLSPEC_ALIGN(64) static LONG s_ADHint;
- DECLSPEC_ALIGN(64) static UnManagedPerAppDomainTPCount s_unmanagedTPCount;
+ static BYTE s_padding[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
+ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static LONG s_ADHint;
+ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static UnManagedPerAppDomainTPCount s_unmanagedTPCount;
//The list of all per-appdomain work-request counts.
static ArrayListStatic s_appDomainIndexList;
};
-#endif //_THREADPOOL_REQUEST_H \ No newline at end of file
+#endif //_THREADPOOL_REQUEST_H
diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp
index 59fec2bdc3..91373930e9 100644
--- a/src/vm/threads.cpp
+++ b/src/vm/threads.cpp
@@ -495,7 +495,6 @@ void Thread::ChooseThreadCPUGroupAffinity()
}
CONTRACTL_END;
-#ifndef FEATURE_PAL
if (!CPUGroupInfo::CanEnableGCCPUGroups() || !CPUGroupInfo::CanEnableThreadUseAllCpuGroups())
return;
@@ -515,7 +514,6 @@ void Thread::ChooseThreadCPUGroupAffinity()
CPUGroupInfo::SetThreadGroupAffinity(GetThreadHandle(), &groupAffinity, NULL);
m_wCPUGroup = groupAffinity.Group;
m_pAffinityMask = groupAffinity.Mask;
-#endif // !FEATURE_PAL
}
void Thread::ClearThreadCPUGroupAffinity()
@@ -527,7 +525,6 @@ void Thread::ClearThreadCPUGroupAffinity()
}
CONTRACTL_END;
-#ifndef FEATURE_PAL
if (!CPUGroupInfo::CanEnableGCCPUGroups() || !CPUGroupInfo::CanEnableThreadUseAllCpuGroups())
return;
@@ -545,7 +542,6 @@ void Thread::ClearThreadCPUGroupAffinity()
m_wCPUGroup = 0;
m_pAffinityMask = 0;
-#endif // !FEATURE_PAL
}
DWORD Thread::StartThread()
@@ -1367,6 +1363,8 @@ void InitThreadManager()
}
CONTRACTL_END;
+ Thread::s_initializeYieldProcessorNormalizedCrst.Init(CrstLeafLock);
+
// All patched helpers should fit into one page.
// If you hit this assert on retail build, there is most likely problem with BBT script.
_ASSERTE_ALL_BUILDS("clr/src/VM/threads.cpp", (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart < (ptrdiff_t)GetOsPageSize());
@@ -1582,7 +1580,7 @@ void Dbg_TrackSyncStack::EnterSync(UINT_PTR caller, void *pAwareLock)
STRESS_LOG4(LF_SYNC, LL_INFO100, "Dbg_TrackSyncStack::EnterSync, IP=%p, Recursion=%d, MonitorHeld=%d, HoldingThread=%p.\n",
caller,
((AwareLock*)pAwareLock)->m_Recursion,
- ((AwareLock*)pAwareLock)->m_MonitorHeld,
+ ((AwareLock*)pAwareLock)->m_MonitorHeld.LoadWithoutBarrier(),
((AwareLock*)pAwareLock)->m_HoldingThread );
if (m_Active)
@@ -1608,7 +1606,7 @@ void Dbg_TrackSyncStack::LeaveSync(UINT_PTR caller, void *pAwareLock)
STRESS_LOG4(LF_SYNC, LL_INFO100, "Dbg_TrackSyncStack::LeaveSync, IP=%p, Recursion=%d, MonitorHeld=%d, HoldingThread=%p.\n",
caller,
((AwareLock*)pAwareLock)->m_Recursion,
- ((AwareLock*)pAwareLock)->m_MonitorHeld,
+ ((AwareLock*)pAwareLock)->m_MonitorHeld.LoadWithoutBarrier(),
((AwareLock*)pAwareLock)->m_HoldingThread );
if (m_Active)
@@ -2017,10 +2015,8 @@ Thread::Thread()
m_fGCSpecial = FALSE;
-#if !defined(FEATURE_PAL)
m_wCPUGroup = 0;
m_pAffinityMask = 0;
-#endif
m_pAllLoggedTypes = NULL;
@@ -2530,7 +2526,7 @@ void UndoRevert(BOOL bReverted, HANDLE hToken)
// We don't want ::CreateThread() calls scattered throughout the source. So gather
// them all here.
-BOOL Thread::CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, void *args)
+BOOL Thread::CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, void *args, LPCWSTR pName)
{
CONTRACTL {
NOTHROW;
@@ -2557,6 +2553,7 @@ BOOL Thread::CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, voi
bRet = CreateNewOSThread(stackSize, start, args);
#ifndef FEATURE_PAL
UndoRevert(bReverted, token);
+ SetThreadName(m_ThreadHandle, pName);
#endif // !FEATURE_PAL
return bRet;
@@ -11749,3 +11746,87 @@ ULONGLONG Thread::QueryThreadProcessorUsage()
return ullCurrentUsage - ullPreviousUsage;
}
#endif // FEATURE_APPDOMAIN_RESOURCE_MONITORING
+
+CrstStatic Thread::s_initializeYieldProcessorNormalizedCrst;
+int Thread::s_yieldsPerNormalizedYield = 0;
+int Thread::s_optimalMaxNormalizedYieldsPerSpinIteration = 0;
+
+void Thread::InitializeYieldProcessorNormalized()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ CrstHolder lock(&s_initializeYieldProcessorNormalizedCrst);
+
+ if (IsYieldProcessorNormalizedInitialized())
+ {
+ return;
+ }
+
+ // Intel pre-Skylake processor: measured typically 14-17 cycles per yield
+ // Intel post-Skylake processor: measured typically 125-150 cycles per yield
+ const int DefaultYieldsPerNormalizedYield = 1; // defaults are for when no measurement is done
+ const int DefaultOptimalMaxNormalizedYieldsPerSpinIteration = 64; // tuned for pre-Skylake processors, for post-Skylake it should be 7
+ const int MeasureDurationMs = 10;
+ const int MaxYieldsPerNormalizedYield = 10; // measured typically 8-9 on pre-Skylake
+ const int MinNsPerNormalizedYield = 37; // measured typically 37-46 on post-Skylake
+ const int NsPerOptimialMaxSpinIterationDuration = 272; // approx. 900 cycles, measured 281 on pre-Skylake, 263 on post-Skylake
+ const int NsPerSecond = 1000 * 1000 * 1000;
+
+ LARGE_INTEGER li;
+ if (!QueryPerformanceFrequency(&li) || (ULONGLONG)li.QuadPart < 1000 / MeasureDurationMs)
+ {
+ // High precision clock not available or clock resolution is too low, resort to defaults
+ s_yieldsPerNormalizedYield = DefaultYieldsPerNormalizedYield;
+ s_optimalMaxNormalizedYieldsPerSpinIteration = DefaultOptimalMaxNormalizedYieldsPerSpinIteration;
+ return;
+ }
+ ULONGLONG ticksPerSecond = li.QuadPart;
+
+ // Measure the nanosecond delay per yield
+ ULONGLONG measureDurationTicks = ticksPerSecond / (1000 / MeasureDurationMs);
+ unsigned int yieldCount = 0;
+ QueryPerformanceCounter(&li);
+ ULONGLONG startTicks = li.QuadPart;
+ ULONGLONG elapsedTicks;
+ do
+ {
+ for (int i = 0; i < 10; ++i)
+ {
+ YieldProcessor();
+ }
+ yieldCount += 10;
+
+ QueryPerformanceCounter(&li);
+ ULONGLONG nowTicks = li.QuadPart;
+ elapsedTicks = nowTicks - startTicks;
+ } while (elapsedTicks < measureDurationTicks);
+ double nsPerYield = (double)elapsedTicks * NsPerSecond / ((double)yieldCount * ticksPerSecond);
+ if (nsPerYield < 1)
+ {
+ nsPerYield = 1;
+ }
+
+ // Calculate the number of yields required to span the duration of a normalized yield
+ int yieldsPerNormalizedYield = (int)(MinNsPerNormalizedYield / nsPerYield + 0.5);
+ if (yieldsPerNormalizedYield < 1)
+ {
+ yieldsPerNormalizedYield = 1;
+ }
+ else if (yieldsPerNormalizedYield > MaxYieldsPerNormalizedYield)
+ {
+ yieldsPerNormalizedYield = MaxYieldsPerNormalizedYield;
+ }
+
+ // Calculate the maximum number of yields that would be optimal for a late spin iteration. Typically, we would not want to
+ // spend excessive amounts of time (thousands of cycles) doing only YieldProcessor, as SwitchToThread/Sleep would do a
+ // better job of allowing other work to run.
+ int optimalMaxNormalizedYieldsPerSpinIteration =
+ (int)(NsPerOptimialMaxSpinIterationDuration / (yieldsPerNormalizedYield * nsPerYield) + 0.5);
+ if (optimalMaxNormalizedYieldsPerSpinIteration < 1)
+ {
+ optimalMaxNormalizedYieldsPerSpinIteration = 1;
+ }
+
+ s_yieldsPerNormalizedYield = yieldsPerNormalizedYield;
+ s_optimalMaxNormalizedYieldsPerSpinIteration = optimalMaxNormalizedYieldsPerSpinIteration;
+}
diff --git a/src/vm/threads.h b/src/vm/threads.h
index 93e39156c8..4000f216f4 100644
--- a/src/vm/threads.h
+++ b/src/vm/threads.h
@@ -515,6 +515,8 @@ typedef Thread::ForbidSuspendThreadHolder ForbidSuspendThreadHolder;
// Each thread has a stack that tracks all enter and leave requests
struct Dbg_TrackSync
{
+ virtual ~Dbg_TrackSync() = default;
+
virtual void EnterSync (UINT_PTR caller, void *pAwareLock) = 0;
virtual void LeaveSync (UINT_PTR caller, void *pAwareLock) = 0;
};
@@ -1944,7 +1946,7 @@ public:
// Create all new threads here. The thread is created as suspended, so
// you must ::ResumeThread to kick it off. It is guaranteed to create the
// thread, or throw.
- BOOL CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, void *args);
+ BOOL CreateNewThread(SIZE_T stackSize, LPTHREAD_START_ROUTINE start, void *args, LPCWSTR pName=NULL);
enum StackSizeBucket
@@ -5249,11 +5251,9 @@ public:
// object associated with them (e.g., the bgc thread).
void SetGCSpecial(bool fGCSpecial);
-#ifndef FEATURE_PAL
private:
WORD m_wCPUGroup;
DWORD_PTR m_pAffinityMask;
-#endif // !FEATURE_PAL
public:
void ChooseThreadCPUGroupAffinity();
@@ -5362,6 +5362,71 @@ public:
m_HijackReturnKind = returnKind;
}
#endif // FEATURE_HIJACK
+
+private:
+ static CrstStatic s_initializeYieldProcessorNormalizedCrst;
+ static int s_yieldsPerNormalizedYield;
+ static int s_optimalMaxNormalizedYieldsPerSpinIteration;
+
+private:
+ static void InitializeYieldProcessorNormalized();
+
+public:
+ static bool IsYieldProcessorNormalizedInitialized()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return s_yieldsPerNormalizedYield != 0 && s_optimalMaxNormalizedYieldsPerSpinIteration != 0;
+ }
+
+public:
+ static void EnsureYieldProcessorNormalizedInitialized()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ if (!IsYieldProcessorNormalizedInitialized())
+ {
+ InitializeYieldProcessorNormalized();
+ }
+ }
+
+public:
+ static int GetOptimalMaxNormalizedYieldsPerSpinIteration()
+ {
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(IsYieldProcessorNormalizedInitialized());
+
+ return s_optimalMaxNormalizedYieldsPerSpinIteration;
+ }
+
+public:
+ static void YieldProcessorNormalized()
+ {
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(IsYieldProcessorNormalizedInitialized());
+
+ int n = s_yieldsPerNormalizedYield;
+ while (--n >= 0)
+ {
+ YieldProcessor();
+ }
+ }
+
+ static void YieldProcessorNormalizedWithBackOff(unsigned int spinIteration)
+ {
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(IsYieldProcessorNormalizedInitialized());
+
+ int n = s_optimalMaxNormalizedYieldsPerSpinIteration;
+ if (spinIteration <= 30 && (1 << spinIteration) < n)
+ {
+ n = 1 << spinIteration;
+ }
+ n *= s_yieldsPerNormalizedYield;
+ while (--n >= 0)
+ {
+ YieldProcessor();
+ }
+ }
};
// End of class Thread
diff --git a/src/vm/tieredcompilation.cpp b/src/vm/tieredcompilation.cpp
index acc26b90a5..ea69bbfec7 100644
--- a/src/vm/tieredcompilation.cpp
+++ b/src/vm/tieredcompilation.cpp
@@ -12,6 +12,7 @@
#include "excep.h"
#include "log.h"
#include "win32threadpool.h"
+#include "threadsuspend.h"
#include "tieredcompilation.h"
// TieredCompilationManager determines which methods should be recompiled and
@@ -47,14 +48,16 @@
// # Important entrypoints in this code:
//
//
-// a) .ctor and Init(...) - called once during AppDomain initialization
-// b) OnMethodCalled(...) - called when a method is being invoked. When a method
-// has been called enough times this is currently the only
-// trigger that initiates re-compilation.
-// c) OnAppDomainShutdown() - called during AppDomain::Exit() to begin the process
-// of stopping tiered compilation. After this point no more
-// background optimization work will be initiated but in-progress
-// work still needs to complete.
+// a) .ctor and Init(...) - called once during AppDomain initialization
+// b) OnMethodCalled(...) - called when a method is being invoked. When a method
+// has been called enough times this is currently the only
+// trigger that initiates re-compilation.
+// c) Shutdown() - called during AppDomain::Exit() to begin the process
+// of stopping tiered compilation. After this point no more
+// background optimization work will be initiated but in-progress
+// work still needs to complete.
+// d) ShutdownAllDomains() - Called from EEShutdownHelper to block until all async work is
+// complete. We must do this before we shutdown the JIT.
//
// # Overall workflow
//
@@ -107,6 +110,7 @@ void TieredCompilationManager::Init(ADID appDomainId)
SpinLockHolder holder(&m_lock);
m_domainId = appDomainId;
+ m_asyncWorkDoneEvent.CreateManualEventNoThrow(TRUE);
}
// Called each time code in this AppDomain has been run. This is our sole entrypoint to begin
@@ -127,6 +131,43 @@ BOOL TieredCompilationManager::OnMethodCalled(MethodDesc* pMethodDesc, DWORD cur
{
return TRUE; // stop notifications for this method
}
+ AsyncPromoteMethodToTier1(pMethodDesc);
+ return TRUE;
+}
+
+void TieredCompilationManager::AsyncPromoteMethodToTier1(MethodDesc* pMethodDesc)
+{
+ STANDARD_VM_CONTRACT;
+
+ NativeCodeVersion t1NativeCodeVersion;
+
+ // Add an inactive native code entry in the versioning table to track the tier1
+ // compilation we are going to create. This entry binds the compilation to a
+ // particular version of the IL code regardless of any changes that may
+ // occur between now and when jitting completes. If the IL does change in that
+ // interval the new code entry won't be activated.
+ {
+ CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
+ ILCodeVersion ilVersion = pCodeVersionManager->GetActiveILCodeVersion(pMethodDesc);
+ NativeCodeVersionCollection nativeVersions = ilVersion.GetNativeCodeVersions(pMethodDesc);
+ for (NativeCodeVersionIterator cur = nativeVersions.Begin(), end = nativeVersions.End(); cur != end; cur++)
+ {
+ if (cur->GetOptimizationTier() == NativeCodeVersion::OptimizationTier1)
+ {
+ // we've already promoted
+ return;
+ }
+ }
+
+ if (FAILED(ilVersion.AddNativeCodeVersion(pMethodDesc, &t1NativeCodeVersion)))
+ {
+ // optimization didn't work for some reason (presumably OOM)
+ // just give up and continue on
+ return;
+ }
+ t1NativeCodeVersion.SetOptimizationTier(NativeCodeVersion::OptimizationTier1);
+ }
// Insert the method into the optimization queue and trigger a thread to service
// the queue if needed.
@@ -141,7 +182,7 @@ BOOL TieredCompilationManager::OnMethodCalled(MethodDesc* pMethodDesc, DWORD cur
// unserviced. Synchronous retries appear unlikely to offer any material improvement
// and complicating the code to narrow an already rare error case isn't desirable.
{
- SListElem<MethodDesc*>* pMethodListItem = new (nothrow) SListElem<MethodDesc*>(pMethodDesc);
+ SListElem<NativeCodeVersion>* pMethodListItem = new (nothrow) SListElem<NativeCodeVersion>(t1NativeCodeVersion);
SpinLockHolder holder(&m_lock);
if (pMethodListItem != NULL)
{
@@ -152,11 +193,11 @@ BOOL TieredCompilationManager::OnMethodCalled(MethodDesc* pMethodDesc, DWORD cur
{
// Our current policy throttles at 1 thread, but in the future we
// could experiment with more parallelism.
- m_countOptimizationThreadsRunning++;
+ IncrementWorkerThreadCount();
}
else
{
- return TRUE; // stop notifications for this method
+ return;
}
}
@@ -165,7 +206,7 @@ BOOL TieredCompilationManager::OnMethodCalled(MethodDesc* pMethodDesc, DWORD cur
if (!ThreadpoolMgr::QueueUserWorkItem(StaticOptimizeMethodsCallback, this, QUEUE_ONLY, TRUE))
{
SpinLockHolder holder(&m_lock);
- m_countOptimizationThreadsRunning--;
+ DecrementWorkerThreadCount();
STRESS_LOG1(LF_TIEREDCOMPILATION, LL_WARNING, "TieredCompilationManager::OnMethodCalled: "
"ThreadpoolMgr::QueueUserWorkItem returned FALSE (no thread will run), method=%pM\n",
pMethodDesc);
@@ -174,20 +215,45 @@ BOOL TieredCompilationManager::OnMethodCalled(MethodDesc* pMethodDesc, DWORD cur
EX_CATCH
{
SpinLockHolder holder(&m_lock);
- m_countOptimizationThreadsRunning--;
+ DecrementWorkerThreadCount();
STRESS_LOG2(LF_TIEREDCOMPILATION, LL_WARNING, "TieredCompilationManager::OnMethodCalled: "
"Exception queuing work item to threadpool, hr=0x%x, method=%pM\n",
GET_EXCEPTION()->GetHR(), pMethodDesc);
}
EX_END_CATCH(RethrowTerminalExceptions);
- return TRUE; // stop notifications for this method
+ return;
}
-void TieredCompilationManager::OnAppDomainShutdown()
+// static
+// called from EEShutDownHelper
+void TieredCompilationManager::ShutdownAllDomains()
{
- SpinLockHolder holder(&m_lock);
- m_isAppDomainShuttingDown = TRUE;
+ STANDARD_VM_CONTRACT;
+
+ AppDomainIterator domain(TRUE);
+ while (domain.Next())
+ {
+ AppDomain * pDomain = domain.GetDomain();
+ if (pDomain != NULL)
+ {
+ pDomain->GetTieredCompilationManager()->Shutdown(TRUE);
+ }
+ }
+}
+
+void TieredCompilationManager::Shutdown(BOOL fBlockUntilAsyncWorkIsComplete)
+{
+ STANDARD_VM_CONTRACT;
+
+ {
+ SpinLockHolder holder(&m_lock);
+ m_isAppDomainShuttingDown = TRUE;
+ }
+ if (fBlockUntilAsyncWorkIsComplete)
+ {
+ m_asyncWorkDoneEvent.Wait(INFINITE, FALSE);
+ }
}
// This is the initial entrypoint for the background thread, called by
@@ -221,31 +287,33 @@ void TieredCompilationManager::OptimizeMethodsCallback()
SpinLockHolder holder(&m_lock);
if (m_isAppDomainShuttingDown)
{
- m_countOptimizationThreadsRunning--;
+ DecrementWorkerThreadCount();
return;
}
}
ULONGLONG startTickCount = CLRGetTickCount64();
- MethodDesc* pMethod = NULL;
+ NativeCodeVersion nativeCodeVersion;
EX_TRY
{
+ GCX_COOP();
ENTER_DOMAIN_ID(m_domainId);
{
+ GCX_PREEMP();
while (true)
{
{
SpinLockHolder holder(&m_lock);
- pMethod = GetNextMethodToOptimize();
- if (pMethod == NULL ||
+ nativeCodeVersion = GetNextMethodToOptimize();
+ if (nativeCodeVersion.IsNull() ||
m_isAppDomainShuttingDown)
{
- m_countOptimizationThreadsRunning--;
+ DecrementWorkerThreadCount();
break;
}
}
- OptimizeMethod(pMethod);
+ OptimizeMethod(nativeCodeVersion);
// If we have been running for too long return the thread to the threadpool and queue another event
// This gives the threadpool a chance to service other requests on this thread before returning to
@@ -256,7 +324,7 @@ void TieredCompilationManager::OptimizeMethodsCallback()
if (!ThreadpoolMgr::QueueUserWorkItem(StaticOptimizeMethodsCallback, this, QUEUE_ONLY, TRUE))
{
SpinLockHolder holder(&m_lock);
- m_countOptimizationThreadsRunning--;
+ DecrementWorkerThreadCount();
STRESS_LOG0(LF_TIEREDCOMPILATION, LL_WARNING, "TieredCompilationManager::OptimizeMethodsCallback: "
"ThreadpoolMgr::QueueUserWorkItem returned FALSE (no thread will run)\n");
}
@@ -270,51 +338,35 @@ void TieredCompilationManager::OptimizeMethodsCallback()
{
STRESS_LOG2(LF_TIEREDCOMPILATION, LL_ERROR, "TieredCompilationManager::OptimizeMethodsCallback: "
"Unhandled exception during method optimization, hr=0x%x, last method=%pM\n",
- GET_EXCEPTION()->GetHR(), pMethod);
+ GET_EXCEPTION()->GetHR(), nativeCodeVersion.GetMethodDesc());
}
EX_END_CATCH(RethrowTerminalExceptions);
}
// Jit compiles and installs new optimized code for a method.
// Called on a background thread.
-void TieredCompilationManager::OptimizeMethod(MethodDesc* pMethod)
+void TieredCompilationManager::OptimizeMethod(NativeCodeVersion nativeCodeVersion)
{
STANDARD_VM_CONTRACT;
- _ASSERTE(pMethod->IsEligibleForTieredCompilation());
- PCODE pJittedCode = CompileMethod(pMethod);
- if (pJittedCode != NULL)
+ _ASSERTE(nativeCodeVersion.GetMethodDesc()->IsEligibleForTieredCompilation());
+ if (CompileCodeVersion(nativeCodeVersion))
{
- InstallMethodCode(pMethod, pJittedCode);
+ ActivateCodeVersion(nativeCodeVersion);
}
}
// Compiles new optimized code for a method.
// Called on a background thread.
-PCODE TieredCompilationManager::CompileMethod(MethodDesc* pMethod)
+BOOL TieredCompilationManager::CompileCodeVersion(NativeCodeVersion nativeCodeVersion)
{
STANDARD_VM_CONTRACT;
PCODE pCode = NULL;
- ULONG sizeOfCode = 0;
+ MethodDesc* pMethod = nativeCodeVersion.GetMethodDesc();
EX_TRY
{
- CORJIT_FLAGS flags = CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_MCJIT_BACKGROUND);
- flags.Add(CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_TIER1));
-
- if (pMethod->IsDynamicMethod())
- {
- ILStubResolver* pResolver = pMethod->AsDynamicMethodDesc()->GetILStubResolver();
- flags.Add(pResolver->GetJitFlags());
- COR_ILMETHOD_DECODER* pILheader = pResolver->GetILHeader();
- pCode = UnsafeJitFunction(pMethod, pILheader, flags, &sizeOfCode);
- }
- else
- {
- COR_ILMETHOD_DECODER::DecoderStatus status;
- COR_ILMETHOD_DECODER header(pMethod->GetILHeader(), pMethod->GetModule()->GetMDImport(), &status);
- pCode = UnsafeJitFunction(pMethod, &header, flags, &sizeOfCode);
- }
+ pCode = pMethod->PrepareCode(nativeCodeVersion);
}
EX_CATCH
{
@@ -324,58 +376,117 @@ PCODE TieredCompilationManager::CompileMethod(MethodDesc* pMethod)
}
EX_END_CATCH(RethrowTerminalExceptions)
- return pCode;
+ return pCode != NULL;
}
// Updates the MethodDesc and precode so that future invocations of a method will
// execute the native code pointed to by pCode.
// Called on a background thread.
-void TieredCompilationManager::InstallMethodCode(MethodDesc* pMethod, PCODE pCode)
+void TieredCompilationManager::ActivateCodeVersion(NativeCodeVersion nativeCodeVersion)
{
STANDARD_VM_CONTRACT;
- _ASSERTE(!pMethod->IsNativeCodeStableAfterInit());
+ MethodDesc* pMethod = nativeCodeVersion.GetMethodDesc();
+ CodeVersionManager* pCodeVersionManager = pMethod->GetCodeVersionManager();
- PCODE pExistingCode = pMethod->GetNativeCode();
-#ifdef FEATURE_INTERPRETER
- if (!pMethod->SetNativeCodeInterlocked(pCode, pExistingCode, TRUE))
-#else
- if (!pMethod->SetNativeCodeInterlocked(pCode, pExistingCode))
-#endif
+ // If the ilParent version is active this will activate the native code version now.
+ // Otherwise if the ilParent version becomes active again in the future the native
+ // code version will activate then.
+ ILCodeVersion ilParent;
+ HRESULT hr = S_OK;
{
- //We aren't there yet, but when the feature is finished we shouldn't be racing against any other code mutator and there would be no
- //reason for this to fail
- STRESS_LOG2(LF_TIEREDCOMPILATION, LL_INFO10, "TieredCompilationManager::InstallMethodCode: Method %pM failed to update native code slot. Code=%pK\n",
- pMethod, pCode);
+ // As long as we are exclusively using precode publishing for tiered compilation
+ // methods this first attempt should succeed
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
+ ilParent = nativeCodeVersion.GetILCodeVersion();
+ hr = ilParent.SetActiveNativeCodeVersion(nativeCodeVersion, FALSE);
}
- else
+ if (hr == CORPROF_E_RUNTIME_SUSPEND_REQUIRED)
{
- Precode* pPrecode = pMethod->GetPrecode();
- if (!pPrecode->SetTargetInterlocked(pCode, FALSE))
+ // if we start using jump-stamp publishing for tiered compilation, the first attempt
+ // without the runtime suspended will fail and then this second attempt will
+ // succeed.
+ // Even though this works performance is likely to be quite bad. Realistically
+ // we are going to need batched updates to makes tiered-compilation + jump-stamp
+ // viable. This fallback path is just here as a proof-of-concept.
+ ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_REJIT);
{
- //We aren't there yet, but when the feature is finished we shouldn't be racing against any other code mutator and there would be no
- //reason for this to fail
- STRESS_LOG2(LF_TIEREDCOMPILATION, LL_INFO10, "TieredCompilationManager::InstallMethodCode: Method %pM failed to update precode. Code=%pK\n",
- pMethod, pCode);
+ CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
+ hr = ilParent.SetActiveNativeCodeVersion(nativeCodeVersion, TRUE);
}
+ ThreadSuspend::RestartEE(FALSE, TRUE);
+ }
+ if (FAILED(hr))
+ {
+ STRESS_LOG2(LF_TIEREDCOMPILATION, LL_INFO10, "TieredCompilationManager::ActivateCodeVersion: Method %pM failed to publish native code for native code version %d\n",
+ pMethod, nativeCodeVersion.GetVersionId());
}
}
// Dequeues the next method in the optmization queue.
// This should be called with m_lock already held and runs
// on the background thread.
-MethodDesc* TieredCompilationManager::GetNextMethodToOptimize()
+NativeCodeVersion TieredCompilationManager::GetNextMethodToOptimize()
{
STANDARD_VM_CONTRACT;
- SListElem<MethodDesc*>* pElem = m_methodsToOptimize.RemoveHead();
+ SListElem<NativeCodeVersion>* pElem = m_methodsToOptimize.RemoveHead();
if (pElem != NULL)
{
- MethodDesc* pMD = pElem->GetValue();
+ NativeCodeVersion nativeCodeVersion = pElem->GetValue();
delete pElem;
- return pMD;
+ return nativeCodeVersion;
+ }
+ return NativeCodeVersion();
+}
+
+void TieredCompilationManager::IncrementWorkerThreadCount()
+{
+ STANDARD_VM_CONTRACT;
+ //m_lock should be held
+
+ m_countOptimizationThreadsRunning++;
+ m_asyncWorkDoneEvent.Reset();
+}
+
+void TieredCompilationManager::DecrementWorkerThreadCount()
+{
+ STANDARD_VM_CONTRACT;
+ //m_lock should be held
+
+ m_countOptimizationThreadsRunning--;
+ if (m_countOptimizationThreadsRunning == 0)
+ {
+ m_asyncWorkDoneEvent.Set();
+ }
+}
+
+//static
+CORJIT_FLAGS TieredCompilationManager::GetJitFlags(NativeCodeVersion nativeCodeVersion)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ CORJIT_FLAGS flags;
+ if (!nativeCodeVersion.GetMethodDesc()->IsEligibleForTieredCompilation())
+ {
+#ifdef FEATURE_INTERPRETER
+ flags.Set(CORJIT_FLAGS::CORJIT_FLAG_MAKEFINALCODE);
+#endif
+ return flags;
+ }
+
+ if (nativeCodeVersion.GetOptimizationTier() == NativeCodeVersion::OptimizationTier0)
+ {
+ flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER0);
+ }
+ else
+ {
+ flags.Set(CORJIT_FLAGS::CORJIT_FLAG_TIER1);
+#ifdef FEATURE_INTERPRETER
+ flags.Set(CORJIT_FLAGS::CORJIT_FLAG_MAKEFINALCODE);
+#endif
}
- return NULL;
+ return flags;
}
#endif // FEATURE_TIERED_COMPILATION
diff --git a/src/vm/tieredcompilation.h b/src/vm/tieredcompilation.h
index 71236c5374..9f6187244a 100644
--- a/src/vm/tieredcompilation.h
+++ b/src/vm/tieredcompilation.h
@@ -26,24 +26,31 @@ public:
void Init(ADID appDomainId);
BOOL OnMethodCalled(MethodDesc* pMethodDesc, DWORD currentCallCount);
- void OnAppDomainShutdown();
+ void AsyncPromoteMethodToTier1(MethodDesc* pMethodDesc);
+ static void ShutdownAllDomains();
+ void Shutdown(BOOL fBlockUntilAsyncWorkIsComplete);
+ static CORJIT_FLAGS GetJitFlags(NativeCodeVersion nativeCodeVersion);
private:
static DWORD StaticOptimizeMethodsCallback(void* args);
void OptimizeMethodsCallback();
- void OptimizeMethod(MethodDesc* pMethod);
- MethodDesc* GetNextMethodToOptimize();
- PCODE CompileMethod(MethodDesc* pMethod);
- void InstallMethodCode(MethodDesc* pMethod, PCODE pCode);
+ void OptimizeMethod(NativeCodeVersion nativeCodeVersion);
+ NativeCodeVersion GetNextMethodToOptimize();
+ BOOL CompileCodeVersion(NativeCodeVersion nativeCodeVersion);
+ void ActivateCodeVersion(NativeCodeVersion nativeCodeVersion);
+
+ void IncrementWorkerThreadCount();
+ void DecrementWorkerThreadCount();
SpinLock m_lock;
- SList<SListElem<MethodDesc*>> m_methodsToOptimize;
+ SList<SListElem<NativeCodeVersion>> m_methodsToOptimize;
ADID m_domainId;
BOOL m_isAppDomainShuttingDown;
DWORD m_countOptimizationThreadsRunning;
DWORD m_callCountOptimizationThreshhold;
DWORD m_optimizationQuantumMs;
+ CLREvent m_asyncWorkDoneEvent;
};
#endif // FEATURE_TIERED_COMPILATION
diff --git a/src/vm/typehandle.cpp b/src/vm/typehandle.cpp
index 32384cc490..9e8afba82f 100644
--- a/src/vm/typehandle.cpp
+++ b/src/vm/typehandle.cpp
@@ -494,7 +494,6 @@ DWORD TypeHandle::IsTransparentProxy() const
return FALSE;
}
-#ifdef FEATURE_HFA
bool TypeHandle::IsHFA() const
{
WRAPPER_NO_CONTRACT;
@@ -520,7 +519,7 @@ CorElementType TypeHandle::GetHFAType() const
return ELEMENT_TYPE_END;
}
-#endif // FEATURE_HFA
+
#ifdef FEATURE_64BIT_ALIGNMENT
bool TypeHandle::RequiresAlign8() const
@@ -1546,6 +1545,14 @@ BOOL TypeHandle::IsByRef() const
}
+BOOL TypeHandle::IsByRefLike() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return(!IsTypeDesc() && AsMethodTable()->IsByRefLike());
+
+}
+
BOOL TypeHandle::IsPointer() const
{
LIMITED_METHOD_CONTRACT;
diff --git a/src/vm/typehandle.h b/src/vm/typehandle.h
index 72a5656cc3..cec772e3be 100644
--- a/src/vm/typehandle.h
+++ b/src/vm/typehandle.h
@@ -394,10 +394,8 @@ public:
CHECK CheckFullyLoaded();
#endif
-#ifdef FEATURE_HFA
bool IsHFA() const;
CorElementType GetHFAType() const;
-#endif // FEATURE_HFA
#ifdef FEATURE_64BIT_ALIGNMENT
bool RequiresAlign8() const;
@@ -513,6 +511,9 @@ public:
// BYREF
BOOL IsByRef() const;
+ // BYREFLIKE (does not return TRUE for IsByRef types)
+ BOOL IsByRefLike() const;
+
// PTR
BOOL IsPointer() const;
diff --git a/src/vm/util.cpp b/src/vm/util.cpp
index 260e0daa38..2c71289c8d 100644
--- a/src/vm/util.cpp
+++ b/src/vm/util.cpp
@@ -1861,8 +1861,6 @@ size_t GetLargestOnDieCacheSize(BOOL bTrueSize)
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_NOTRIGGER;
-#if defined(_TARGET_AMD64_) || defined (_TARGET_X86_)
-
static size_t maxSize;
static size_t maxTrueSize;
@@ -1879,6 +1877,7 @@ size_t GetLargestOnDieCacheSize(BOOL bTrueSize)
}
}
+#if defined(_TARGET_AMD64_) || defined (_TARGET_X86_)
DefaultCatchFilterParam param;
param.pv = COMPLUS_EXCEPTION_EXECUTE_HANDLER;
@@ -2001,18 +2000,20 @@ size_t GetLargestOnDieCacheSize(BOOL bTrueSize)
{
}
PAL_ENDTRY
+#else
+ maxSize = maxTrueSize = GetLogicalProcessorCacheSizeFromOS() ; // Returns the size of the highest level processor cache
+#endif
+
+#if defined(_TARGET_ARM64_)
+ // Bigger gen0 size helps arm64 targets
+ maxSize = maxTrueSize * 3;
+#endif
// printf("GetLargestOnDieCacheSize returns %d, adjusted size %d\n", maxSize, maxTrueSize);
if (bTrueSize)
return maxTrueSize;
else
return maxSize;
-
-#else
- size_t cache_size = GetLogicalProcessorCacheSizeFromOS() ; // Returns the size of the highest level processor cache
- return cache_size;
-
-#endif
}
//---------------------------------------------------------------------
@@ -2845,7 +2846,6 @@ void InitializeClrNotifications()
#if defined(FEATURE_GDBJIT)
#include "gdbjit.h"
-__declspec(thread) bool tls_isSymReaderInProgress = false;
#endif // FEATURE_GDBJIT
// called from the runtime
@@ -2859,19 +2859,12 @@ void DACNotify::DoJITNotification(MethodDesc *MethodDescPtr)
MODE_PREEMPTIVE;
}
CONTRACTL_END;
-#if defined(FEATURE_GDBJIT) && defined(FEATURE_PAL) && !defined(CROSSGEN_COMPILE)
- if(!tls_isSymReaderInProgress)
- {
- tls_isSymReaderInProgress = true;
- NotifyGdb::MethodCompiled(MethodDescPtr);
- tls_isSymReaderInProgress = false;
- }
-#endif
+
TADDR Args[2] = { JIT_NOTIFICATION, (TADDR) MethodDescPtr };
DACNotifyExceptionHelper(Args, 2);
}
-void DACNotify::DoJITDiscardNotification(MethodDesc *MethodDescPtr)
+void DACNotify::DoJITPitchingNotification(MethodDesc *MethodDescPtr)
{
CONTRACTL
{
@@ -2883,9 +2876,9 @@ void DACNotify::DoJITDiscardNotification(MethodDesc *MethodDescPtr)
CONTRACTL_END;
#if defined(FEATURE_GDBJIT) && defined(FEATURE_PAL) && !defined(CROSSGEN_COMPILE)
- NotifyGdb::MethodDropped(MethodDescPtr);
+ NotifyGdb::MethodPitched(MethodDescPtr);
#endif
- TADDR Args[2] = { JIT_DISCARD_NOTIFICATION, (TADDR) MethodDescPtr };
+ TADDR Args[2] = { JIT_PITCHING_NOTIFICATION, (TADDR) MethodDescPtr };
DACNotifyExceptionHelper(Args, 2);
}
@@ -3007,10 +3000,10 @@ BOOL DACNotify::ParseJITNotification(TADDR Args[], TADDR& MethodDescPtr)
return TRUE;
}
-BOOL DACNotify::ParseJITDiscardNotification(TADDR Args[], TADDR& MethodDescPtr)
+BOOL DACNotify::ParseJITPitchingNotification(TADDR Args[], TADDR& MethodDescPtr)
{
- _ASSERTE(Args[0] == JIT_DISCARD_NOTIFICATION);
- if (Args[0] != JIT_DISCARD_NOTIFICATION)
+ _ASSERTE(Args[0] == JIT_PITCHING_NOTIFICATION);
+ if (Args[0] != JIT_PITCHING_NOTIFICATION)
{
return FALSE;
}
diff --git a/src/vm/util.hpp b/src/vm/util.hpp
index 1f86d6c2d5..edfd9161e4 100644
--- a/src/vm/util.hpp
+++ b/src/vm/util.hpp
@@ -44,6 +44,13 @@
#define UtilMessageBoxNonLocalizedVA __error("Use one of the EEMessageBox APIs (defined in eemessagebox.h) from inside the EE")
#define WszMessageBox __error("Use one of the EEMessageBox APIs (defined in eemessagebox.h) from inside the EE")
+// Hot cache lines need to be aligned to cache line size to improve performance
+#if defined(ARM64)
+#define MAX_CACHE_LINE_SIZE 128
+#else
+#define MAX_CACHE_LINE_SIZE 64
+#endif
+
//========================================================================
// More convenient names for integer types of a guaranteed size.
//========================================================================
@@ -1064,7 +1071,7 @@ public:
MODULE_LOAD_NOTIFICATION=1,
MODULE_UNLOAD_NOTIFICATION=2,
JIT_NOTIFICATION=3,
- JIT_DISCARD_NOTIFICATION=4,
+ JIT_PITCHING_NOTIFICATION=4,
EXCEPTION_NOTIFICATION=5,
GC_NOTIFICATION= 6,
CATCH_ENTER_NOTIFICATION = 7,
@@ -1072,7 +1079,7 @@ public:
// called from the runtime
static void DoJITNotification(MethodDesc *MethodDescPtr);
- static void DoJITDiscardNotification(MethodDesc *MethodDescPtr);
+ static void DoJITPitchingNotification(MethodDesc *MethodDescPtr);
static void DoModuleLoadNotification(Module *Module);
static void DoModuleUnloadNotification(Module *Module);
static void DoExceptionNotification(class Thread* ThreadPtr);
@@ -1082,7 +1089,7 @@ public:
// called from the DAC
static int GetType(TADDR Args[]);
static BOOL ParseJITNotification(TADDR Args[], TADDR& MethodDescPtr);
- static BOOL ParseJITDiscardNotification(TADDR Args[], TADDR& MethodDescPtr);
+ static BOOL ParseJITPitchingNotification(TADDR Args[], TADDR& MethodDescPtr);
static BOOL ParseModuleLoadNotification(TADDR Args[], TADDR& ModulePtr);
static BOOL ParseModuleUnloadNotification(TADDR Args[], TADDR& ModulePtr);
static BOOL ParseExceptionNotification(TADDR Args[], TADDR& ThreadPtr);
diff --git a/src/vm/vars.cpp b/src/vm/vars.cpp
index 3a8046b26b..ff941d2101 100644
--- a/src/vm/vars.cpp
+++ b/src/vm/vars.cpp
@@ -99,7 +99,6 @@ GPTR_IMPL(MethodTable, g_pICastableInterface);
GPTR_IMPL(MethodDesc, g_pExecuteBackoutCodeHelperMethod);
-GPTR_IMPL(MethodDesc, g_pObjectCtorMD);
GPTR_IMPL(MethodDesc, g_pObjectFinalizerMD);
GPTR_IMPL(Thread,g_pFinalizerThread);
diff --git a/src/vm/vars.hpp b/src/vm/vars.hpp
index cc167f2809..c9f4848692 100644
--- a/src/vm/vars.hpp
+++ b/src/vm/vars.hpp
@@ -404,7 +404,6 @@ GPTR_DECL(MethodTable, g_pICastableInterface);
GPTR_DECL(MethodDesc, g_pExecuteBackoutCodeHelperMethod);
-GPTR_DECL(MethodDesc, g_pObjectCtorMD);
GPTR_DECL(MethodDesc, g_pObjectFinalizerMD);
//<TODO> @TODO Remove eventually - determines whether the verifier throws an exception when something fails</TODO>
diff --git a/src/vm/virtualcallstub.cpp b/src/vm/virtualcallstub.cpp
index c230f254c6..2e94a16666 100644
--- a/src/vm/virtualcallstub.cpp
+++ b/src/vm/virtualcallstub.cpp
@@ -20,6 +20,10 @@
#include "compile.h"
#endif
+#ifdef FEATURE_PERFMAP
+#include "perfmap.h"
+#endif
+
#ifndef DACCESS_COMPILE
//@TODO: make these conditional on whether logs are being produced
@@ -2694,6 +2698,10 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStub(PCODE ad
LOG((LF_STUBS, LL_INFO10000, "GenerateDispatchStub for token" FMT_ADDR "and pMT" FMT_ADDR "at" FMT_ADDR "\n",
DBG_ADDR(dispatchToken), DBG_ADDR(pMTExpected), DBG_ADDR(holder->stub())));
+#ifdef FEATURE_PERFMAP
+ PerfMap::LogStubs(__FUNCTION__, "GenerateDispatchStub", (PCODE)holder->stub(), holder->stub()->size());
+#endif
+
RETURN (holder);
}
@@ -2736,6 +2744,10 @@ DispatchHolder *VirtualCallStubManager::GenerateDispatchStubLong(PCODE
LOG((LF_STUBS, LL_INFO10000, "GenerateDispatchStub for token" FMT_ADDR "and pMT" FMT_ADDR "at" FMT_ADDR "\n",
DBG_ADDR(dispatchToken), DBG_ADDR(pMTExpected), DBG_ADDR(holder->stub())));
+#ifdef FEATURE_PERFMAP
+ PerfMap::LogStubs(__FUNCTION__, "GenerateDispatchStub", (PCODE)holder->stub(), holder->stub()->size());
+#endif
+
RETURN (holder);
}
#endif
@@ -2822,6 +2834,10 @@ ResolveHolder *VirtualCallStubManager::GenerateResolveStub(PCODE addr
LOG((LF_STUBS, LL_INFO10000, "GenerateResolveStub for token" FMT_ADDR "at" FMT_ADDR "\n",
DBG_ADDR(dispatchToken), DBG_ADDR(holder->stub())));
+#ifdef FEATURE_PERFMAP
+ PerfMap::LogStubs(__FUNCTION__, "GenerateResolveStub", (PCODE)holder->stub(), holder->stub()->size());
+#endif
+
RETURN (holder);
}
@@ -2852,6 +2868,10 @@ LookupHolder *VirtualCallStubManager::GenerateLookupStub(PCODE addrOfResolver, s
LOG((LF_STUBS, LL_INFO10000, "GenerateLookupStub for token" FMT_ADDR "at" FMT_ADDR "\n",
DBG_ADDR(dispatchToken), DBG_ADDR(holder->stub())));
+#ifdef FEATURE_PERFMAP
+ PerfMap::LogStubs(__FUNCTION__, "GenerateLookupStub", (PCODE)holder->stub(), holder->stub()->size());
+#endif
+
RETURN (holder);
}
diff --git a/src/vm/win32threadpool.cpp b/src/vm/win32threadpool.cpp
index 18df0dc76e..eabbcb93ae 100644
--- a/src/vm/win32threadpool.cpp
+++ b/src/vm/win32threadpool.cpp
@@ -86,7 +86,7 @@ SVAL_IMPL(LONG,ThreadpoolMgr,MaxFreeCPThreads); // = MaxFreeCP
Volatile<LONG> ThreadpoolMgr::NumCPInfrastructureThreads = 0; // number of threads currently busy handling draining cycle
// Cacheline aligned, hot variable
-DECLSPEC_ALIGN(64) SVAL_IMPL(ThreadpoolMgr::ThreadCounter, ThreadpoolMgr, WorkerCounter);
+DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) SVAL_IMPL(ThreadpoolMgr::ThreadCounter, ThreadpoolMgr, WorkerCounter);
SVAL_IMPL(LONG,ThreadpoolMgr,MinLimitTotalWorkerThreads); // = MaxLimitCPThreadsPerCPU * number of CPUS
SVAL_IMPL(LONG,ThreadpoolMgr,MaxLimitTotalWorkerThreads); // = MaxLimitCPThreadsPerCPU * number of CPUS
@@ -97,7 +97,7 @@ LONG ThreadpoolMgr::cpuUtilizationAverage = 0;
HillClimbing ThreadpoolMgr::HillClimbingInstance;
// Cacheline aligned, 3 hot variables updated in a group
-DECLSPEC_ALIGN(64) LONG ThreadpoolMgr::PriorCompletedWorkRequests = 0;
+DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) LONG ThreadpoolMgr::PriorCompletedWorkRequests = 0;
DWORD ThreadpoolMgr::PriorCompletedWorkRequestsTime;
DWORD ThreadpoolMgr::NextCompletedWorkRequestsTime;
@@ -116,10 +116,10 @@ int ThreadpoolMgr::ThreadAdjustmentInterval;
LONG ThreadpoolMgr::Initialization=0; // indicator of whether the threadpool is initialized.
// Cacheline aligned, hot variable
-DECLSPEC_ALIGN(64) unsigned int ThreadpoolMgr::LastDequeueTime; // used to determine if work items are getting thread starved
+DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) unsigned int ThreadpoolMgr::LastDequeueTime; // used to determine if work items are getting thread starved
// Move out of from preceeding variables' cache line
-DECLSPEC_ALIGN(64) int ThreadpoolMgr::offset_counter = 0;
+DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) int ThreadpoolMgr::offset_counter = 0;
SPTR_IMPL(WorkRequest,ThreadpoolMgr,WorkRequestHead); // Head of work request queue
SPTR_IMPL(WorkRequest,ThreadpoolMgr,WorkRequestTail); // Head of work request queue
@@ -144,17 +144,17 @@ HANDLE ThreadpoolMgr::TimerThread=NULL;
Thread *ThreadpoolMgr::pTimerThread=NULL;
// Cacheline aligned, hot variable
-DECLSPEC_ALIGN(64) DWORD ThreadpoolMgr::LastTickCount;
+DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) DWORD ThreadpoolMgr::LastTickCount;
#ifdef _DEBUG
DWORD ThreadpoolMgr::TickCountAdjustment=0;
#endif
// Cacheline aligned, hot variable
-DECLSPEC_ALIGN(64) LONG ThreadpoolMgr::GateThreadStatus=GATE_THREAD_STATUS_NOT_RUNNING;
+DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) LONG ThreadpoolMgr::GateThreadStatus=GATE_THREAD_STATUS_NOT_RUNNING;
// Move out of from preceeding variables' cache line
-DECLSPEC_ALIGN(64) ThreadpoolMgr::RecycledListsWrapper ThreadpoolMgr::RecycledLists;
+DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) ThreadpoolMgr::RecycledListsWrapper ThreadpoolMgr::RecycledLists;
ThreadpoolMgr::TimerInfo *ThreadpoolMgr::TimerInfosToBeRecycled = NULL;
@@ -1815,8 +1815,8 @@ Thread* ThreadpoolMgr::CreateUnimpersonatedThread(LPTHREAD_START_ROUTINE lpStart
// CreateNewThread takes care of reverting any impersonation - so dont do anything here.
bOK = pThread->CreateNewThread(0, // default stack size
lpStartAddress,
- lpArgs //arguments
- );
+ lpArgs, //arguments
+ W(".NET Core ThreadPool"));
}
else {
#ifndef FEATURE_PAL
diff --git a/src/vm/win32threadpool.h b/src/vm/win32threadpool.h
index 65be889a50..fc5742b494 100644
--- a/src/vm/win32threadpool.h
+++ b/src/vm/win32threadpool.h
@@ -123,9 +123,8 @@ class ThreadpoolMgr
class UnfairSemaphore
{
private:
-
// padding to ensure we get our own cache line
- BYTE padding1[64];
+ BYTE padding1[MAX_CACHE_LINE_SIZE];
//
// We track everything we care about in a single 64-bit struct to allow us to
@@ -146,12 +145,12 @@ class ThreadpoolMgr
} m_counts;
private:
+ // padding to ensure we get our own cache line
+ BYTE padding2[MAX_CACHE_LINE_SIZE];
+
const int m_spinLimitPerProcessor; //used when calculating max spin duration
CLRSemaphore m_sem; //waiters wait on this
- // padding to ensure we get our own cache line
- BYTE padding2[64];
-
INDEBUG(int m_maxCount;)
bool UpdateCounts(Counts newCounts, Counts currentCounts)
@@ -350,6 +349,9 @@ public:
{
static const int MaxPossibleCount = 0x7fff;
+ // padding to ensure we get our own cache line
+ BYTE padding1[MAX_CACHE_LINE_SIZE];
+
union Counts
{
struct
@@ -370,11 +372,10 @@ public:
LONGLONG AsLongLong;
bool operator==(Counts other) {LIMITED_METHOD_CONTRACT; return AsLongLong == other.AsLongLong;}
-
} counts;
// padding to ensure we get our own cache line
- BYTE padding[64];
+ BYTE padding2[MAX_CACHE_LINE_SIZE];
Counts GetCleanCounts()
{
@@ -962,11 +963,11 @@ public:
//
class RecycledListsWrapper
{
- DWORD CacheGuardPre[64/sizeof(DWORD)];
+ DWORD CacheGuardPre[MAX_CACHE_LINE_SIZE/sizeof(DWORD)];
RecycledListInfo (*pRecycledListPerProcessor)[MEMTYPE_COUNT]; // RecycledListInfo [numProc][MEMTYPE_COUNT]
- DWORD CacheGuardPost[64/sizeof(DWORD)];
+ DWORD CacheGuardPost[MAX_CACHE_LINE_SIZE/sizeof(DWORD)];
public:
void Initialize( unsigned int numProcs );
@@ -1247,11 +1248,11 @@ private:
SVAL_DECL(LONG,MinLimitTotalWorkerThreads); // same as MinLimitTotalCPThreads
SVAL_DECL(LONG,MaxLimitTotalWorkerThreads); // same as MaxLimitTotalCPThreads
- DECLSPEC_ALIGN(64) static unsigned int LastDequeueTime; // used to determine if work items are getting thread starved
+ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static unsigned int LastDequeueTime; // used to determine if work items are getting thread starved
static HillClimbing HillClimbingInstance;
- DECLSPEC_ALIGN(64) static LONG PriorCompletedWorkRequests;
+ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static LONG PriorCompletedWorkRequests;
static DWORD PriorCompletedWorkRequestsTime;
static DWORD NextCompletedWorkRequestsTime;
@@ -1277,7 +1278,7 @@ private:
static const DWORD WorkerTimeout = 20 * 1000;
static const DWORD WorkerTimeoutAppX = 5 * 1000; // shorter timeout to allow threads to exit prior to app suspension
- DECLSPEC_ALIGN(64) SVAL_DECL(ThreadCounter,WorkerCounter);
+ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) SVAL_DECL(ThreadCounter,WorkerCounter);
//
// WorkerSemaphore is an UnfairSemaphore because:
@@ -1306,7 +1307,7 @@ private:
SVAL_DECL(LIST_ENTRY,TimerQueue); // queue of timers
static HANDLE TimerThread; // Currently we only have one timer thread
static Thread* pTimerThread;
- DECLSPEC_ALIGN(64) static DWORD LastTickCount; // the count just before timer thread goes to sleep
+ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static DWORD LastTickCount; // the count just before timer thread goes to sleep
static BOOL InitCompletionPortThreadpool; // flag indicating whether completion port threadpool has been initialized
static HANDLE GlobalCompletionPort; // used for binding io completions on file handles
@@ -1319,20 +1320,20 @@ private:
SVAL_DECL(LONG,MinLimitTotalCPThreads);
SVAL_DECL(LONG,MaxFreeCPThreads); // = MaxFreeCPThreadsPerCPU * Number of CPUS
- DECLSPEC_ALIGN(64) static LONG GateThreadStatus; // See GateThreadStatus enumeration
+ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static LONG GateThreadStatus; // See GateThreadStatus enumeration
static Volatile<LONG> NumCPInfrastructureThreads; // number of threads currently busy handling draining cycle
SVAL_DECL(LONG,cpuUtilization);
static LONG cpuUtilizationAverage;
- DECLSPEC_ALIGN(64) static RecycledListsWrapper RecycledLists;
+ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static RecycledListsWrapper RecycledLists;
#ifdef _DEBUG
static DWORD TickCountAdjustment; // add this value to value returned by GetTickCount
#endif
- DECLSPEC_ALIGN(64) static int offset_counter;
+ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static int offset_counter;
static const int offset_multiplier = 128;
};
diff --git a/src/zap/zapimage.cpp b/src/zap/zapimage.cpp
index 2b38eaf107..96fa81fba1 100644
--- a/src/zap/zapimage.cpp
+++ b/src/zap/zapimage.cpp
@@ -572,6 +572,7 @@ void ZapImage::AllocateVirtualSections()
#endif // defined(WIN64EXCEPTIONS)
m_pPreloadSections[CORCOMPILE_SECTION_READONLY_WARM] = NewVirtualSection(pTextSection, IBCProfiledSection | WarmRange | ReadonlySection, sizeof(TADDR));
+ m_pPreloadSections[CORCOMPILE_SECTION_READONLY_VCHUNKS_AND_DICTIONARY] = NewVirtualSection(pTextSection, IBCProfiledSection | WarmRange | ReadonlySection, sizeof(TADDR));
//
// GC Info for methods which were not touched in profiling
@@ -1106,6 +1107,10 @@ HANDLE ZapImage::SaveImage(LPCWSTR wszOutputFileName, CORCOMPILE_NGEN_SIGNATURE
HANDLE hFile = GenerateFile(wszOutputFileName, pNativeImageSig);
+ if (m_zapper->m_pOpt->m_verbose)
+ {
+ PrintStats(wszOutputFileName);
+ }
return hFile;
}
diff --git a/src/zap/zapinfo.cpp b/src/zap/zapinfo.cpp
index f1399b3d4d..b8ef3eab91 100644
--- a/src/zap/zapinfo.cpp
+++ b/src/zap/zapinfo.cpp
@@ -448,25 +448,6 @@ void ZapInfo::CompileMethod()
}
#endif
- if (!m_jitFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_SKIP_VERIFICATION))
- {
- BOOL raiseVerificationException, unverifiableGenericCode;
-
- m_jitFlags = GetCompileFlagsIfGenericInstantiation(
- m_currentMethodHandle,
- m_jitFlags,
- this,
- &raiseVerificationException,
- &unverifiableGenericCode);
-
- // Instead of raising a VerificationException, we will leave the method
- // uncompiled. If it gets called at runtime, we will raise the
- // VerificationException at that time while trying to compile the method.
- if (raiseVerificationException)
- return;
- }
-
-
if (m_pImage->m_stats)
{
m_pImage->m_stats->m_methods++;
@@ -3564,6 +3545,11 @@ const char* ZapInfo::getMethodName(CORINFO_METHOD_HANDLE ftn, const char **modul
return m_pEEJitInfo->getMethodName(ftn, moduleName);
}
+const char* ZapInfo::getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, const char **className, const char** namespaceName)
+{
+ return m_pEEJitInfo->getMethodNameFromMetadata(ftn, className, namespaceName);
+}
+
unsigned ZapInfo::getMethodHash(CORINFO_METHOD_HANDLE ftn)
{
return m_pEEJitInfo->getMethodHash(ftn);
@@ -3702,10 +3688,11 @@ CORINFO_MODULE_HANDLE ZapInfo::getMethodModule(CORINFO_METHOD_HANDLE method)
}
void ZapInfo::getMethodVTableOffset(CORINFO_METHOD_HANDLE method,
- unsigned * pOffsetOfIndirection,
- unsigned * pOffsetAfterIndirection)
+ unsigned * pOffsetOfIndirection,
+ unsigned * pOffsetAfterIndirection,
+ bool * isRelative)
{
- m_pEEJitInfo->getMethodVTableOffset(method, pOffsetOfIndirection, pOffsetAfterIndirection);
+ m_pEEJitInfo->getMethodVTableOffset(method, pOffsetOfIndirection, pOffsetAfterIndirection, isRelative);
}
CORINFO_METHOD_HANDLE ZapInfo::resolveVirtualMethod(
diff --git a/src/zap/zapinfo.h b/src/zap/zapinfo.h
index 44ad5de52d..d533975c79 100644
--- a/src/zap/zapinfo.h
+++ b/src/zap/zapinfo.h
@@ -616,6 +616,10 @@ public:
const char* getMethodName(CORINFO_METHOD_HANDLE ftn,
const char **moduleName);
+ const char* getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn,
+ const char **className,
+ const char **namespaceName);
+
unsigned getMethodHash(CORINFO_METHOD_HANDLE ftn);
DWORD getMethodAttribs(CORINFO_METHOD_HANDLE ftn);
void setMethodAttribs(CORINFO_METHOD_HANDLE ftn, CorInfoMethodRuntimeFlags attribs);
@@ -662,7 +666,8 @@ public:
void getMethodVTableOffset(CORINFO_METHOD_HANDLE method,
unsigned * pOffsetOfIndirection,
- unsigned * pOffsetAfterIndirection);
+ unsigned * pOffsetAfterIndirection,
+ bool * isRelative);
CORINFO_METHOD_HANDLE resolveVirtualMethod(
CORINFO_METHOD_HANDLE virtualMethod,
diff --git a/src/zap/zapperstats.cpp b/src/zap/zapperstats.cpp
index 8a7be16905..40c03dd15f 100644
--- a/src/zap/zapperstats.cpp
+++ b/src/zap/zapperstats.cpp
@@ -73,6 +73,7 @@ static_assert_no_msg((sizeof(callReasons)/sizeof(callReasons[0])) == CORINFO_IND
ZapperStats::ZapperStats()
: m_methods( 0 )
, m_failedMethods( 0 )
+ , m_failedILStubs( 0 )
, m_ilCodeSize( 0 )
, m_nativeCodeSize( 0 )
, m_nativeColdCodeSize( 0 )
@@ -90,6 +91,9 @@ ZapperStats::ZapperStats()
, m_nativeColdCodeSizeInSplitProfiledMethods( 0 )
, m_nativeCodeSizeInProfiledMethods( 0 )
, m_nativeColdCodeSizeInProfiledMethods( 0 )
+ , m_totalHotCodeSize( 0 )
+ , m_totalUnprofiledCodeSize( 0 )
+ , m_totalColdCodeSize( 0 )
, m_totalCodeSizeInProfiledMethods( 0 )
, m_totalColdCodeSizeInProfiledMethods( 0 )
, m_inputFileSize( 0 )