Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for cross module inlining and cross module generic compilation to Crossgen2 #68919

Merged
merged 22 commits into from
Jun 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5aa5f79
Add support for cross module inlining and cross module generic compil…
davidwrighton Jun 10, 2022
acf28b3
Temporary workaround
davidwrighton Jun 10, 2022
313792b
Disable cross module pinvoke inlining
davidwrighton Jun 10, 2022
6a5130f
Fix Mono.Linker.Tests
davidwrighton Jun 10, 2022
e31e8bf
Make the loader module algorithm more similar to calculations in cros…
davidwrighton Jun 10, 2022
308c5c1
Address code review feedback, and fix naming for native side of Ready…
davidwrighton Jun 10, 2022
7326b39
Update the R2R docs
davidwrighton Jun 11, 2022
ca1d623
Fix issues found in testing
davidwrighton Jun 13, 2022
0a4247f
Fix markdownlint
davidwrighton Jun 13, 2022
55a8164
Handle RawArrayData use in intrinsic in better way
davidwrighton Jun 13, 2022
64a63b9
Adjust command line switches for new behavior to be disabled, and exp…
davidwrighton Jun 13, 2022
3cbce96
Fix the build
davidwrighton Jun 14, 2022
c46fe1d
Fix break for R2R binaries without cross module inlining enabled
davidwrighton Jun 14, 2022
45fed21
Move shash to heap from global, just to see what it does
davidwrighton Jun 15, 2022
93a041f
Address Michal's feedback
davidwrighton Jun 16, 2022
a47ab4f
Merge branch 'main' of https://github.com/dotnet/runtime into cross_m…
davidwrighton Jun 16, 2022
85831bb
Merge branch 'main' of github.com:dotnet/runtime into cross_module_r2r
davidwrighton Jun 16, 2022
7d6e658
Address feedback final
davidwrighton Jun 17, 2022
7ab7301
Move MutableModule around per Michal's request
davidwrighton Jun 17, 2022
2e1541b
Fix error where the load in LookupMap can actually be torn in the pre…
davidwrighton Jun 17, 2022
6af05fd
Update R2R version bump to 6.3 as this missed checking in ahead of so…
davidwrighton Jun 18, 2022
bd13931
Merge branch 'main' into cross_module_r2r
davidwrighton Jun 18, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 71 additions & 14 deletions docs/design/coreclr/botr/readytorun-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Revisions:
* 4.1 - [Tomas Rylek](https://github.com/trylek) - 2020
* 5.3 - [Tomas Rylek](https://github.com/trylek) - 2021
* 5.4 - [David Wrighton](https://github.com/davidwrighton) - 2021
* 6.3 - [David Wrighton](https://github.com/davidwrighton) - 2022

# Introduction

Expand Down Expand Up @@ -124,12 +125,15 @@ struct READYTORUN_CORE_HEADER

### READYTORUN_CORE_HEADER::Flags

| Flag | Value | Description
|:----------------------------------------|-----------:|:-----------
| READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE | 0x00000001 | Set if the original IL image was platform neutral. The platform neutrality is part of assembly name. This flag can be used to reconstruct the full original assembly name.
| READYTORUN_FLAG_COMPOSITE | 0x00000002 | The image represents a composite R2R file resulting from a combined compilation of a larger number of input MSIL assemblies.
| READYTORUN_FLAG_EMBEDDED_MSIL | 0x00000004 | Input MSIL is embedded in the R2R image.
| READYTORUN_FLAG_COMPONENT | 0x00000008 | This is a component assembly of a composite R2R image
| Flag | Value | Description
|:-------------------------------------------|-----------:|:-----------
| READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE | 0x00000001 | Set if the original IL image was platform neutral. The platform neutrality is part of assembly name. This flag can be used to reconstruct the full original assembly name.
trylek marked this conversation as resolved.
Show resolved Hide resolved
| READYTORUN_FLAG_COMPOSITE | 0x00000002 | The image represents a composite R2R file resulting from a combined compilation of a larger number of input MSIL assemblies.
| READYTORUN_FLAG_PARTIAL | 0x00000004 |
| READYTORUN_FLAG_NONSHARED_PINVOKE_STUBS | 0x00000008 | PInvoke stubs compiled into image are non-shareable (no secret parameter)
| READYTORUN_FLAG_EMBEDDED_MSIL | 0x00000010 | Input MSIL is embedded in the R2R image.
| READYTORUN_FLAG_COMPONENT | 0x00000020 | This is a component assembly of a composite R2R image
| READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE | 0x00000040 | This R2R module has multiple modules within its version bubble (For versions before version 6.2, all modules are assumed to possibly have this characteristic)

## READYTORUN_SECTION

Expand Down Expand Up @@ -173,6 +177,7 @@ The following section types are defined and described later in this document:
| OwnerCompositeExecutable | 116 | Image (added in V4.1)
| PgoInstrumentationData | 117 | Image (added in V5.2)
| ManifestAssemblyMvids | 118 | Image (added in V5.3)
| CrossModuleInlineInfo | 119 | Image (added in V6.3)

## ReadyToRunSectionType.CompilerIdentifier

Expand Down Expand Up @@ -203,13 +208,19 @@ struct READYTORUN_IMPORT_SECTION

| ReadyToRunImportSectionFlags | Value | Description
|:---------------------------------------|-------:|:-----------
| READYTORUN_IMPORT_SECTION_FLAGS_EAGER | 0x0001 | Set if the slots in the section have to be initialized at image load time. It is used to avoid lazy initialization when it cannot be done or when it would have undesirable reliability or performance effects (unexpected failure or GC trigger points, overhead of lazy initialization).
| ReadyToRunImportSectionFlags::None | 0x0000 | None
| ReadyToRunImportSectionFlags::Eager | 0x0001 | Set if the slots in the section have to be initialized at image load time. It is used to avoid lazy initialization when it cannot be done or when it would have undesirable reliability or performance effects (unexpected failure or GC trigger points, overhead of lazy initialization).
| ReadyToRunImportSectionFlags::PCode | 0x0004 | Section contains pointers to code


### READYTORUN_IMPORT_SECTIONS::Type

| ReadyToRunImportSectionType | Value | Description
|:---------------------------------------|-------:|:-----------
| READYTORUN_IMPORT_SECTION_TYPE_UNKNOWN | 0 | The type of slots in this section is unspecified.
| ReadyToRunImportSectionType | Value | Description
|:--------------------------------------------|-------:|:-----------
| ReadyToRunImportSectionType::Unknown | 0 | The type of slots in this section is unspecified.
| ReadyToRunImportSectionType::StubDispatch | 2 | The type of slots in this section rely on stubs for dispatch.
| ReadyToRunImportSectionType::StringHandle | 3 | The type of slots in this section hold strings
| ReadyToRunImportSectionType::ILBodyFixups | 7 | The type of slots in this section represent cross module IL bodies

*Future*: The section type can be used to group slots of the same type together. For example, all virtual
stub dispatch slots may be grouped together to simplify resetting of virtual stub dispatch cells into their
Expand Down Expand Up @@ -264,6 +275,8 @@ fixup kind, the rest of the signature varies based on the fixup kind.
| READYTORUN_FIXUP_Verify_TypeLayout | 0x32 | Generate a runtime check to ensure that the field offset matches between compile and runtime. Unlike CheckFieldOffset, this will generate a runtime exception on failure instead of silently dropping the method
| READYTORUN_FIXUP_Check_VirtualFunctionOverride | 0x33 | Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, code will not be used. See [Virtual override signatures](virtual-override-signatures) for details of the signature used.
| READYTORUN_FIXUP_Verify_VirtualFunctionOverride | 0x33 | Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, generate runtime failure. See [Virtual override signatures](virtual-override-signatures) for details of the signature used.
| READYTORUN_FIXUP_Check_IL_Body | 0x35 | Check to see if an IL method is defined the same at runtime as at compile time. A failed match will cause code not to be used. See[IL Body signatures](il-body-signatures) for details.
| READYTORUN_FIXUP_Verify_IL_Body | 0x36 | Verify an IL body is defined the same at compile time and runtime. A failed match will cause a hard runtime failure. See[IL Body signatures](il-body-signatures) for details.
| READYTORUN_FIXUP_ModuleOverride | 0x80 | When or-ed to the fixup ID, the fixup byte in the signature is followed by an encoded uint with assemblyref index, either within the MSIL metadata of the master context module for the signature or within the manifest metadata R2R header table (used in cases inlining brings in references to assemblies not seen in the input MSIL).

#### Method Signatures
Expand Down Expand Up @@ -304,6 +317,9 @@ ECMA 335 does not have a natural encoding for describing an overriden method. Th
| READYTORUN_VIRTUAL_OVERRIDE_None | 0x00 | No flags are set
| READYTORUN_VIRTUAL_OVERRIDE_VirtualFunctionOverriden | 0x01 | If set, then the virtual function has an implementation, which is encoded in the optional method implementation signature.

#### IL Body signatures

ECMA 335 does not define a format that can represent the exact implementation of a method by itself. This signature holds all of the IL of the method, the EH table, the locals table, and each token (other than type references) in those tables is replaced with an index into a local stream of signatures. Those signatures are simply verbatim copies of the needed metadata to describe MemberRefs, TypeSpecs, MethodSpecs, StandaloneSignatures and strings. All of that is bundled into a large byte array. In addition, a series of TypeSignatures follows which allow the type references to be resolved, as well as a methodreference to the uninstantiated method. Assuming all of this matches with the data that is present at runtime, the fixup is considered to be satisfied. See ReadyToRunStandaloneMetadata.cs for the exact details of the format.

### READYTORUN_IMPORT_SECTIONS::AuxiliaryData

Expand Down Expand Up @@ -495,7 +511,7 @@ properly look up methods stored in this section in the composite R2R case.

**TODO**: document profile data encoding

## ReadyToRunSectionType.ManifestMetadata (v2.3+)
## ReadyToRunSectionType.ManifestMetadata (v2.3+ with changes for v6.3+)

Manifest metadata is an [ECMA-335] metadata blob containing extra reference assemblies within
the version bubble introduced by inlining on top of assembly references stored in the input MSIL.
Expand All @@ -504,12 +520,16 @@ translate module override indices in signatures to the actual reference modules
the `READYTORUN_FIXUP_ModuleOverride` bit flag on the signature fixup byte or the
`ELEMENT_TYPE_MODULE_ZAPSIG` COR element type).

**Note:** It doesn't make sense to store references to assemblies external to the version bubble
in the manifest metadata as there's no guarantee that their metadata token values remain
constant; thus we cannot encode signatures relative to them.
**Note:** It doesn't make sense to use references to assemblies external to the version bubble
in the manifest metadata via the `READYTORUN_FIXUP_ModuleOverride` or `ELEMENT_TYPE_MODULE_ZAPSIG` concept
as there's no guarantee that their metadata token values remain constant; thus we cannot encode signatures relative to them.
However, as of R2R version 6.2, the native manifest metadata may contain tokens to be further resolved to actual
implementation assemblies.

The module override index translation algorithm is as follows (**ILAR** = *the number of `AssemblyRef` rows in the input MSIL*):

For R2R version 6.2 and below

| Module override index (*i*) | Reference assembly
|:----------------------------|:------------------
| *i* = 0 | Global context - assembly containing the signature
Expand All @@ -518,6 +538,16 @@ The module override index translation algorithm is as follows (**ILAR** = *the n

**Note:** This means that the entry corresponding to *i* = **ILAR** + 1 is actually undefined as it corresponds to the `NULL` entry (ROWID #0) in the manifest metadata AssemblyRef table. The first meaningful index into the manifest metadata, *i* = **ILAR** + 2, corresponding to ROWID #1, is historically filled in by Crossgen with the input assembly info but this shouldn't be depended upon, in fact the input assembly is useless in the manifest metadata as the module override to it can be encoded by using the special index 0.

For R2R version 6.3 and above
| Module override index (*i*) | Reference assembly
|:----------------------------|:------------------
| *i* = 0 | Global context - assembly containing the signature
| 1 <= *i* <= **ILAR** | *i* is the index into the MSIL `AssemblyRef` table
| *i* = **ILAR** + 1 | *i* is the index which refers to the Manifest metadata itself
| *i* > **ILAR** + 1 | *i* - **ILAR** - 2 is the zero-based index into the `AssemblyRef` table in the manifest metadata

In addition, a ModuleRef within the module which refers to `System.Private.CoreLib` may be used to serve as the *ResolutionContext* of a *TypeRef* within the manifest metadata. This will always refer to the module which contains the `System.Object` type.

## ReadyToRunSectionType.AttributePresence (v3.1+)

**TODO**: document attribute presence encoding
Expand Down Expand Up @@ -578,6 +608,33 @@ Number of assemblies stored in the manifest metadata is equal to the number of M
MVID records are used at runtime to verify that the assemblies loaded match those referenced by the
manifest metadata representing the versioning bubble.

## ReadyToRunSectionType.CrossModuleInlineInfo (v6.3+)
The inlining information section captures what methods got inlined into other methods. It consists of a single _Native Format Hashtable_ (described below).

The entries in the hashtable are lists of inliners for each inlinee. One entry in the hashtable corresponds to one inlinee. The hashtable is hashed with the version resilient hashcode of the uninstantiated methoddef inlinee.

The entry of the hashtable is a counted sequence of compressed unsigned integers which begins with an InlineeIndex which combines a 30 bit index with 2 bits of flags which how the sequence of inliners shall be parsed and what table is to be indexed into to find the inlinee.

* InlineeIndex
* Index with 2 flags field in lowest 2 bits to define the inlinee
- If (flags & 1) == 0 then index is a MethodDef RID, and if the module is a composite image, a module index of the method follows
- If (flags & 1) == 1, then index is an index into the ILBody import section
- If (flags & 2) == 0 then inliner list is:
- Inliner RID deltas - See definition below
- if (flags & 2) == 2 then what follows is:
- count of delta encoded indices into the ILBody import section
- the sequence of delta encoded indices into the first import section with a type of READYTORUN_IMPORT_SECTION_TYPE_ILBODYFIXUPS
- Inliner RID deltas - See definition below

* Inliner RID deltas (for multi-module version bubble images specified by the module having the READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE flag set)
- a sequence of inliner RID deltas with flag in the lowest bit
- if flag is set, the inliner RID is followed by a module ID
- otherwise the module is the same as the module of the inlinee method
* Inliner RID deltas (for single module version bubble images)
- a sequence of inliner RID deltas

This section may be included in addition to a InliningInfo2 section.

# Native Format

Native format is set of encoding patterns that allow persisting type system data in a binary format that is
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1706,7 +1706,7 @@ ClrDataAccess::GetModuleData(CLRDATA_ADDRESS addr, struct DacpModuleData *Module
ModuleData->TypeRefToMethodTableMap = PTR_CDADDR(pModule->m_TypeRefToMethodTableMap.pTable);
ModuleData->MethodDefToDescMap = PTR_CDADDR(pModule->m_MethodDefToDescMap.pTable);
ModuleData->FieldDefToDescMap = PTR_CDADDR(pModule->m_FieldDefToDescMap.pTable);
ModuleData->MemberRefToDescMap = NULL;
ModuleData->MemberRefToDescMap = PTR_CDADDR(pModule->m_MemberRefMap.pTable);
ModuleData->FileReferencesMap = PTR_CDADDR(pModule->m_FileReferencesMap.pTable);
ModuleData->ManifestModuleReferencesMap = PTR_CDADDR(pModule->m_ManifestModuleReferencesMap.pTable);

Expand Down
15 changes: 13 additions & 2 deletions src/coreclr/inc/corcompile.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ inline ReadyToRunImportSectionFlags operator &( const ReadyToRunImportSectionFla
return static_cast<ReadyToRunImportSectionFlags>(static_cast<uint16_t>(left) & static_cast<uint16_t>(right));
}

inline ReadyToRunCrossModuleInlineFlags operator |( const ReadyToRunCrossModuleInlineFlags left, const ReadyToRunCrossModuleInlineFlags right)
{
return static_cast<ReadyToRunCrossModuleInlineFlags>(static_cast<uint32_t>(left) | static_cast<uint32_t>(right));
}

inline ReadyToRunCrossModuleInlineFlags operator &( const ReadyToRunCrossModuleInlineFlags left, const ReadyToRunCrossModuleInlineFlags right)
{
return static_cast<ReadyToRunCrossModuleInlineFlags>(static_cast<uint32_t>(left) & static_cast<uint32_t>(right));
}

#ifdef TARGET_X86

typedef DPTR(RUNTIME_FUNCTION) PTR_RUNTIME_FUNCTION;
Expand Down Expand Up @@ -149,17 +159,18 @@ enum CORCOMPILE_FIXUP_BLOB_KIND
ENCODE_CHECK_VIRTUAL_FUNCTION_OVERRIDE, /* Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, code will not be used */
ENCODE_VERIFY_VIRTUAL_FUNCTION_OVERRIDE, /* Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, generate runtime failure. */

ENCODE_CHECK_IL_BODY, /* Check to see if an IL method is defined the same at runtime as at compile time. A failed match will cause code not to be used. */
ENCODE_VERIFY_IL_BODY, /* Verify an IL body is defined the same at compile time and runtime. A failed match will cause a hard runtime failure. */

ENCODE_MODULE_HANDLE = 0x50, /* Module token */
ENCODE_STATIC_FIELD_ADDRESS, /* For accessing a static field */
ENCODE_MODULE_ID_FOR_STATICS, /* For accessing static fields */
ENCODE_MODULE_ID_FOR_GENERIC_STATICS, /* For accessing static fields */
ENCODE_CLASS_ID_FOR_STATICS, /* For accessing static fields */
ENCODE_SYNC_LOCK, /* For synchronizing access to a type */
ENCODE_PROFILING_HANDLE, /* For the method's profiling counter */
ENCODE_VARARGS_METHODDEF, /* For calling a varargs method */
ENCODE_VARARGS_METHODREF,
ENCODE_VARARGS_SIG,
ENCODE_ACTIVE_DEPENDENCY, /* Conditional active dependency */
};

enum EncodeMethodSigFlags
Expand Down
20 changes: 18 additions & 2 deletions src/coreclr/inc/readytorun.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

// Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
#define READYTORUN_MAJOR_VERSION 0x0006
#define READYTORUN_MINOR_VERSION 0x0002
#define READYTORUN_MINOR_VERSION 0x0003

#define MINIMUM_READYTORUN_MAJOR_VERSION 0x006

Expand Down Expand Up @@ -60,6 +60,7 @@ enum ReadyToRunFlag
READYTORUN_FLAG_NONSHARED_PINVOKE_STUBS = 0x00000008, // PInvoke stubs compiled into image are non-shareable (no secret parameter)
READYTORUN_FLAG_EMBEDDED_MSIL = 0x00000010, // MSIL is embedded in the composite R2R executable
READYTORUN_FLAG_COMPONENT = 0x00000020, // This is the header describing a component assembly of composite R2R
READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE = 0x00000040, // This R2R module has multiple modules within its version bubble (For versions before version 6.2, all modules are assumed to possibly have this characteristic)
};

enum class ReadyToRunSectionType : uint32_t
Expand All @@ -83,6 +84,7 @@ enum class ReadyToRunSectionType : uint32_t
OwnerCompositeExecutable = 116, // Added in V4.1
PgoInstrumentationData = 117, // Added in V5.2
ManifestAssemblyMvids = 118, // Added in V5.3
CrossModuleInlineInfo = 119, // Added in V6.2

// If you add a new section consider whether it is a breaking or non-breaking change.
// Usually it is non-breaking, but if it is preferable to have older runtimes fail
Expand All @@ -99,9 +101,10 @@ struct READYTORUN_SECTION

enum class ReadyToRunImportSectionType : uint8_t
{
Unknown = 0,
Unknown = 0,
StubDispatch = 2,
StringHandle = 3,
ILBodyFixups = 7,
};

enum class ReadyToRunImportSectionFlags : uint16_t
Expand Down Expand Up @@ -165,6 +168,16 @@ enum ReadyToRunVirtualFunctionOverrideFlags
READYTORUN_VIRTUAL_OVERRIDE_VirtualFunctionOverriden = 0x01,
};

enum class ReadyToRunCrossModuleInlineFlags : uint32_t
{
CrossModuleInlinee = 0x1,
HasCrossModuleInliners = 0x2,
CrossModuleInlinerIndexShift = 2,

InlinerRidHasModule = 0x1,
InlinerRidShift = 1,
};

//
// Constants for fixup signature encoding
//
Expand Down Expand Up @@ -227,6 +240,9 @@ enum ReadyToRunFixupKind

READYTORUN_FIXUP_Check_VirtualFunctionOverride = 0x33, /* Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, code will not be used */
READYTORUN_FIXUP_Verify_VirtualFunctionOverride = 0x34, /* Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, generate runtime failure. */

READYTORUN_FIXUP_Check_IL_Body = 0x35, /* Check to see if an IL method is defined the same at runtime as at compile time. A failed match will cause code not to be used. */
READYTORUN_FIXUP_Verify_IL_Body = 0x36, /* Verify an IL body is defined the same at compile time and runtime. A failed match will cause a hard runtime failure. */
};

//
Expand Down
Loading