-
Notifications
You must be signed in to change notification settings - Fork 5k
Add ThisptrRetbufPrecode to set of precodes handled by StubPrecode path #112548
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 ThisptrRetbufPrecode to set of precodes handled by StubPrecode path #112548
Conversation
- Use the StubPrecode logic to create UMEntryThunk instances - Allocate them all on the Global LoaderAllocator - Unify the creation of UMEntryThunks between the C++/CLI fixup generation path and the delegate creation path - Remove ThunkHeapStubManager as it is now subsumed by RangeSectionStubManager - Tweak code paths around all of this so that we don't have unnecessary indirections (This change will add 2 memory loads to the UMEntryThunk path on x86/x64, but that should be fairly immaterial compared to the cost of an actual reverse p/invoke)
…he UMEntryThunks to not successfully allocate/work
Tagging subscribers to this area: @mangod9 |
…_retbuf_precode
Remove unused machinery
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds support for handling the ThisPtrRetBufPrecode in the StubPrecode path by updating both the design documentation and the precode resolution logic.
- Updated design doc to include Version 2 of the precode contract with ThisPtrRetBufPrecode support.
- Modified the GetMethodDesc methods to use different offsets based on the precode contract version.
Comments suppressed due to low confidence (2)
docs/design/datacontracts/PrecodeStubs.md:27
- [nitpick] There is a naming inconsistency between the PR title ('ThisPtrRetBufPrecode') and the documentation ('ThisPointerRetBufPrecodeType'). Consider aligning the naming for consistency.
| PrecodeMachineDescriptor | ThisPointerRetBufPrecodeType | precode sort byte for this pointer ret buf precodes |
docs/design/datacontracts/PrecodeStubs.md:180
- The branch for ContractVersion(PrecodeStubs) == 1 in this implementation throws a NotImplementedException, which could lead to runtime errors if this code path is hit. Consider adding a clarifying comment or an appropriate fallback to ensure production stability.
throw new NotImplementedException(); // TODO(cdac)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo two nits.
src/coreclr/vm/amd64/AsmHelpers.asm
Outdated
; rcx -This pointer | ||
; rdx -ReturnBuffer | ||
LEAF_ENTRY ThisPtrRetBufPrecodeWorker, _TEXT | ||
mov METHODDESC_REGISTER, [METHODDESC_REGISTER + 0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A nit - use the ThisPtrRetBufPrecodeData__Target
instead of 0 like in the Unix code?
// We get the precode type in two phases: | ||
// 1. Read the precode type from the intruction address. | ||
// 2. If it's "stub", look at the stub data and get the actual precode type - it could be stub, | ||
// but it could also be a pinvoke precode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A nit - this comment would be worth updating to mention the new precode
internal enum KnownPrecodeType | ||
{ | ||
Stub = 1, | ||
PInvokeImport, // also known as NDirectImport in the runtime | ||
Fixup, | ||
ThisPtrRetBuf, | ||
} | ||
|
||
internal abstract class ValidPrecode | ||
{ | ||
public TargetPointer InstrPointer { get; } | ||
public KnownPrecodeType PrecodeType { get; } | ||
|
||
protected ValidPrecode(TargetPointer instrPointer, KnownPrecodeType precodeType) | ||
{ | ||
InstrPointer = instrPointer; | ||
PrecodeType = precodeType; | ||
} | ||
|
||
internal abstract TargetPointer GetMethodDesc(Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor); | ||
|
||
} | ||
|
||
internal class StubPrecode : ValidPrecode | ||
{ | ||
internal StubPrecode(TargetPointer instrPointer, KnownPrecodeType type = KnownPrecodeType.Stub) : base(instrPointer, type) { } | ||
|
||
internal override TargetPointer GetMethodDesc(Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) | ||
{ | ||
TargetPointer stubPrecodeDataAddress = InstrPointer + precodeMachineDescriptor.StubCodePageSize; | ||
Data.StubPrecodeData_2 stubPrecodeData = target.ProcessedData.GetOrAdd<Data.StubPrecodeData_2>(stubPrecodeDataAddress); | ||
return stubPrecodeData.SecretParam; | ||
} | ||
} | ||
|
||
public sealed class PInvokeImportPrecode : StubPrecode | ||
{ | ||
internal PInvokeImportPrecode(TargetPointer instrPointer) : base(instrPointer, KnownPrecodeType.PInvokeImport) { } | ||
} | ||
|
||
public sealed class FixupPrecode : ValidPrecode | ||
{ | ||
internal FixupPrecode(TargetPointer instrPointer) : base(instrPointer, KnownPrecodeType.Fixup) { } | ||
internal override TargetPointer GetMethodDesc(Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) | ||
{ | ||
TargetPointer fixupPrecodeDataAddress = InstrPointer + precodeMachineDescriptor.StubCodePageSize; | ||
Data.FixupPrecodeData fixupPrecodeData = target.ProcessedData.GetOrAdd<Data.FixupPrecodeData>(fixupPrecodeDataAddress); | ||
return fixupPrecodeData.MethodDesc; | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could re-use these existing code from PrecodeStubs_1
.
This would require a an extra file, but I think eventually it may make sense to have a folder and namespace for each contract that has helpers or multiple versions.
public void TestPrecodeStubPrecodeExpectedMethodDesc_1(PrecodeTestDescriptor test) | ||
{ | ||
var builder = new PrecodeBuilder(test.Arch, /* Test version 1 of the contract */1); | ||
builder.AddPlatformMetadata(test); | ||
|
||
TargetPointer expectedMethodDesc = new TargetPointer(0xeeee_eee0u); // arbitrary | ||
TargetCodePointer stub1 = builder.AddStubPrecodeEntry("Stub 1", test, expectedMethodDesc); | ||
|
||
var target = CreateTarget(builder); | ||
Assert.NotNull(target); | ||
|
||
var precodeContract = target.Contracts.PrecodeStubs; | ||
|
||
Assert.NotNull(precodeContract); | ||
|
||
var actualMethodDesc = precodeContract.GetMethodDescFromStubAddress(stub1); | ||
Assert.Equal(expectedMethodDesc, actualMethodDesc); | ||
} | ||
|
||
[Theory] | ||
[MemberData(nameof(PrecodeTestDescriptorData))] | ||
public void TestPrecodeStubPrecodeExpectedMethodDesc_2(PrecodeTestDescriptor test) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if it makes more sense to write tests for each feature instead of for each contract version. On contracts versions that don't support a feature, the test can be skipped.
For example have the existing test run on both contract versions then the ThisPtrRetBufPrecodeEntry
only run on version 2.
@davidwrighton browser-wasm was failing the build when it was merged:
|
This adds the ThisPtrRetBufPrecode to the set of codes handled by the StubPrecode stub pasting path.
Perf changes from about 2.35 ns per iteration of a minimal loop with a ThisPtrRetBufPrecode to about 3.03 ns per iteration on my computer (Windows X64). I expect the cost to be a little less on an Arm64 machine since the old ThisPtrRetBufPrecode was a bit more expensive there. However, this removes the previous scheme where we allocate and jit custom stubs for this, so I think the simplification is worth the runtime cost.
The overall change has a size increase though, as this work adds support for this type of precode to the cDAC, as well as some tests.
Test app