Skip to content

Commit

Permalink
Merge branch 'main' into fix-interp-kg
Browse files Browse the repository at this point in the history
  • Loading branch information
lewing authored Apr 6, 2024
2 parents e74e1b1 + 995409f commit e1654bb
Show file tree
Hide file tree
Showing 949 changed files with 28,646 additions and 16,551 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
]
},
"microsoft.dotnet.xharness.cli": {
"version": "9.0.0-prerelease.24168.2",
"version": "9.0.0-prerelease.24203.1",
"commands": [
"xharness"
]
Expand Down
6 changes: 4 additions & 2 deletions docs/design/coreclr/botr/clr-abi.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,13 @@ This section describes the conventions the JIT needs to follow when generating c

## Funclets

For all platforms except Windows/x86, all managed EH handlers (finally, fault, filter, filter-handler, and catch) are extracted into their own 'funclets'. To the OS they are treated just like first class functions (separate PDATA and XDATA (`RUNTIME_FUNCTION` entry), etc.). The CLR currently treats them just like part of the parent function in many ways. The main function and all funclets must be allocated in a single code allocation (see hot cold splitting). They 'share' GC info. Only the main function prolog can be hot patched.
For all platforms except Windows/x86 on CoreCLR, all managed EH handlers (finally, fault, filter, filter-handler, and catch) are extracted into their own 'funclets'. To the OS they are treated just like first class functions (separate PDATA and XDATA (`RUNTIME_FUNCTION` entry), etc.). The CLR currently treats them just like part of the parent function in many ways. The main function and all funclets must be allocated in a single code allocation (see hot cold splitting). They 'share' GC info. Only the main function prolog can be hot patched.

The only way to enter a handler funclet is via a call. In the case of an exception, the call is from the VM's EH subsystem as part of exception dispatch/unwind. In the non-exceptional case, this is called local unwind or a non-local exit. In C# this is accomplished by simply falling-through/out of a try body or an explicit goto. In IL this is always accomplished via a LEAVE opcode, within a try body, targeting an IL offset outside the try body. In such cases the call is from the JITed code of the parent function.

For Windows/x86, all handlers are generated within the method body, typically in lexical order. A nested try/catch is generated completely within the EH region in which it is nested. These handlers are essentially "in-line funclets", but they do not look like normal functions: they do not have a normal prolog or epilog, although they do have special entry/exit and register conventions. Also, nested handlers are not un-nested as for funclets: the code for a nested handler is generated within the handler in which it is nested.
For Windows/x86 on CoreCLR, all handlers are generated within the method body, typically in lexical order. A nested try/catch is generated completely within the EH region in which it is nested. These handlers are essentially "in-line funclets", but they do not look like normal functions: they do not have a normal prolog or epilog, although they do have special entry/exit and register conventions. Also, nested handlers are not un-nested as for funclets: the code for a nested handler is generated within the handler in which it is nested.

For Windows/x86 on NativeAOT and Linux/x86, funclets are used just like on other platforms.

## Cloned finallys

Expand Down
288 changes: 288 additions & 0 deletions docs/design/coreclr/jit/profile-count-reconstruction.md

Large diffs are not rendered by default.

100 changes: 100 additions & 0 deletions docs/design/datacontracts/contract-descriptor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Contract Descriptor

## Summary

The [data contracts design](./datacontracts_design.md) is a mechanism that allows diagnostic tooling
to understand the behavior of certain .NET runtime subsystems and data structures. In a typical
scenario, a diagnostic tool such as a debugger may have access to a target .NET process (or a memory
dump of such a process) from which it may request to read and write certain regions of memory.

This document describes a mechanism by which a diagnostic tool may acquire the following information:
* some details about the target process' architecture
* a collection of types and their sizes and/or the offsets of certain fields within each type
* a collection of global values
* a collection of /algorithmic contracts/ that are satisfied by the target process

## Contract descriptor

The contract descriptor consists of the follow structure. All multi-byte values are in target architecture endianness.

```c
struct DotNetRuntimeContractDescriptor
{
uint64_t magic;
uint32_t flags;
uint32_t descriptor_size;
const char *descriptor;
uint32_t aux_data_count;
uint32_t pad0;
uintptr_t *aux_data;
};
```

The `magic` is `0x44_4e_43_43_44_41_43_00` ("DNCCDAC\0") stored using the target architecture
endianness. This is sufficient to discover the target architecture endianness by comparing the
value in memory to `0x44_4e_43_43_44_41_43_00` and to `0x00_43_41_44_43_43_4e_44`.

The following `flags` bits are defined:

| Bits 31-2 | Bit 1 | Bit 0 |
| --------- | ------- | ----- |
| Reserved | ptrSize | 1 |

If `ptrSize` is 0, the architecture is 64-bit. If it is 1, the architecture is 32-bit. The
reserved bits should be written as zero. Diagnostic tooling may ignore non-zero reserved bits.

The `descriptor` is a pointer to a UTF-8 JSON string described in [data descriptor physical layout](./data_descriptor.md#Physical_JSON_descriptor). The total number of bytes is given by `descriptor_size`.

The auxiliary data for the JSON descriptor is stored at the location `aux_data` in `aux_data_count` pointer-sized slots.

### Architecture properties

Although `DotNetRuntimeContractDescriptor` contains enough information to discover the target
architecture endianness pointer size, it is expected that in all scenarios diagnostic tooling will
already have this information available through other channels. Diagnostic tools may use the
information derived from `DotNetRuntimeContractDescriptor` for validation.

### Compatible contracts

The `descriptor` is a JSON dictionary that is used for storing the [in-memory data descriptor](./data_descriptor.md#Physical_JSON_Descriptor)
and the [compatible contracts](./datacontracts_design.md#Compatible_Contract).

The compatible contracts are stored in the top-level key `"contracts"`. The value will be a
dictionary that contains each contract name as a key. Each value is the version of the contract as
a JSON integer constant.

**Contract example**:

``` jsonc
{"Thread":1,"GCHandle":1,...}
```

**Complete in-memory data descriptor example**:

``` jsonc
{
"version": "0",
"baseline": "example-64",
"types":
{
"Thread": { "ThreadId": 32, "ThreadState": 0, "Next": 128 },
"ThreadStore": { "ThreadCount": 32, "ThreadList": 8 }
},
"globals":
{
"FEATURE_COMINTEROP": 0,
"s_pThreadStore": [ 0 ] // indirect from aux data offset 0
},
"contracts": {"Thread": 1, "GCHandle": 1, "ThreadStore": 1}
}
```

## Contract symbol

To aid in discovery, the contract descriptor should be exported by the module hosting the .NET
runtime with the name `DotNetRuntimeContractDescriptor` using the C symbol naming conventions of the
target platform.

In scenarios where multiple .NET runtimes may be present in a single process, diagnostic tooling
should look for the symbol in each loaded module to discover all the runtimes.

Loading

0 comments on commit e1654bb

Please sign in to comment.