-
Notifications
You must be signed in to change notification settings - Fork 5k
Block inlines with different exception wrapping behavior #113849
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
Conversation
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.
Copilot wasn't able to review any files in this pull request.
Files not reviewed (1)
- src/coreclr/vm/jitinterface.cpp: Language not supported
@jkotas PTAL |
src/coreclr/vm/jitinterface.cpp
Outdated
Module* pCalleeModule = pCallee->GetModule(); | ||
Module* pRootModule = pOrigCaller->GetModule(); | ||
|
||
if (pRootModule->IsRuntimeWrapExceptions() != pCalleeModule->IsRuntimeWrapExceptions()) |
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.
These checks are cached cheap bit tests. We should do them first, and run the more expensive IL header decoder only when the modes do not match.
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.
Also looks like there are still issues with (lack of) an IL header.
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.
Likely I need to handle transient methods here too.
I think we need the same check in crossgen2 for R2R |
Would the check go into |
I do not see caller and callee in CrossModuleInlineableUncached. I think it needs to be earlier. |
@jkotas I started in on crossgen2 support, but I can't see where custom attributes on modules are recorded, so could use some help finishing it off. Also I think it would be better to either always return INLINE_FAIL from |
@davidwrighton maybe you can give me some pointers? How do we find module attributes in crossgen2? I see support for assembly attributes but nothing for modules. |
We don't have higher level facilities for this in crossgen2, this will have to use System.Reflection.Metadata APIs, something along the lines of:
We'll probably want to cache this on EcmaModule. |
Thanks. By "this" do you mean "WrapNonExceptionThrows" or something more general? |
@AndyAyersMS just WrapNonExceptionThrows. We don't need this concept much, so let's not build a generalized mechanism unless we need to. |
EcmaModule rootModule = (root.OwningType as MetadataType)?.Module as EcmaModule; | ||
EcmaModule calleeModule = (callee.OwningType as MetadataType)?.Module as EcmaModule; | ||
|
||
// If this inline crosses module boundaries, ensure the modules agree on exception wrapping behavior. |
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 think this should be directly in CorInfoImpl.cs. It is not a policy decision whether the inlining is allowed for this case. It is required for correctness, so it should not be abstracted by the policy.
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.
Ok. I can then undo some of the other changes, since the policy will no longer need to know the root method.
@@ -16,6 +16,8 @@ public partial class EcmaModule : ModuleDesc | |||
{ | |||
private readonly PEReader _peReader; | |||
protected readonly MetadataReader _metadataReader; | |||
private bool _isWrapNonExceptionThrowsKnown; | |||
private bool _isWrapNonExceptionThrows; |
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.
These may need to be volatile
.
@jkotas PTAL. I haven't added a test case and am not sure this gets properly tested anywhere. |
I finally got around to testing this in crossgen, and the inline is still happening. It looks like the attribute is actually on the assembly, not on the module? In my C# calling assembly:
And the runtime seems to look it up on the assembly, even though it gets associated with the module...? runtime/src/coreclr/vm/ceeload.cpp Lines 1071 to 1099 in a27c3cd
|
`Yes, it looks like it. The attribute sources say the attribute target is an assembly: Line 9 in f7bffcb
My guess is that it is a left-over from fragile NGen. Fragile NGen allowed precomputing information associated with Module structure. So in order for the attribute value to be computed at NGen time, caches like this were moved from Assembly to Module structure. |
If I modify my EcmaModule code to look at assembly attributes then the inline is blocked as expected. I can push that change, or would you suggest revising the whole concept to be assembly-based throughout? |
I think it is fine to keep it on Module. Assembly and Module are 1:1 in .NET Core. The managed type system even has EcmaAssembly inheriting from EcmaModule. |
Are you going to add a test as well? |
I think the existing outerloop R2R pri1 composite tests should cover this (if they also compile the wrappers). But we may need to mark the IL method that throws in the normal runtime tests with aggressive inlining. Let me try that in a separate PR and see if I can get it to fail. |
if (!_isWrapNonExceptionThrowsKnown) | ||
{ | ||
var reader = MetadataReader; | ||
var c = reader.StringComparer; | ||
foreach (var attr in reader.GetAssemblyDefinition().GetCustomAttributes()) | ||
{ | ||
if (reader.GetAttributeNamespaceAndName(attr, out var ns, out var n)) | ||
{ | ||
if (c.Equals(ns, "System.Runtime.CompilerServices") && c.Equals(n, "RuntimeCompatibilityAttribute")) | ||
{ | ||
var dec = reader.GetCustomAttribute(attr).DecodeValue(new CustomAttributeTypeProvider(this)); | ||
|
||
foreach (var arg in dec.NamedArguments) | ||
{ | ||
if (arg.Name == "WrapNonExceptionThrows") | ||
{ | ||
if (!(arg.Value is bool)) | ||
ThrowHelper.ThrowBadImageFormatException(); | ||
_isWrapNonExceptionThrows = (bool)arg.Value; | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
_isWrapNonExceptionThrowsKnown = true; | ||
} | ||
return _isWrapNonExceptionThrows; | ||
} | ||
} |
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.
if (!_isWrapNonExceptionThrowsKnown) | |
{ | |
var reader = MetadataReader; | |
var c = reader.StringComparer; | |
foreach (var attr in reader.GetAssemblyDefinition().GetCustomAttributes()) | |
{ | |
if (reader.GetAttributeNamespaceAndName(attr, out var ns, out var n)) | |
{ | |
if (c.Equals(ns, "System.Runtime.CompilerServices") && c.Equals(n, "RuntimeCompatibilityAttribute")) | |
{ | |
var dec = reader.GetCustomAttribute(attr).DecodeValue(new CustomAttributeTypeProvider(this)); | |
foreach (var arg in dec.NamedArguments) | |
{ | |
if (arg.Name == "WrapNonExceptionThrows") | |
{ | |
if (!(arg.Value is bool)) | |
ThrowHelper.ThrowBadImageFormatException(); | |
_isWrapNonExceptionThrows = (bool)arg.Value; | |
break; | |
} | |
} | |
} | |
} | |
} | |
_isWrapNonExceptionThrowsKnown = true; | |
} | |
return _isWrapNonExceptionThrows; | |
} | |
} | |
if (!_isWrapNonExceptionThrowsComputed) | |
{ | |
_isWrapNonExceptionThrows = ComputeIsWrapNonExceptionThrows(); | |
_isWrapNonExceptionThrowsComputed = true; | |
} | |
return _isWrapNonExceptionThrows; | |
} | |
} | |
private bool ComputeIsWrapNonExceptionThrows() | |
{ | |
var reader = MetadataReader; | |
var c = reader.StringComparer; | |
foreach (var attr in reader.GetAssemblyDefinition().GetCustomAttributes()) | |
{ | |
if (reader.GetAttributeNamespaceAndName(attr, out var ns, out var n)) | |
{ | |
if (c.Equals(ns, "System.Runtime.CompilerServices") && c.Equals(n, "RuntimeCompatibilityAttribute")) | |
{ | |
var dec = reader.GetCustomAttribute(attr).DecodeValue(new CustomAttributeTypeProvider(this)); | |
foreach (var arg in dec.NamedArguments) | |
{ | |
if (arg.Name == "WrapNonExceptionThrows") | |
{ | |
if (!(arg.Value is bool)) | |
ThrowHelper.ThrowBadImageFormatException(); | |
return (bool)arg.Value; | |
} | |
} | |
} | |
} | |
} | |
return false; | |
} |
The AOT compilers use a pattern with separate method that computes the cached value. It makes the getter cheaper and inlining candidate.
src/coreclr/vm/jitinterface.cpp
Outdated
// We will either find or create transient method details. | ||
_ASSERTE(!cxt.HasTransientMethodDetails()); | ||
|
||
// IL methods with no RVA indicate there is no implementation defined in metadata. |
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.
This is copy&paste from CEEInfo::getMethodInfo
. Can this be factored into a method to avoid duplicate code?
I haven't been able to get the expected failure yet in CI. Not sure why yet.... |
Aha, the composite mode tests in R2R outerloop do not enable jit optimizations. If I enable optimization, then the composite R2R tests for Directed_3 will fail. Seems like running these tests with optimizations enabled is desirable, since very few people will build unoptimized R2R images. |
Also mark `filter` test entry point as `aggressiveinlining` to create a cross-assembly wrapped exception throw test when crossgen2 runs in composite mode. (see dotnet#113815 / dotnet#113849)
Also mark `filter` test entry point as `aggressiveinlining` to create a cross-assembly wrapped exception throw test when crossgen2 runs in composite mode. (see dotnet#113815 / dotnet#113849)
Now that #114464 is merged, and I've added aggressive inlining to the IL test case, crossgen2 tests should fail on that test, if the fix doesn't work. |
/azp run runtime-coreclr crossgen2 outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
@jkotas PTAL... |
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.
Thanks!
Fixes #113815