Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a745f28
[mono][interp] Remove invalid addition of conversion opcodes (#116130)
BrzVlad Jun 2, 2025
f313e8d
[mono][interp] Disable inlining of methods that call methods using St…
BrzVlad Jun 2, 2025
a6094c1
Automated bump of chrome version (#116186)
github-actions[bot] Jun 2, 2025
32a6d12
Switch to using non-arch specific runtime properties (#116155)
mmitche Jun 2, 2025
ac7097c
Update dependencies from https://dev.azure.com/dnceng/internal/_git/d…
dotnet-maestro[bot] Jun 2, 2025
b5abdd1
JIT: Handle return buffer definitions in TreeLifeUpdater (#116137)
jakobbotsch Jun 2, 2025
4bdcb8d
Fix invalid continuation caused by exception after inlining in `TryRu…
kotlarmilos Jun 2, 2025
8a62488
remove redundant unsafe code (#116187)
EgorBo Jun 2, 2025
2d5a2ee
Remove canceled AsyncOperations from channel queues (#116021)
stephentoub Jun 2, 2025
8d59055
Interpreter EH implementation (#116046)
janvorli Jun 2, 2025
39e8c08
Disable CoreCLR interpreter test for JIT stress (#116219)
janvorli Jun 2, 2025
cb57cb8
Add missing connection strings to EnvironmentVariablesConfigurationPr…
tarekgh Jun 2, 2025
5ffe44f
Implement SVE2 Xor, XorRotateRight (#115891)
snickolls-arm Jun 2, 2025
664ac25
Fix parsing of json with UTF 8 BOM in the host (#115967)
vitek-karas Jun 2, 2025
5fab56b
Reduce allocations in PollingWildCardChangeToken (#116175)
pentp Jun 3, 2025
a2c9e7b
Unify the JsonDocument and Utf8JsonReader handling of DateTime/Offset…
IDisposable Jun 3, 2025
5edf06e
Use span copy in BigInteger.TryGetBytes when applicable (#115445)
Rob-Hague Jun 3, 2025
fcd197c
Support bounded channel with bound of 0 (rendezvous) (#116097)
stephentoub Jun 3, 2025
43edad5
JIT: Consistently use `TypeIs`/`OperIs` for type/oper checks (#116141)
jakobbotsch Jun 3, 2025
b78c19a
Notify debuggers on rethrown exceptions (#116246)
MichalStrehovsky Jun 3, 2025
ca376e3
Generalize check for dynamic scopes when saving EH clauses (#116217)
jakobbotsch Jun 3, 2025
bf80e0e
JIT: Avoid VN-based removal of dead byref store in async funcs (#116136)
jakobbotsch Jun 3, 2025
b1c624c
Enable Unsafe.Bitcast for Nullable<T> (#116232)
jkotas Jun 3, 2025
8ffd896
Enable exceptions on wrong LDELEM arguments (#116235)
janvorli Jun 3, 2025
1a7d466
Fix floating point <-> integer conversions (#116236)
janvorli Jun 3, 2025
9d1d2a9
Implement empty string intrinsic in interpreter (#116237)
janvorli Jun 3, 2025
b142ec3
Fix LDTOKEN in interpreter (#116234)
janvorli Jun 3, 2025
6ab0311
SVE2 API for AbsoluteDifferenceAdd (#115513)
jacob-crawley Jun 3, 2025
b4eb528
Replace runtime's 'OutputRID' by the corresponding vmr property 'Targ…
tmds Jun 3, 2025
3aa0c97
[wasm] Always respect WasmEnableSIMD when trimming (#115313)
lewing Jun 3, 2025
de610d0
Option to disallow duplicate JSON properties (#115856)
PranavSenthilnathan Jun 3, 2025
4572cef
JIT: Remove old loop metrics (#116267)
amanasifkhalid Jun 3, 2025
c630f27
Share allocation helpers between CoreCLR and NativeAOT (#115339)
filipnavara Jun 3, 2025
54a677f
Cleanup HMF definitions
am11 May 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 4 additions & 4 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,15 @@
<MicrosoftNetCoreAppRefPackRefDir>$([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'ref', '$(NetCoreAppCurrent)'))</MicrosoftNetCoreAppRefPackRefDir>
<MicrosoftNetCoreAppRefPackDataDir>$([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRefPackDir)', 'data'))</MicrosoftNetCoreAppRefPackDataDir>

<MicrosoftNetCoreAppRuntimePackDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(OutputRID)', '$(LibrariesConfiguration)'))</MicrosoftNetCoreAppRuntimePackDir>
<MicrosoftNetCoreAppRuntimePackRidDir>$([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)', 'runtimes', '$(OutputRID)'))</MicrosoftNetCoreAppRuntimePackRidDir>
<MicrosoftNetCoreAppRuntimePackDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'microsoft.netcore.app.runtime.$(TargetRid)', '$(LibrariesConfiguration)'))</MicrosoftNetCoreAppRuntimePackDir>
<MicrosoftNetCoreAppRuntimePackRidDir>$([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackDir)', 'runtimes', '$(TargetRid)'))</MicrosoftNetCoreAppRuntimePackRidDir>
<MicrosoftNetCoreAppRuntimePackRidLibTfmDir>$([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'lib', '$(NetCoreAppCurrent)'))</MicrosoftNetCoreAppRuntimePackRidLibTfmDir>
<MicrosoftNetCoreAppRuntimePackNativeDir>$([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'native'))</MicrosoftNetCoreAppRuntimePackNativeDir>
</PropertyGroup>

<PropertyGroup>
<DotNetHostBinDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', '$(OutputRID).$(HostConfiguration)', 'corehost'))</DotNetHostBinDir>
<DotNetCdacBinDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'mscordaccore_universal', '$(Configuration)', '$(NetCoreAppCurrent)', '$(OutputRID)', 'publish'))</DotNetCdacBinDir>
<DotNetHostBinDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', '$(TargetRid).$(HostConfiguration)', 'corehost'))</DotNetHostBinDir>
<DotNetCdacBinDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'mscordaccore_universal', '$(Configuration)', '$(NetCoreAppCurrent)', '$(TargetRid)', 'publish'))</DotNetCdacBinDir>
</PropertyGroup>

<!--Feature switches -->
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@

<PropertyGroup>
<!-- Define properties that depend on the host SDK RID here. -->
<BuildHostTools Condition="'$(DotNetBuildOrchestrator)' == 'true' and '$(OutputRID)' != '$(NETCoreSdkRuntimeIdentifier)'">true</BuildHostTools>
<BuildHostTools Condition="'$(DotNetBuildOrchestrator)' == 'true' and '$(TargetRid)' != '$(NETCoreSdkRuntimeIdentifier)'">true</BuildHostTools>
<BuildHostILTools Condition="'$(BuildHostTools)' == 'true' and $([MSBuild]::IsOsPlatform(Windows))">true</BuildHostILTools>
</PropertyGroup>

Expand Down
33 changes: 7 additions & 26 deletions docs/design/coreclr/botr/corelib.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ The CLR provides a [`mscorlib` binder](https://github.com/dotnet/runtime/blob/ma

Two techniques exist for calling into the CLR from managed code. FCall allows you to call directly into the CLR code, and provides a lot of flexibility in terms of manipulating objects, though it is easy to cause GC holes by not tracking object references correctly. QCall also allows you to call into the CLR via the P/Invoke, but is much harder to accidentally mis-use. FCalls are identified in managed code as extern methods with the [`MethodImplOptions.InternalCall`](https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.methodimploptions) bit set. QCalls are marked `static extern` methods similar to regular P/Invokes, but are directed toward a library called `"QCall"`.

There is a small variant of FCall called HCall (for Helper call) for implementing JIT helpers. The HCall is intended for doing things like accessing multi-dimensional array elements, range checks, etc. The only difference between HCall and FCall is that HCall methods won't show up in an exception stack trace.

### Choosing between FCall, QCall, P/Invoke, and writing in managed code

First, remember that you should be writing as much as possible in managed code. You avoid a raft of potential GC hole issues, you get a better debugging experience, and the code is often simpler.
Expand All @@ -54,7 +52,7 @@ If the only reason you're defining a FCall method is to call a native method, yo

If you still need to implement a feature inside the runtime, consider if there is a way to reduce the frequency of transitioning to native code. Can you write the common case in managed and only call into native for some rare corner cases? You're usually best off keeping as much as possible in managed code.

QCalls are the preferred mechanism going forward. You should only use FCalls when you are "forced" to. This happens when there is common "short path" through the code that is important to optimize. This short path should not be more than a few hundred instructions, cannot allocate GC memory, take locks or throw exceptions (`GC_NOTRIGGER`, `NOTHROWS`). In all other circumstances (and especially when you enter a FCall and then simply erect HelperMethodFrame), you should be using QCall.
QCalls are the preferred mechanism going forward. You should only use FCalls when you are "forced" to. This happens when there is common "short path" through the code that is important to optimize. This short path should not be more than a few hundred instructions, cannot allocate GC memory, take locks or throw exceptions (`GC_NOTRIGGER`, `NOTHROWS`). In all other circumstances, you should be using QCall.

FCalls were specifically designed for short paths of code that must be optimized. They allowed explicit control over when erecting a frame was done. However, it is error prone and not worth the complexity for many APIs. QCalls are essentially P/Invokes into the CLR. In the event the performance of an FCall is required consider creating a QCall and marking it with [`SuppressGCTransitionAttribute`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.suppressgctransitionattribute).

Expand All @@ -64,8 +62,6 @@ As a result, QCalls give you some advantageous marshaling for `SafeHandle`s auto

QCalls are very much like a normal P/Invoke from CoreLib to CLR. Unlike FCalls, QCalls will marshal all arguments as unmanaged types like a normal P/Invoke. QCall also switch to preemptive GC mode like a normal P/Invoke. These two features should make QCalls easier to write reliably compared to FCalls. QCalls are not prone to GC holes and GC starvation bugs that are common with FCalls.

QCalls perform better than FCalls that erect a `HelperMethodFrame`. The overhead is about 1.4x less compared to FCall w/ `HelperMethodFrame` overhead on x86 and x64.

The preferred types for QCall arguments are primitive types that are efficiently handled by the P/Invoke marshaler (`INT32`, `LPCWSTR`, `BOOL`). Notice that `BOOL` is the correct boolean flavor for QCall arguments. On the other hand, `CLR_BOOL` is the correct boolean flavor for FCall arguments.

The pointers to common unmanaged EE structures should be wrapped into handle types. This is to make the managed implementation type safe and avoid falling into unsafe C# everywhere. See AssemblyHandle in [vm\qcall.h][qcall] for an example.
Expand Down Expand Up @@ -164,7 +160,7 @@ extern "C" BOOL QCALLTYPE Foo_BarInternal(int flags, LPCWSTR wszString, QCall::S

## FCall functional behavior

FCalls allow more flexibility in terms of passing object references around, but with higher code complexity and more opportunities to make mistakes. Additionally, FCall methods must either erect a helper method frame along their common code paths, or for any FCall of non-trivial length, explicitly poll for whether a garbage collection must occur. Failing to do so will lead to starvation issues if managed code repeatedly calls the FCall method in a tight loop, because FCalls execute while the thread only allows the GC to run in a cooperative manner.
FCalls allow more flexibility in terms of passing object references around, but with higher code complexity and more opportunities to make mistakes. Additionally, for any FCall of non-trivial length, explicitly poll for whether a garbage collection must occur. Failing to do so will lead to starvation issues if managed code repeatedly calls the FCall method in a tight loop, because FCalls execute while the thread only allows the GC to run in a cooperative manner.

FCalls require a lot of boilerplate code, too much to describe here. Refer to [fcall.h][fcall] for details.

Expand All @@ -176,8 +172,6 @@ A more complete discussion on GC holes can be found in the [CLR Code Guide](../.

Object references passed as parameters to FCall methods are not GC-protected, meaning that if a GC occurs, those references will point to the old location in memory of an object, not the new location. For this reason, FCalls usually follow the discipline of accepting something like `StringObject*` as their parameter type, then explicitly converting that to a `STRINGREF` before doing operations that may trigger a GC. If you expect to use an object reference later, you must GC protect object references before triggering a GC.

All GC heap allocations within an FCall method must happen within a helper method frame. If you allocate memory on the GC heap, the GC may collect dead objects and move objects around in unpredictable ways, with some low probability. For this reason, you must manually report any object references in your method to the GC, so that if a garbage collection occurs, your object reference will be updated to refer to the new location in memory. Any pointers into managed objects (like arrays or Strings) within your code will not be updated automatically, and must be re-fetched after any operation that may allocate memory and before your first usage. Reporting a reference can be done via the `GCPROTECT_*` macros or as parameters when erecting a helper method frame.

Failing to properly report an `OBJECTREF` or to update an interior pointer is commonly referred to as a "GC hole", because the `OBJECTREF` class will do some validation that it points to a valid object every time you dereference it in Debug and Checked builds. When an `OBJECTREF` pointing to an invalid object is dereferenced, an assert will trigger saying something like "Detected an invalid object reference. Possible GC hole?". This assert is unfortunately easy to hit when writing "manually managed" code.

Note that QCall's programming model is restrictive to sidestep GC holes by forcing you to pass in the address of an object reference on the stack. This guarantees that the object reference is GC protected by the JIT's reporting logic, and that the actual object reference will not move because it is not allocated in the GC heap. QCall is our recommended approach, precisely because it makes GC holes harder to write.
Expand All @@ -188,8 +182,6 @@ The managed stack walker needs to be able to find its way from FCalls. It is rel

Complex constructs like stack allocated objects with destructors or exception handling in the FCall implementation may confuse the epilog walker. This can lead to GC holes or crashes during stack walking. There is no comprehensive list of what constructs should be avoided to prevent this class of bugs. An FCall implementation that is fine one day may break with the next C++ compiler update. We depend on stress runs and code coverage to find bugs in this area.

Setting a breakpoint inside an FCall implementation may confuse the epilog walker. It leads to an "Invalid breakpoint in a helpermethod frame epilog" assert inside [vm\i386\gmsx86.cpp](https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/i386/gmsx86.cpp).

### FCall example – managed

Here's a real-world example from the `String` class:
Expand Down Expand Up @@ -218,29 +210,18 @@ The FCall entrypoint has to be registered in tables in [vm\ecalllist.h][ecalllis

[ecalllist]: https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/ecalllist.h

This method is an instance method in managed code, with the "this" parameter passed as the first argument. We use `StringObject*` as the argument type, then copy it into a `STRINGREF` so we get some error checking when we use it.
This example shows an FCall method that takes a managed object (`Object*`) as a raw pointer. These raw inputs are considered "unsafe" and must be validated or converted if they’re used in a GC-sensitive context.

```C++
FCIMPL1(Object*, AppDomainNative::IsStringInterned, StringObject* pStringUNSAFE)
FCIMPL1(FC_BOOL_RET, ExceptionNative::IsImmutableAgileException, Object* pExceptionUNSAFE)
{
FCALL_CONTRACT;

STRINGREF refString = ObjectToSTRINGREF(pStringUNSAFE);
STRINGREF* prefRetVal = NULL;

HELPER_METHOD_FRAME_BEGIN_RET_1(refString);

if (refString == NULL)
COMPlusThrow(kArgumentNullException, W("ArgumentNull_String"));

prefRetVal = GetAppDomain()->IsStringInterned(&refString);

HELPER_METHOD_FRAME_END();
ASSERT(pExceptionUNSAFE != NULL);

if (prefRetVal == NULL)
return NULL;
OBJECTREF pException = (OBJECTREF) pExceptionUNSAFE;

return OBJECTREFToObject(*prefRetVal);
FC_RETURN_BOOL(CLRException::IsPreallocatedExceptionObject(pException));
}
FCIMPLEND
```
Expand Down
4 changes: 2 additions & 2 deletions docs/design/coreclr/botr/exceptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,9 @@ This is the "fcall", "jit helper", and so forth. The typical way that the runtim

On the other hand, if an fcall function can do anything that might throw a CLR internal exception (one of the C++ exceptions), that exception must not be allowed to leak back out to managed code. To handle this case, the CLR has the UnwindAndContinueHandler (UACH), which is a set of code to catch the C++ EH exceptions, and re-raise them as managed exceptions.

Any runtime function that is called from managed code, and might throw a C++ EH exception, must wrap the throwing code in INSTALL_UNWIND_AND_CONTINUE_HANDLER / UNINSTALL_UNWIND_AND_CONTINUE_HANDLER. Installing a HELPER_METHOD_FRAME will automatically install the UACH. There is a non-trivial amount of overhead to installing a UACH, so they shouldn't be used everywhere. One technique that is used in performance critical code is to run without a UACH, and install one just before throwing an exception.
Any runtime function that is called from managed code, and might throw a C++ EH exception, must wrap the throwing code in INSTALL_UNWIND_AND_CONTINUE_HANDLER / UNINSTALL_UNWIND_AND_CONTINUE_HANDLER. There is a non-trivial amount of overhead to installing a UACH, so they shouldn't be used everywhere. One technique that is used in performance critical code is to run without a UACH, and install one just before throwing an exception.

When a C++ exception is thrown, and there is a missing UACH, the typical failure will be a Contract Violation of "GC_TRIGGERS called in a GC_NOTRIGGER region" in CPFH_RealFirstPassHandler. To fix these, look for managed to runtime transitions, and check for INSTALL_UNWIND_AND_CONTINUE_HANDLER or HELPER_METHOD_FRAME_BEGIN_XXX.
When a C++ exception is thrown, and there is a missing UACH, the typical failure will be a Contract Violation of "GC_TRIGGERS called in a GC_NOTRIGGER region" in CPFH_RealFirstPassHandler. To fix these, look for managed to runtime transitions, and check for INSTALL_UNWIND_AND_CONTINUE_HANDLER.

Runtime code into managed code
------------------------------
Expand Down
33 changes: 14 additions & 19 deletions docs/design/coreclr/botr/guide-for-porting.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,27 +340,22 @@ Here is an annotated list of the stubs implemented for Unix on Arm64.
calls. Necessary for all applications as this is how the main method is
called.

2. `LazyMachStateCaptureState`/`HelperMethodFrameRestoreState` – Needed to
support a GC occurring with an FCALL or HCALL on the stack. (Incorrect
implementations will cause unpredictable crashes during or after garbage
collection)

3. `NDirectImportThunk` – Needed to support saving off a set of arguments to
2. `NDirectImportThunk` – Needed to support saving off a set of arguments to
a p/invoke so that the runtime can find the actual target. Also uses one
of the secret arguments (Used by all p/invoke methods)

4. `PrecodeFixupThunk` – Needed to convert the secret argument from a
3. `PrecodeFixupThunk` – Needed to convert the secret argument from a
FixupPrecode\* to a MethodDesc\*. This function exists to reduce the
code size of FixupPrecodes as there are (Used by many managed methods)

5. `ThePreStub` - Needed to support saving off a set of arguments to the
4. `ThePreStub` - Needed to support saving off a set of arguments to the
stack so that the runtime can find or jit the right target method.
(Needed for any jitted method to execute Used by all managed methods)

6. `ThePreStubPatch` – Exists to provide a reliable spot for the managed
5. `ThePreStubPatch` – Exists to provide a reliable spot for the managed
debugger to put a breakpoint.

7. GC Write Barriers – These are used to provide the GC with information
6. GC Write Barriers – These are used to provide the GC with information
about what memory is being updated. The existing implementations of
these are all complex, and there are a number of controls where the
runtime can adjust to tweak the behavior of the barrier in various ways.
Expand All @@ -373,40 +368,40 @@ Here is an annotated list of the stubs implemented for Unix on Arm64.
FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP can be implemented as
performance needs require.

8. `ComCallPreStub`/ `COMToCLRDispatchHelper` /`GenericComCallStub` - not
7. `ComCallPreStub`/ `COMToCLRDispatchHelper` /`GenericComCallStub` - not
necessary for non-Windows platforms at this time

9. `TheUMEntryPrestub`/ `UMThunkStub` - used to enter the runtime from
8. `TheUMEntryPrestub`/ `UMThunkStub` - used to enter the runtime from
non-managed code through entrypoints generated from the
Marshal.GetFunctionPointerForDelegate api.

10. `OnHijackTripThread` - needed for thread suspension to support GC + other
9. `OnHijackTripThread` - needed for thread suspension to support GC + other
suspension requiring events. This is typically not needed for very early
stage bringup of the product, but will be needed for any decent size
application

11. `CallEHFunclet` – Used to call catch, finally and fault funclets. Behavior
10. `CallEHFunclet` – Used to call catch, finally and fault funclets. Behavior
is specific to exactly how funclets are implemented.

12. `CallEHFilterFunclet` – Used to call filter funclets. Behavior is specific
11. `CallEHFilterFunclet` – Used to call filter funclets. Behavior is specific
to exactly how funclets are implemented.

13. `ResolveWorkerChainLookupAsmStub`/ `ResolveWorkerAsmStub` Used for virtual
12. `ResolveWorkerChainLookupAsmStub`/ `ResolveWorkerAsmStub` Used for virtual
stub dispatch (virtual call support for interface, and some virtual
methods). These work in tandem with the logic in virtualcallstubcpu.h to
implement the logic described in [Virtual Stub Dispatch](virtual-stub-dispatch.md)

14. `ProfileEnter`/ `ProfileLeave`/ `ProfileTailcall` – Used to call function
13. `ProfileEnter`/ `ProfileLeave`/ `ProfileTailcall` – Used to call function
entry/exit profile functions acquired through the ICorProfiler
interface. Used in VERY rare circumstances. It is reasonable to wait to
implement these until the final stages of productization. Most profilers
do not use this functionality.

15. `JIT_PInvokeBegin`/`JIT_PInvokeEnd` – Leave/enter the managed runtime state. Necessary
14. `JIT_PInvokeBegin`/`JIT_PInvokeEnd` – Leave/enter the managed runtime state. Necessary
for ReadyToRun pre-compiled pinvoke calls, so that they do not cause GC
starvation

16. `VarargPInvokeStub`/ `GenericPInvokeCalliHelper` Used to support calli
15. `VarargPInvokeStub`/ `GenericPInvokeCalliHelper` Used to support calli
pinvokes. It is expected that C\# 8.0 will increase use of this feature.
Today use of this feature on Unix requires hand-written IL. On Windows
this feature is commonly used by C++/CLI
Expand Down
Loading