Convert Array.IsSimpleCopy and CanAssignArray type to managed - #104103

Merged
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type
Jul 7, 2024
Merged

Convert Array.IsSimpleCopy and CanAssignArray type to managed#104103
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Move the two routines back again.

Removes a HELPER_METHOD_FRAME and improves performance for deciding the path.

Benchmark code:

Details
privateobject[]objArray_Int={123};privateobject[]objArray_Str={"abc"};privateint[]intArray={123};privateuint[]uintArray={123};privatebyte[]byteArray={1};privatestring[]strArray={"abc"};privateint*[]intPointerArray={(int*)0x1234};privateuint*[]uintPointerArray={(uint*)0x1234};[Benchmark]publicvoidSimpleCopy_Derived()=>Array.Copy(strArray,objArray_Str,1);[Benchmark]publicvoidMustCast()=>Array.Copy(objArray_Str,strArray,1);[Benchmark]publicvoidSimpleCopy_Primitive()=>Array.Copy(intArray,uintArray,1);[Benchmark]publicvoidPrimitiveWiden()=>Array.Copy(byteArray,intArray,1);[Benchmark]publicvoidSimpleCopy_Pointer()=>Array.Copy(intPointerArray,uintPointerArray,1);

Result:

MethodJobToolchainMeanErrorStdDevRatio
SimpleCopy_DerivedJob-RIAPVG\PR\corerun.exe10.092 ns0.0322 ns0.0269 ns0.87
SimpleCopy_DerivedJob-PSZNKI\main\corerun.exe11.586 ns0.1421 ns0.1329 ns1.00
MustCastJob-RIAPVG\PR\corerun.exe10.382 ns0.0319 ns0.0282 ns0.43
MustCastJob-PSZNKI\main\corerun.exe23.951 ns0.1351 ns0.1198 ns1.00
SimpleCopy_PrimitiveJob-RIAPVG\PR\corerun.exe7.974 ns0.0679 ns0.0635 ns0.69
SimpleCopy_PrimitiveJob-PSZNKI\main\corerun.exe11.608 ns0.0954 ns0.0893 ns1.00
PrimitiveWidenJob-RIAPVG\PR\corerun.exe12.496 ns0.0779 ns0.0691 ns0.44
PrimitiveWidenJob-PSZNKI\main\corerun.exe28.334 ns0.5434 ns0.5083 ns1.00
SimpleCopy_PointerJob-RIAPVG\PR\corerun.exe8.105 ns0.1383 ns0.1226 ns0.80
SimpleCopy_PointerJob-PSZNKI\main\corerun.exe10.155 ns0.0799 ns0.0747 ns1.00

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jun 27, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 27, 2024
@huoyaoyuanhuoyaoyuan added area-VM-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jun 27, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Comment on lines +220 to +226
{
// Only pointers are valid for TypeDesc in array element

// Compatible pointers
if (srcTH.CanCastTo(destTH))
return ArrayAssignType.SimpleCopy;
}

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any pointer type goes through the non-simple copy paths, it will result in the same fatal crash.

@jkotas

Copy link
Copy Markdown
Member

Could you please take a look at the Mono test failures?

if (result != CastResult.MaybeCast)
return result == CastResult.CanCast;

return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The first thing that the QCall is going to do is repeat the cache lookup...

@jkotasjkotasJun 28, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the unmanaged TypeHandle.CanCastTo assumes that some cases are very cheap to check for and it does not bother to add them to the cache. These cases will be much slower here since we are always going to take the QCall transition for them. We should either check for them here and/or add them to cache (similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons).

It probably does not matter for the one caller added in this PR, but it may matter for future callers.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons

The methods should probably be combined at managed side. They are doing the exact same things.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes (it is fine to do it in a follow up PR).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are doing the exact same things.

In fact they aren't. RuntimeTypeHandle::CanCastTo allows T -> Nullable<T>.

The non-cached cases include nullables, COM and I(Dynamic)Castable interfaces. I don't think specially handling them is worthy, even for future callers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you would either need to have a bool flag that controls the special handling or introduce two QCalls with similar implementation.

@jkotasjkotasJun 29, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you plan to do something about this one? (Unifying with RuntimeTypeHandle::CanCastTo should be separate PR, fixing CanCastTo_NoCacheLookup to avoid unnecessary cache lookup should be in this one.)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, together with some other cases sharable between RuntimeTypeHandle and MethodTable, like type equivalence.

@huoyaoyuan

huoyaoyuan commented Jun 28, 2024

Copy link
Copy Markdown
MemberAuthor

Could you please take a look at the Mono test failures?

Yes, I expect the same pointer case should also be disabled for mono.
Well it may be because mono doesn't support compatible pointer case. I don't think enabling it on mono is worthy.

}

[Fact]
[SkipOnMono("Mono does not support pointer compatibility in Array.Copy")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please open an issue on this and disable the test against this issue?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it something we'd ever want to support on mono?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not want to have behavior differences between different runtime. Every behavior difference between runtime is a bug. We may choose to not fix some of these bugs, but I do not see a good reason for it here.

For example, similar Mono-specific issue in casting logic was fixed just a few days ago: #103841

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the behavior is different among all three runtimes. NativeAOT allows conversion between any pointers.

The current coreclr behavior is really complex to support. Can we make a breaking change instead to support only exactly same pointers?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current coreclr behavior is really complex to support.

Why is it complex to support?

Can we make a breaking change instead to support only exactly same pointers?

I do not see how we would justify this breaking change.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #104197 for mono.

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Anything remaining for this?

@jkotas

Copy link
Copy Markdown
Member

Cleaning up the cache lookups #104103 (comment) ?

Comment threadsrc/coreclr/vm/comutilnative.cpp Outdated
return bResult;
}

extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
extern"C"BOOLQCALLTYPETypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)
extern"C"BOOLQCALLTYPETypeHandle_CanCastToNoCacheLookup(void* fromTypeHnd, void* toTypeHnd)

I think this would be a better name for the QCall to make it clear what it does. Matches convention used for cast helpers (ChkCastAny_NoCacheLookup, etc.)

public bool CanCastTo(TypeHandle destTH)
{
if (m_asTAddr == destTH.m_asTAddr)
return true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering whether this logic should live in CastHelpers.cs next to all other casting logic, so that it is not missed if there are any bug fixes.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They look quite inconsistent...Methods in CastHelpers are HCalls in jithelpers.
BTW does it make sense to convert such methods to QCall, and move out of jithelpers since they are not directly used by JIT?

@jkotasjkotasJul 7, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to convert such methods to QCall

Yes. We want to get rid of all FCalls with HELPER_METHOD_FRAME.

not directly used by JIT

Those are slow path for helpers used by the JIT. Fast paths of those helpers are either in assembly code or in C#.

The split between JIT helpers and other helpers is blurry. It is not unusual for the two to have overlapping logic. We even have methods that are used as JIT helpers, but they are used for other purposes as well. I do not have strong opinions about the best source file split. Anything we come up with will have some downsides.

This can be worked on in a follow up.

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@jkotas

Copy link
Copy Markdown
Member

/ba-g All failures have known issues opened for them. I am not able to tell why BA is not able to match them

@jkotas
jkotas merged commit 7b71281 into dotnet:mainJul 7, 2024
@huoyaoyuan
huoyaoyuan deleted the array-assign-type branch July 8, 2024 01:51
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 7, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@huoyaoyuan@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Convert Array.IsSimpleCopy and CanAssignArray type to managed - #104103

Merged
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type
Jul 7, 2024
Merged

Convert Array.IsSimpleCopy and CanAssignArray type to managed#104103
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Move the two routines back again.

Removes a HELPER_METHOD_FRAME and improves performance for deciding the path.

Benchmark code:

Details
privateobject[]objArray_Int={123};privateobject[]objArray_Str={"abc"};privateint[]intArray={123};privateuint[]uintArray={123};privatebyte[]byteArray={1};privatestring[]strArray={"abc"};privateint*[]intPointerArray={(int*)0x1234};privateuint*[]uintPointerArray={(uint*)0x1234};[Benchmark]publicvoidSimpleCopy_Derived()=>Array.Copy(strArray,objArray_Str,1);[Benchmark]publicvoidMustCast()=>Array.Copy(objArray_Str,strArray,1);[Benchmark]publicvoidSimpleCopy_Primitive()=>Array.Copy(intArray,uintArray,1);[Benchmark]publicvoidPrimitiveWiden()=>Array.Copy(byteArray,intArray,1);[Benchmark]publicvoidSimpleCopy_Pointer()=>Array.Copy(intPointerArray,uintPointerArray,1);

Result:

MethodJobToolchainMeanErrorStdDevRatio
SimpleCopy_DerivedJob-RIAPVG\PR\corerun.exe10.092 ns0.0322 ns0.0269 ns0.87
SimpleCopy_DerivedJob-PSZNKI\main\corerun.exe11.586 ns0.1421 ns0.1329 ns1.00
MustCastJob-RIAPVG\PR\corerun.exe10.382 ns0.0319 ns0.0282 ns0.43
MustCastJob-PSZNKI\main\corerun.exe23.951 ns0.1351 ns0.1198 ns1.00
SimpleCopy_PrimitiveJob-RIAPVG\PR\corerun.exe7.974 ns0.0679 ns0.0635 ns0.69
SimpleCopy_PrimitiveJob-PSZNKI\main\corerun.exe11.608 ns0.0954 ns0.0893 ns1.00
PrimitiveWidenJob-RIAPVG\PR\corerun.exe12.496 ns0.0779 ns0.0691 ns0.44
PrimitiveWidenJob-PSZNKI\main\corerun.exe28.334 ns0.5434 ns0.5083 ns1.00
SimpleCopy_PointerJob-RIAPVG\PR\corerun.exe8.105 ns0.1383 ns0.1226 ns0.80
SimpleCopy_PointerJob-PSZNKI\main\corerun.exe10.155 ns0.0799 ns0.0747 ns1.00

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jun 27, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 27, 2024
@huoyaoyuanhuoyaoyuan added area-VM-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jun 27, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Comment on lines +220 to +226
{
// Only pointers are valid for TypeDesc in array element

// Compatible pointers
if (srcTH.CanCastTo(destTH))
return ArrayAssignType.SimpleCopy;
}

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any pointer type goes through the non-simple copy paths, it will result in the same fatal crash.

@jkotas

Copy link
Copy Markdown
Member

Could you please take a look at the Mono test failures?

if (result != CastResult.MaybeCast)
return result == CastResult.CanCast;

return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The first thing that the QCall is going to do is repeat the cache lookup...

@jkotasjkotasJun 28, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the unmanaged TypeHandle.CanCastTo assumes that some cases are very cheap to check for and it does not bother to add them to the cache. These cases will be much slower here since we are always going to take the QCall transition for them. We should either check for them here and/or add them to cache (similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons).

It probably does not matter for the one caller added in this PR, but it may matter for future callers.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons

The methods should probably be combined at managed side. They are doing the exact same things.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes (it is fine to do it in a follow up PR).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are doing the exact same things.

In fact they aren't. RuntimeTypeHandle::CanCastTo allows T -> Nullable<T>.

The non-cached cases include nullables, COM and I(Dynamic)Castable interfaces. I don't think specially handling them is worthy, even for future callers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you would either need to have a bool flag that controls the special handling or introduce two QCalls with similar implementation.

@jkotasjkotasJun 29, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you plan to do something about this one? (Unifying with RuntimeTypeHandle::CanCastTo should be separate PR, fixing CanCastTo_NoCacheLookup to avoid unnecessary cache lookup should be in this one.)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, together with some other cases sharable between RuntimeTypeHandle and MethodTable, like type equivalence.

@huoyaoyuan

huoyaoyuan commented Jun 28, 2024

Copy link
Copy Markdown
MemberAuthor

Could you please take a look at the Mono test failures?

Yes, I expect the same pointer case should also be disabled for mono.
Well it may be because mono doesn't support compatible pointer case. I don't think enabling it on mono is worthy.

}

[Fact]
[SkipOnMono("Mono does not support pointer compatibility in Array.Copy")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please open an issue on this and disable the test against this issue?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it something we'd ever want to support on mono?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not want to have behavior differences between different runtime. Every behavior difference between runtime is a bug. We may choose to not fix some of these bugs, but I do not see a good reason for it here.

For example, similar Mono-specific issue in casting logic was fixed just a few days ago: #103841

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the behavior is different among all three runtimes. NativeAOT allows conversion between any pointers.

The current coreclr behavior is really complex to support. Can we make a breaking change instead to support only exactly same pointers?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current coreclr behavior is really complex to support.

Why is it complex to support?

Can we make a breaking change instead to support only exactly same pointers?

I do not see how we would justify this breaking change.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #104197 for mono.

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Anything remaining for this?

@jkotas

Copy link
Copy Markdown
Member

Cleaning up the cache lookups #104103 (comment) ?

Comment threadsrc/coreclr/vm/comutilnative.cpp Outdated
return bResult;
}

extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
extern"C"BOOLQCALLTYPETypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)
extern"C"BOOLQCALLTYPETypeHandle_CanCastToNoCacheLookup(void* fromTypeHnd, void* toTypeHnd)

I think this would be a better name for the QCall to make it clear what it does. Matches convention used for cast helpers (ChkCastAny_NoCacheLookup, etc.)

public bool CanCastTo(TypeHandle destTH)
{
if (m_asTAddr == destTH.m_asTAddr)
return true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering whether this logic should live in CastHelpers.cs next to all other casting logic, so that it is not missed if there are any bug fixes.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They look quite inconsistent...Methods in CastHelpers are HCalls in jithelpers.
BTW does it make sense to convert such methods to QCall, and move out of jithelpers since they are not directly used by JIT?

@jkotasjkotasJul 7, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to convert such methods to QCall

Yes. We want to get rid of all FCalls with HELPER_METHOD_FRAME.

not directly used by JIT

Those are slow path for helpers used by the JIT. Fast paths of those helpers are either in assembly code or in C#.

The split between JIT helpers and other helpers is blurry. It is not unusual for the two to have overlapping logic. We even have methods that are used as JIT helpers, but they are used for other purposes as well. I do not have strong opinions about the best source file split. Anything we come up with will have some downsides.

This can be worked on in a follow up.

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@jkotas

Copy link
Copy Markdown
Member

/ba-g All failures have known issues opened for them. I am not able to tell why BA is not able to match them

@jkotas
jkotas merged commit 7b71281 into dotnet:mainJul 7, 2024
@huoyaoyuan
huoyaoyuan deleted the array-assign-type branch July 8, 2024 01:51
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 7, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@huoyaoyuan@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Convert Array.IsSimpleCopy and CanAssignArray type to managed - #104103

Merged
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type
Jul 7, 2024
Merged

Convert Array.IsSimpleCopy and CanAssignArray type to managed#104103
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Move the two routines back again.

Removes a HELPER_METHOD_FRAME and improves performance for deciding the path.

Benchmark code:

Details
privateobject[]objArray_Int={123};privateobject[]objArray_Str={"abc"};privateint[]intArray={123};privateuint[]uintArray={123};privatebyte[]byteArray={1};privatestring[]strArray={"abc"};privateint*[]intPointerArray={(int*)0x1234};privateuint*[]uintPointerArray={(uint*)0x1234};[Benchmark]publicvoidSimpleCopy_Derived()=>Array.Copy(strArray,objArray_Str,1);[Benchmark]publicvoidMustCast()=>Array.Copy(objArray_Str,strArray,1);[Benchmark]publicvoidSimpleCopy_Primitive()=>Array.Copy(intArray,uintArray,1);[Benchmark]publicvoidPrimitiveWiden()=>Array.Copy(byteArray,intArray,1);[Benchmark]publicvoidSimpleCopy_Pointer()=>Array.Copy(intPointerArray,uintPointerArray,1);

Result:

MethodJobToolchainMeanErrorStdDevRatio
SimpleCopy_DerivedJob-RIAPVG\PR\corerun.exe10.092 ns0.0322 ns0.0269 ns0.87
SimpleCopy_DerivedJob-PSZNKI\main\corerun.exe11.586 ns0.1421 ns0.1329 ns1.00
MustCastJob-RIAPVG\PR\corerun.exe10.382 ns0.0319 ns0.0282 ns0.43
MustCastJob-PSZNKI\main\corerun.exe23.951 ns0.1351 ns0.1198 ns1.00
SimpleCopy_PrimitiveJob-RIAPVG\PR\corerun.exe7.974 ns0.0679 ns0.0635 ns0.69
SimpleCopy_PrimitiveJob-PSZNKI\main\corerun.exe11.608 ns0.0954 ns0.0893 ns1.00
PrimitiveWidenJob-RIAPVG\PR\corerun.exe12.496 ns0.0779 ns0.0691 ns0.44
PrimitiveWidenJob-PSZNKI\main\corerun.exe28.334 ns0.5434 ns0.5083 ns1.00
SimpleCopy_PointerJob-RIAPVG\PR\corerun.exe8.105 ns0.1383 ns0.1226 ns0.80
SimpleCopy_PointerJob-PSZNKI\main\corerun.exe10.155 ns0.0799 ns0.0747 ns1.00

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jun 27, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 27, 2024
@huoyaoyuanhuoyaoyuan added area-VM-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jun 27, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Comment on lines +220 to +226
{
// Only pointers are valid for TypeDesc in array element

// Compatible pointers
if (srcTH.CanCastTo(destTH))
return ArrayAssignType.SimpleCopy;
}

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any pointer type goes through the non-simple copy paths, it will result in the same fatal crash.

@jkotas

Copy link
Copy Markdown
Member

Could you please take a look at the Mono test failures?

if (result != CastResult.MaybeCast)
return result == CastResult.CanCast;

return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The first thing that the QCall is going to do is repeat the cache lookup...

@jkotasjkotasJun 28, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the unmanaged TypeHandle.CanCastTo assumes that some cases are very cheap to check for and it does not bother to add them to the cache. These cases will be much slower here since we are always going to take the QCall transition for them. We should either check for them here and/or add them to cache (similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons).

It probably does not matter for the one caller added in this PR, but it may matter for future callers.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons

The methods should probably be combined at managed side. They are doing the exact same things.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes (it is fine to do it in a follow up PR).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are doing the exact same things.

In fact they aren't. RuntimeTypeHandle::CanCastTo allows T -> Nullable<T>.

The non-cached cases include nullables, COM and I(Dynamic)Castable interfaces. I don't think specially handling them is worthy, even for future callers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you would either need to have a bool flag that controls the special handling or introduce two QCalls with similar implementation.

@jkotasjkotasJun 29, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you plan to do something about this one? (Unifying with RuntimeTypeHandle::CanCastTo should be separate PR, fixing CanCastTo_NoCacheLookup to avoid unnecessary cache lookup should be in this one.)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, together with some other cases sharable between RuntimeTypeHandle and MethodTable, like type equivalence.

@huoyaoyuan

huoyaoyuan commented Jun 28, 2024

Copy link
Copy Markdown
MemberAuthor

Could you please take a look at the Mono test failures?

Yes, I expect the same pointer case should also be disabled for mono.
Well it may be because mono doesn't support compatible pointer case. I don't think enabling it on mono is worthy.

}

[Fact]
[SkipOnMono("Mono does not support pointer compatibility in Array.Copy")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please open an issue on this and disable the test against this issue?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it something we'd ever want to support on mono?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not want to have behavior differences between different runtime. Every behavior difference between runtime is a bug. We may choose to not fix some of these bugs, but I do not see a good reason for it here.

For example, similar Mono-specific issue in casting logic was fixed just a few days ago: #103841

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the behavior is different among all three runtimes. NativeAOT allows conversion between any pointers.

The current coreclr behavior is really complex to support. Can we make a breaking change instead to support only exactly same pointers?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current coreclr behavior is really complex to support.

Why is it complex to support?

Can we make a breaking change instead to support only exactly same pointers?

I do not see how we would justify this breaking change.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #104197 for mono.

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Anything remaining for this?

@jkotas

Copy link
Copy Markdown
Member

Cleaning up the cache lookups #104103 (comment) ?

Comment threadsrc/coreclr/vm/comutilnative.cpp Outdated
return bResult;
}

extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
extern"C"BOOLQCALLTYPETypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)
extern"C"BOOLQCALLTYPETypeHandle_CanCastToNoCacheLookup(void* fromTypeHnd, void* toTypeHnd)

I think this would be a better name for the QCall to make it clear what it does. Matches convention used for cast helpers (ChkCastAny_NoCacheLookup, etc.)

public bool CanCastTo(TypeHandle destTH)
{
if (m_asTAddr == destTH.m_asTAddr)
return true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering whether this logic should live in CastHelpers.cs next to all other casting logic, so that it is not missed if there are any bug fixes.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They look quite inconsistent...Methods in CastHelpers are HCalls in jithelpers.
BTW does it make sense to convert such methods to QCall, and move out of jithelpers since they are not directly used by JIT?

@jkotasjkotasJul 7, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to convert such methods to QCall

Yes. We want to get rid of all FCalls with HELPER_METHOD_FRAME.

not directly used by JIT

Those are slow path for helpers used by the JIT. Fast paths of those helpers are either in assembly code or in C#.

The split between JIT helpers and other helpers is blurry. It is not unusual for the two to have overlapping logic. We even have methods that are used as JIT helpers, but they are used for other purposes as well. I do not have strong opinions about the best source file split. Anything we come up with will have some downsides.

This can be worked on in a follow up.

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@jkotas

Copy link
Copy Markdown
Member

/ba-g All failures have known issues opened for them. I am not able to tell why BA is not able to match them

@jkotas
jkotas merged commit 7b71281 into dotnet:mainJul 7, 2024
@huoyaoyuan
huoyaoyuan deleted the array-assign-type branch July 8, 2024 01:51
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 7, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@huoyaoyuan@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Convert Array.IsSimpleCopy and CanAssignArray type to managed - #104103

Merged
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type
Jul 7, 2024
Merged

Convert Array.IsSimpleCopy and CanAssignArray type to managed#104103
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Move the two routines back again.

Removes a HELPER_METHOD_FRAME and improves performance for deciding the path.

Benchmark code:

Details
privateobject[]objArray_Int={123};privateobject[]objArray_Str={"abc"};privateint[]intArray={123};privateuint[]uintArray={123};privatebyte[]byteArray={1};privatestring[]strArray={"abc"};privateint*[]intPointerArray={(int*)0x1234};privateuint*[]uintPointerArray={(uint*)0x1234};[Benchmark]publicvoidSimpleCopy_Derived()=>Array.Copy(strArray,objArray_Str,1);[Benchmark]publicvoidMustCast()=>Array.Copy(objArray_Str,strArray,1);[Benchmark]publicvoidSimpleCopy_Primitive()=>Array.Copy(intArray,uintArray,1);[Benchmark]publicvoidPrimitiveWiden()=>Array.Copy(byteArray,intArray,1);[Benchmark]publicvoidSimpleCopy_Pointer()=>Array.Copy(intPointerArray,uintPointerArray,1);

Result:

MethodJobToolchainMeanErrorStdDevRatio
SimpleCopy_DerivedJob-RIAPVG\PR\corerun.exe10.092 ns0.0322 ns0.0269 ns0.87
SimpleCopy_DerivedJob-PSZNKI\main\corerun.exe11.586 ns0.1421 ns0.1329 ns1.00
MustCastJob-RIAPVG\PR\corerun.exe10.382 ns0.0319 ns0.0282 ns0.43
MustCastJob-PSZNKI\main\corerun.exe23.951 ns0.1351 ns0.1198 ns1.00
SimpleCopy_PrimitiveJob-RIAPVG\PR\corerun.exe7.974 ns0.0679 ns0.0635 ns0.69
SimpleCopy_PrimitiveJob-PSZNKI\main\corerun.exe11.608 ns0.0954 ns0.0893 ns1.00
PrimitiveWidenJob-RIAPVG\PR\corerun.exe12.496 ns0.0779 ns0.0691 ns0.44
PrimitiveWidenJob-PSZNKI\main\corerun.exe28.334 ns0.5434 ns0.5083 ns1.00
SimpleCopy_PointerJob-RIAPVG\PR\corerun.exe8.105 ns0.1383 ns0.1226 ns0.80
SimpleCopy_PointerJob-PSZNKI\main\corerun.exe10.155 ns0.0799 ns0.0747 ns1.00

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jun 27, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 27, 2024
@huoyaoyuanhuoyaoyuan added area-VM-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jun 27, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Comment on lines +220 to +226
{
// Only pointers are valid for TypeDesc in array element

// Compatible pointers
if (srcTH.CanCastTo(destTH))
return ArrayAssignType.SimpleCopy;
}

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any pointer type goes through the non-simple copy paths, it will result in the same fatal crash.

@jkotas

Copy link
Copy Markdown
Member

Could you please take a look at the Mono test failures?

if (result != CastResult.MaybeCast)
return result == CastResult.CanCast;

return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The first thing that the QCall is going to do is repeat the cache lookup...

@jkotasjkotasJun 28, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the unmanaged TypeHandle.CanCastTo assumes that some cases are very cheap to check for and it does not bother to add them to the cache. These cases will be much slower here since we are always going to take the QCall transition for them. We should either check for them here and/or add them to cache (similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons).

It probably does not matter for the one caller added in this PR, but it may matter for future callers.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons

The methods should probably be combined at managed side. They are doing the exact same things.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes (it is fine to do it in a follow up PR).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are doing the exact same things.

In fact they aren't. RuntimeTypeHandle::CanCastTo allows T -> Nullable<T>.

The non-cached cases include nullables, COM and I(Dynamic)Castable interfaces. I don't think specially handling them is worthy, even for future callers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you would either need to have a bool flag that controls the special handling or introduce two QCalls with similar implementation.

@jkotasjkotasJun 29, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you plan to do something about this one? (Unifying with RuntimeTypeHandle::CanCastTo should be separate PR, fixing CanCastTo_NoCacheLookup to avoid unnecessary cache lookup should be in this one.)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, together with some other cases sharable between RuntimeTypeHandle and MethodTable, like type equivalence.

@huoyaoyuan

huoyaoyuan commented Jun 28, 2024

Copy link
Copy Markdown
MemberAuthor

Could you please take a look at the Mono test failures?

Yes, I expect the same pointer case should also be disabled for mono.
Well it may be because mono doesn't support compatible pointer case. I don't think enabling it on mono is worthy.

}

[Fact]
[SkipOnMono("Mono does not support pointer compatibility in Array.Copy")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please open an issue on this and disable the test against this issue?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it something we'd ever want to support on mono?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not want to have behavior differences between different runtime. Every behavior difference between runtime is a bug. We may choose to not fix some of these bugs, but I do not see a good reason for it here.

For example, similar Mono-specific issue in casting logic was fixed just a few days ago: #103841

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the behavior is different among all three runtimes. NativeAOT allows conversion between any pointers.

The current coreclr behavior is really complex to support. Can we make a breaking change instead to support only exactly same pointers?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current coreclr behavior is really complex to support.

Why is it complex to support?

Can we make a breaking change instead to support only exactly same pointers?

I do not see how we would justify this breaking change.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #104197 for mono.

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Anything remaining for this?

@jkotas

Copy link
Copy Markdown
Member

Cleaning up the cache lookups #104103 (comment) ?

Comment threadsrc/coreclr/vm/comutilnative.cpp Outdated
return bResult;
}

extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
extern"C"BOOLQCALLTYPETypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)
extern"C"BOOLQCALLTYPETypeHandle_CanCastToNoCacheLookup(void* fromTypeHnd, void* toTypeHnd)

I think this would be a better name for the QCall to make it clear what it does. Matches convention used for cast helpers (ChkCastAny_NoCacheLookup, etc.)

public bool CanCastTo(TypeHandle destTH)
{
if (m_asTAddr == destTH.m_asTAddr)
return true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering whether this logic should live in CastHelpers.cs next to all other casting logic, so that it is not missed if there are any bug fixes.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They look quite inconsistent...Methods in CastHelpers are HCalls in jithelpers.
BTW does it make sense to convert such methods to QCall, and move out of jithelpers since they are not directly used by JIT?

@jkotasjkotasJul 7, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to convert such methods to QCall

Yes. We want to get rid of all FCalls with HELPER_METHOD_FRAME.

not directly used by JIT

Those are slow path for helpers used by the JIT. Fast paths of those helpers are either in assembly code or in C#.

The split between JIT helpers and other helpers is blurry. It is not unusual for the two to have overlapping logic. We even have methods that are used as JIT helpers, but they are used for other purposes as well. I do not have strong opinions about the best source file split. Anything we come up with will have some downsides.

This can be worked on in a follow up.

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@jkotas

Copy link
Copy Markdown
Member

/ba-g All failures have known issues opened for them. I am not able to tell why BA is not able to match them

@jkotas
jkotas merged commit 7b71281 into dotnet:mainJul 7, 2024
@huoyaoyuan
huoyaoyuan deleted the array-assign-type branch July 8, 2024 01:51
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 7, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@huoyaoyuan@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Convert Array.IsSimpleCopy and CanAssignArray type to managed - #104103

Merged
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type
Jul 7, 2024
Merged

Convert Array.IsSimpleCopy and CanAssignArray type to managed#104103
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Move the two routines back again.

Removes a HELPER_METHOD_FRAME and improves performance for deciding the path.

Benchmark code:

Details
privateobject[]objArray_Int={123};privateobject[]objArray_Str={"abc"};privateint[]intArray={123};privateuint[]uintArray={123};privatebyte[]byteArray={1};privatestring[]strArray={"abc"};privateint*[]intPointerArray={(int*)0x1234};privateuint*[]uintPointerArray={(uint*)0x1234};[Benchmark]publicvoidSimpleCopy_Derived()=>Array.Copy(strArray,objArray_Str,1);[Benchmark]publicvoidMustCast()=>Array.Copy(objArray_Str,strArray,1);[Benchmark]publicvoidSimpleCopy_Primitive()=>Array.Copy(intArray,uintArray,1);[Benchmark]publicvoidPrimitiveWiden()=>Array.Copy(byteArray,intArray,1);[Benchmark]publicvoidSimpleCopy_Pointer()=>Array.Copy(intPointerArray,uintPointerArray,1);

Result:

MethodJobToolchainMeanErrorStdDevRatio
SimpleCopy_DerivedJob-RIAPVG\PR\corerun.exe10.092 ns0.0322 ns0.0269 ns0.87
SimpleCopy_DerivedJob-PSZNKI\main\corerun.exe11.586 ns0.1421 ns0.1329 ns1.00
MustCastJob-RIAPVG\PR\corerun.exe10.382 ns0.0319 ns0.0282 ns0.43
MustCastJob-PSZNKI\main\corerun.exe23.951 ns0.1351 ns0.1198 ns1.00
SimpleCopy_PrimitiveJob-RIAPVG\PR\corerun.exe7.974 ns0.0679 ns0.0635 ns0.69
SimpleCopy_PrimitiveJob-PSZNKI\main\corerun.exe11.608 ns0.0954 ns0.0893 ns1.00
PrimitiveWidenJob-RIAPVG\PR\corerun.exe12.496 ns0.0779 ns0.0691 ns0.44
PrimitiveWidenJob-PSZNKI\main\corerun.exe28.334 ns0.5434 ns0.5083 ns1.00
SimpleCopy_PointerJob-RIAPVG\PR\corerun.exe8.105 ns0.1383 ns0.1226 ns0.80
SimpleCopy_PointerJob-PSZNKI\main\corerun.exe10.155 ns0.0799 ns0.0747 ns1.00

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jun 27, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 27, 2024
@huoyaoyuanhuoyaoyuan added area-VM-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jun 27, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Comment on lines +220 to +226
{
// Only pointers are valid for TypeDesc in array element

// Compatible pointers
if (srcTH.CanCastTo(destTH))
return ArrayAssignType.SimpleCopy;
}

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any pointer type goes through the non-simple copy paths, it will result in the same fatal crash.

@jkotas

Copy link
Copy Markdown
Member

Could you please take a look at the Mono test failures?

if (result != CastResult.MaybeCast)
return result == CastResult.CanCast;

return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The first thing that the QCall is going to do is repeat the cache lookup...

@jkotasjkotasJun 28, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the unmanaged TypeHandle.CanCastTo assumes that some cases are very cheap to check for and it does not bother to add them to the cache. These cases will be much slower here since we are always going to take the QCall transition for them. We should either check for them here and/or add them to cache (similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons).

It probably does not matter for the one caller added in this PR, but it may matter for future callers.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons

The methods should probably be combined at managed side. They are doing the exact same things.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes (it is fine to do it in a follow up PR).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are doing the exact same things.

In fact they aren't. RuntimeTypeHandle::CanCastTo allows T -> Nullable<T>.

The non-cached cases include nullables, COM and I(Dynamic)Castable interfaces. I don't think specially handling them is worthy, even for future callers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you would either need to have a bool flag that controls the special handling or introduce two QCalls with similar implementation.

@jkotasjkotasJun 29, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you plan to do something about this one? (Unifying with RuntimeTypeHandle::CanCastTo should be separate PR, fixing CanCastTo_NoCacheLookup to avoid unnecessary cache lookup should be in this one.)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, together with some other cases sharable between RuntimeTypeHandle and MethodTable, like type equivalence.

@huoyaoyuan

huoyaoyuan commented Jun 28, 2024

Copy link
Copy Markdown
MemberAuthor

Could you please take a look at the Mono test failures?

Yes, I expect the same pointer case should also be disabled for mono.
Well it may be because mono doesn't support compatible pointer case. I don't think enabling it on mono is worthy.

}

[Fact]
[SkipOnMono("Mono does not support pointer compatibility in Array.Copy")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please open an issue on this and disable the test against this issue?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it something we'd ever want to support on mono?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not want to have behavior differences between different runtime. Every behavior difference between runtime is a bug. We may choose to not fix some of these bugs, but I do not see a good reason for it here.

For example, similar Mono-specific issue in casting logic was fixed just a few days ago: #103841

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the behavior is different among all three runtimes. NativeAOT allows conversion between any pointers.

The current coreclr behavior is really complex to support. Can we make a breaking change instead to support only exactly same pointers?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current coreclr behavior is really complex to support.

Why is it complex to support?

Can we make a breaking change instead to support only exactly same pointers?

I do not see how we would justify this breaking change.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #104197 for mono.

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Anything remaining for this?

@jkotas

Copy link
Copy Markdown
Member

Cleaning up the cache lookups #104103 (comment) ?

Comment threadsrc/coreclr/vm/comutilnative.cpp Outdated
return bResult;
}

extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
extern"C"BOOLQCALLTYPETypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)
extern"C"BOOLQCALLTYPETypeHandle_CanCastToNoCacheLookup(void* fromTypeHnd, void* toTypeHnd)

I think this would be a better name for the QCall to make it clear what it does. Matches convention used for cast helpers (ChkCastAny_NoCacheLookup, etc.)

public bool CanCastTo(TypeHandle destTH)
{
if (m_asTAddr == destTH.m_asTAddr)
return true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering whether this logic should live in CastHelpers.cs next to all other casting logic, so that it is not missed if there are any bug fixes.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They look quite inconsistent...Methods in CastHelpers are HCalls in jithelpers.
BTW does it make sense to convert such methods to QCall, and move out of jithelpers since they are not directly used by JIT?

@jkotasjkotasJul 7, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to convert such methods to QCall

Yes. We want to get rid of all FCalls with HELPER_METHOD_FRAME.

not directly used by JIT

Those are slow path for helpers used by the JIT. Fast paths of those helpers are either in assembly code or in C#.

The split between JIT helpers and other helpers is blurry. It is not unusual for the two to have overlapping logic. We even have methods that are used as JIT helpers, but they are used for other purposes as well. I do not have strong opinions about the best source file split. Anything we come up with will have some downsides.

This can be worked on in a follow up.

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@jkotas

Copy link
Copy Markdown
Member

/ba-g All failures have known issues opened for them. I am not able to tell why BA is not able to match them

@jkotas
jkotas merged commit 7b71281 into dotnet:mainJul 7, 2024
@huoyaoyuan
huoyaoyuan deleted the array-assign-type branch July 8, 2024 01:51
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 7, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@huoyaoyuan@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Convert Array.IsSimpleCopy and CanAssignArray type to managed - #104103

Merged
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type
Jul 7, 2024
Merged

Convert Array.IsSimpleCopy and CanAssignArray type to managed#104103
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Move the two routines back again.

Removes a HELPER_METHOD_FRAME and improves performance for deciding the path.

Benchmark code:

Details
privateobject[]objArray_Int={123};privateobject[]objArray_Str={"abc"};privateint[]intArray={123};privateuint[]uintArray={123};privatebyte[]byteArray={1};privatestring[]strArray={"abc"};privateint*[]intPointerArray={(int*)0x1234};privateuint*[]uintPointerArray={(uint*)0x1234};[Benchmark]publicvoidSimpleCopy_Derived()=>Array.Copy(strArray,objArray_Str,1);[Benchmark]publicvoidMustCast()=>Array.Copy(objArray_Str,strArray,1);[Benchmark]publicvoidSimpleCopy_Primitive()=>Array.Copy(intArray,uintArray,1);[Benchmark]publicvoidPrimitiveWiden()=>Array.Copy(byteArray,intArray,1);[Benchmark]publicvoidSimpleCopy_Pointer()=>Array.Copy(intPointerArray,uintPointerArray,1);

Result:

MethodJobToolchainMeanErrorStdDevRatio
SimpleCopy_DerivedJob-RIAPVG\PR\corerun.exe10.092 ns0.0322 ns0.0269 ns0.87
SimpleCopy_DerivedJob-PSZNKI\main\corerun.exe11.586 ns0.1421 ns0.1329 ns1.00
MustCastJob-RIAPVG\PR\corerun.exe10.382 ns0.0319 ns0.0282 ns0.43
MustCastJob-PSZNKI\main\corerun.exe23.951 ns0.1351 ns0.1198 ns1.00
SimpleCopy_PrimitiveJob-RIAPVG\PR\corerun.exe7.974 ns0.0679 ns0.0635 ns0.69
SimpleCopy_PrimitiveJob-PSZNKI\main\corerun.exe11.608 ns0.0954 ns0.0893 ns1.00
PrimitiveWidenJob-RIAPVG\PR\corerun.exe12.496 ns0.0779 ns0.0691 ns0.44
PrimitiveWidenJob-PSZNKI\main\corerun.exe28.334 ns0.5434 ns0.5083 ns1.00
SimpleCopy_PointerJob-RIAPVG\PR\corerun.exe8.105 ns0.1383 ns0.1226 ns0.80
SimpleCopy_PointerJob-PSZNKI\main\corerun.exe10.155 ns0.0799 ns0.0747 ns1.00

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jun 27, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 27, 2024
@huoyaoyuanhuoyaoyuan added area-VM-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jun 27, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Comment on lines +220 to +226
{
// Only pointers are valid for TypeDesc in array element

// Compatible pointers
if (srcTH.CanCastTo(destTH))
return ArrayAssignType.SimpleCopy;
}

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any pointer type goes through the non-simple copy paths, it will result in the same fatal crash.

@jkotas

Copy link
Copy Markdown
Member

Could you please take a look at the Mono test failures?

if (result != CastResult.MaybeCast)
return result == CastResult.CanCast;

return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The first thing that the QCall is going to do is repeat the cache lookup...

@jkotasjkotasJun 28, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the unmanaged TypeHandle.CanCastTo assumes that some cases are very cheap to check for and it does not bother to add them to the cache. These cases will be much slower here since we are always going to take the QCall transition for them. We should either check for them here and/or add them to cache (similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons).

It probably does not matter for the one caller added in this PR, but it may matter for future callers.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons

The methods should probably be combined at managed side. They are doing the exact same things.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes (it is fine to do it in a follow up PR).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are doing the exact same things.

In fact they aren't. RuntimeTypeHandle::CanCastTo allows T -> Nullable<T>.

The non-cached cases include nullables, COM and I(Dynamic)Castable interfaces. I don't think specially handling them is worthy, even for future callers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you would either need to have a bool flag that controls the special handling or introduce two QCalls with similar implementation.

@jkotasjkotasJun 29, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you plan to do something about this one? (Unifying with RuntimeTypeHandle::CanCastTo should be separate PR, fixing CanCastTo_NoCacheLookup to avoid unnecessary cache lookup should be in this one.)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, together with some other cases sharable between RuntimeTypeHandle and MethodTable, like type equivalence.

@huoyaoyuan

huoyaoyuan commented Jun 28, 2024

Copy link
Copy Markdown
MemberAuthor

Could you please take a look at the Mono test failures?

Yes, I expect the same pointer case should also be disabled for mono.
Well it may be because mono doesn't support compatible pointer case. I don't think enabling it on mono is worthy.

}

[Fact]
[SkipOnMono("Mono does not support pointer compatibility in Array.Copy")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please open an issue on this and disable the test against this issue?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it something we'd ever want to support on mono?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not want to have behavior differences between different runtime. Every behavior difference between runtime is a bug. We may choose to not fix some of these bugs, but I do not see a good reason for it here.

For example, similar Mono-specific issue in casting logic was fixed just a few days ago: #103841

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the behavior is different among all three runtimes. NativeAOT allows conversion between any pointers.

The current coreclr behavior is really complex to support. Can we make a breaking change instead to support only exactly same pointers?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current coreclr behavior is really complex to support.

Why is it complex to support?

Can we make a breaking change instead to support only exactly same pointers?

I do not see how we would justify this breaking change.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #104197 for mono.

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Anything remaining for this?

@jkotas

Copy link
Copy Markdown
Member

Cleaning up the cache lookups #104103 (comment) ?

Comment threadsrc/coreclr/vm/comutilnative.cpp Outdated
return bResult;
}

extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
extern"C"BOOLQCALLTYPETypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)
extern"C"BOOLQCALLTYPETypeHandle_CanCastToNoCacheLookup(void* fromTypeHnd, void* toTypeHnd)

I think this would be a better name for the QCall to make it clear what it does. Matches convention used for cast helpers (ChkCastAny_NoCacheLookup, etc.)

public bool CanCastTo(TypeHandle destTH)
{
if (m_asTAddr == destTH.m_asTAddr)
return true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering whether this logic should live in CastHelpers.cs next to all other casting logic, so that it is not missed if there are any bug fixes.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They look quite inconsistent...Methods in CastHelpers are HCalls in jithelpers.
BTW does it make sense to convert such methods to QCall, and move out of jithelpers since they are not directly used by JIT?

@jkotasjkotasJul 7, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to convert such methods to QCall

Yes. We want to get rid of all FCalls with HELPER_METHOD_FRAME.

not directly used by JIT

Those are slow path for helpers used by the JIT. Fast paths of those helpers are either in assembly code or in C#.

The split between JIT helpers and other helpers is blurry. It is not unusual for the two to have overlapping logic. We even have methods that are used as JIT helpers, but they are used for other purposes as well. I do not have strong opinions about the best source file split. Anything we come up with will have some downsides.

This can be worked on in a follow up.

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@jkotas

Copy link
Copy Markdown
Member

/ba-g All failures have known issues opened for them. I am not able to tell why BA is not able to match them

@jkotas
jkotas merged commit 7b71281 into dotnet:mainJul 7, 2024
@huoyaoyuan
huoyaoyuan deleted the array-assign-type branch July 8, 2024 01:51
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 7, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@huoyaoyuan@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Convert Array.IsSimpleCopy and CanAssignArray type to managed - #104103

Merged
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type
Jul 7, 2024
Merged

Convert Array.IsSimpleCopy and CanAssignArray type to managed#104103
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Move the two routines back again.

Removes a HELPER_METHOD_FRAME and improves performance for deciding the path.

Benchmark code:

Details
privateobject[]objArray_Int={123};privateobject[]objArray_Str={"abc"};privateint[]intArray={123};privateuint[]uintArray={123};privatebyte[]byteArray={1};privatestring[]strArray={"abc"};privateint*[]intPointerArray={(int*)0x1234};privateuint*[]uintPointerArray={(uint*)0x1234};[Benchmark]publicvoidSimpleCopy_Derived()=>Array.Copy(strArray,objArray_Str,1);[Benchmark]publicvoidMustCast()=>Array.Copy(objArray_Str,strArray,1);[Benchmark]publicvoidSimpleCopy_Primitive()=>Array.Copy(intArray,uintArray,1);[Benchmark]publicvoidPrimitiveWiden()=>Array.Copy(byteArray,intArray,1);[Benchmark]publicvoidSimpleCopy_Pointer()=>Array.Copy(intPointerArray,uintPointerArray,1);

Result:

MethodJobToolchainMeanErrorStdDevRatio
SimpleCopy_DerivedJob-RIAPVG\PR\corerun.exe10.092 ns0.0322 ns0.0269 ns0.87
SimpleCopy_DerivedJob-PSZNKI\main\corerun.exe11.586 ns0.1421 ns0.1329 ns1.00
MustCastJob-RIAPVG\PR\corerun.exe10.382 ns0.0319 ns0.0282 ns0.43
MustCastJob-PSZNKI\main\corerun.exe23.951 ns0.1351 ns0.1198 ns1.00
SimpleCopy_PrimitiveJob-RIAPVG\PR\corerun.exe7.974 ns0.0679 ns0.0635 ns0.69
SimpleCopy_PrimitiveJob-PSZNKI\main\corerun.exe11.608 ns0.0954 ns0.0893 ns1.00
PrimitiveWidenJob-RIAPVG\PR\corerun.exe12.496 ns0.0779 ns0.0691 ns0.44
PrimitiveWidenJob-PSZNKI\main\corerun.exe28.334 ns0.5434 ns0.5083 ns1.00
SimpleCopy_PointerJob-RIAPVG\PR\corerun.exe8.105 ns0.1383 ns0.1226 ns0.80
SimpleCopy_PointerJob-PSZNKI\main\corerun.exe10.155 ns0.0799 ns0.0747 ns1.00

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jun 27, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 27, 2024
@huoyaoyuanhuoyaoyuan added area-VM-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jun 27, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Comment on lines +220 to +226
{
// Only pointers are valid for TypeDesc in array element

// Compatible pointers
if (srcTH.CanCastTo(destTH))
return ArrayAssignType.SimpleCopy;
}

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any pointer type goes through the non-simple copy paths, it will result in the same fatal crash.

@jkotas

Copy link
Copy Markdown
Member

Could you please take a look at the Mono test failures?

if (result != CastResult.MaybeCast)
return result == CastResult.CanCast;

return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The first thing that the QCall is going to do is repeat the cache lookup...

@jkotasjkotasJun 28, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the unmanaged TypeHandle.CanCastTo assumes that some cases are very cheap to check for and it does not bother to add them to the cache. These cases will be much slower here since we are always going to take the QCall transition for them. We should either check for them here and/or add them to cache (similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons).

It probably does not matter for the one caller added in this PR, but it may matter for future callers.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons

The methods should probably be combined at managed side. They are doing the exact same things.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes (it is fine to do it in a follow up PR).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are doing the exact same things.

In fact they aren't. RuntimeTypeHandle::CanCastTo allows T -> Nullable<T>.

The non-cached cases include nullables, COM and I(Dynamic)Castable interfaces. I don't think specially handling them is worthy, even for future callers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you would either need to have a bool flag that controls the special handling or introduce two QCalls with similar implementation.

@jkotasjkotasJun 29, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you plan to do something about this one? (Unifying with RuntimeTypeHandle::CanCastTo should be separate PR, fixing CanCastTo_NoCacheLookup to avoid unnecessary cache lookup should be in this one.)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, together with some other cases sharable between RuntimeTypeHandle and MethodTable, like type equivalence.

@huoyaoyuan

huoyaoyuan commented Jun 28, 2024

Copy link
Copy Markdown
MemberAuthor

Could you please take a look at the Mono test failures?

Yes, I expect the same pointer case should also be disabled for mono.
Well it may be because mono doesn't support compatible pointer case. I don't think enabling it on mono is worthy.

}

[Fact]
[SkipOnMono("Mono does not support pointer compatibility in Array.Copy")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please open an issue on this and disable the test against this issue?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it something we'd ever want to support on mono?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not want to have behavior differences between different runtime. Every behavior difference between runtime is a bug. We may choose to not fix some of these bugs, but I do not see a good reason for it here.

For example, similar Mono-specific issue in casting logic was fixed just a few days ago: #103841

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the behavior is different among all three runtimes. NativeAOT allows conversion between any pointers.

The current coreclr behavior is really complex to support. Can we make a breaking change instead to support only exactly same pointers?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current coreclr behavior is really complex to support.

Why is it complex to support?

Can we make a breaking change instead to support only exactly same pointers?

I do not see how we would justify this breaking change.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #104197 for mono.

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Anything remaining for this?

@jkotas

Copy link
Copy Markdown
Member

Cleaning up the cache lookups #104103 (comment) ?

Comment threadsrc/coreclr/vm/comutilnative.cpp Outdated
return bResult;
}

extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
extern"C"BOOLQCALLTYPETypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)
extern"C"BOOLQCALLTYPETypeHandle_CanCastToNoCacheLookup(void* fromTypeHnd, void* toTypeHnd)

I think this would be a better name for the QCall to make it clear what it does. Matches convention used for cast helpers (ChkCastAny_NoCacheLookup, etc.)

public bool CanCastTo(TypeHandle destTH)
{
if (m_asTAddr == destTH.m_asTAddr)
return true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering whether this logic should live in CastHelpers.cs next to all other casting logic, so that it is not missed if there are any bug fixes.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They look quite inconsistent...Methods in CastHelpers are HCalls in jithelpers.
BTW does it make sense to convert such methods to QCall, and move out of jithelpers since they are not directly used by JIT?

@jkotasjkotasJul 7, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to convert such methods to QCall

Yes. We want to get rid of all FCalls with HELPER_METHOD_FRAME.

not directly used by JIT

Those are slow path for helpers used by the JIT. Fast paths of those helpers are either in assembly code or in C#.

The split between JIT helpers and other helpers is blurry. It is not unusual for the two to have overlapping logic. We even have methods that are used as JIT helpers, but they are used for other purposes as well. I do not have strong opinions about the best source file split. Anything we come up with will have some downsides.

This can be worked on in a follow up.

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@jkotas

Copy link
Copy Markdown
Member

/ba-g All failures have known issues opened for them. I am not able to tell why BA is not able to match them

@jkotas
jkotas merged commit 7b71281 into dotnet:mainJul 7, 2024
@huoyaoyuan
huoyaoyuan deleted the array-assign-type branch July 8, 2024 01:51
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 7, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@huoyaoyuan@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Convert Array.IsSimpleCopy and CanAssignArray type to managed - #104103

Merged
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type
Jul 7, 2024
Merged

Convert Array.IsSimpleCopy and CanAssignArray type to managed#104103
jkotas merged 15 commits into
dotnet:mainfrom
huoyaoyuan:array-assign-type

Conversation

@huoyaoyuan

Copy link
Copy Markdown
Member

Move the two routines back again.

Removes a HELPER_METHOD_FRAME and improves performance for deciding the path.

Benchmark code:

Details
privateobject[]objArray_Int={123};privateobject[]objArray_Str={"abc"};privateint[]intArray={123};privateuint[]uintArray={123};privatebyte[]byteArray={1};privatestring[]strArray={"abc"};privateint*[]intPointerArray={(int*)0x1234};privateuint*[]uintPointerArray={(uint*)0x1234};[Benchmark]publicvoidSimpleCopy_Derived()=>Array.Copy(strArray,objArray_Str,1);[Benchmark]publicvoidMustCast()=>Array.Copy(objArray_Str,strArray,1);[Benchmark]publicvoidSimpleCopy_Primitive()=>Array.Copy(intArray,uintArray,1);[Benchmark]publicvoidPrimitiveWiden()=>Array.Copy(byteArray,intArray,1);[Benchmark]publicvoidSimpleCopy_Pointer()=>Array.Copy(intPointerArray,uintPointerArray,1);

Result:

MethodJobToolchainMeanErrorStdDevRatio
SimpleCopy_DerivedJob-RIAPVG\PR\corerun.exe10.092 ns0.0322 ns0.0269 ns0.87
SimpleCopy_DerivedJob-PSZNKI\main\corerun.exe11.586 ns0.1421 ns0.1329 ns1.00
MustCastJob-RIAPVG\PR\corerun.exe10.382 ns0.0319 ns0.0282 ns0.43
MustCastJob-PSZNKI\main\corerun.exe23.951 ns0.1351 ns0.1198 ns1.00
SimpleCopy_PrimitiveJob-RIAPVG\PR\corerun.exe7.974 ns0.0679 ns0.0635 ns0.69
SimpleCopy_PrimitiveJob-PSZNKI\main\corerun.exe11.608 ns0.0954 ns0.0893 ns1.00
PrimitiveWidenJob-RIAPVG\PR\corerun.exe12.496 ns0.0779 ns0.0691 ns0.44
PrimitiveWidenJob-PSZNKI\main\corerun.exe28.334 ns0.5434 ns0.5083 ns1.00
SimpleCopy_PointerJob-RIAPVG\PR\corerun.exe8.105 ns0.1383 ns0.1226 ns0.80
SimpleCopy_PointerJob-PSZNKI\main\corerun.exe10.155 ns0.0799 ns0.0747 ns1.00

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jun 27, 2024
@dotnet-policy-servicedotnet-policy-serviceBot added the community-contribution Indicates that the PR has been added by a community member label Jun 27, 2024
@huoyaoyuanhuoyaoyuan added area-VM-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jun 27, 2024
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

Comment on lines +220 to +226
{
// Only pointers are valid for TypeDesc in array element

// Compatible pointers
if (srcTH.CanCastTo(destTH))
return ArrayAssignType.SimpleCopy;
}

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any pointer type goes through the non-simple copy paths, it will result in the same fatal crash.

@jkotas

Copy link
Copy Markdown
Member

Could you please take a look at the Mono test failures?

if (result != CastResult.MaybeCast)
return result == CastResult.CanCast;

return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The first thing that the QCall is going to do is repeat the cache lookup...

@jkotasjkotasJun 28, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the unmanaged TypeHandle.CanCastTo assumes that some cases are very cheap to check for and it does not bother to add them to the cache. These cases will be much slower here since we are always going to take the QCall transition for them. We should either check for them here and/or add them to cache (similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons).

It probably does not matter for the one caller added in this PR, but it may matter for future callers.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to how RuntimeTypeHandle::CanCastTo adds them to the cache for the same reasons

The methods should probably be combined at managed side. They are doing the exact same things.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes (it is fine to do it in a follow up PR).

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are doing the exact same things.

In fact they aren't. RuntimeTypeHandle::CanCastTo allows T -> Nullable<T>.

The non-cached cases include nullables, COM and I(Dynamic)Castable interfaces. I don't think specially handling them is worthy, even for future callers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you would either need to have a bool flag that controls the special handling or introduce two QCalls with similar implementation.

@jkotasjkotasJun 29, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you plan to do something about this one? (Unifying with RuntimeTypeHandle::CanCastTo should be separate PR, fixing CanCastTo_NoCacheLookup to avoid unnecessary cache lookup should be in this one.)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, together with some other cases sharable between RuntimeTypeHandle and MethodTable, like type equivalence.

@huoyaoyuan

huoyaoyuan commented Jun 28, 2024

Copy link
Copy Markdown
MemberAuthor

Could you please take a look at the Mono test failures?

Yes, I expect the same pointer case should also be disabled for mono.
Well it may be because mono doesn't support compatible pointer case. I don't think enabling it on mono is worthy.

}

[Fact]
[SkipOnMono("Mono does not support pointer compatibility in Array.Copy")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please open an issue on this and disable the test against this issue?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it something we'd ever want to support on mono?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not want to have behavior differences between different runtime. Every behavior difference between runtime is a bug. We may choose to not fix some of these bugs, but I do not see a good reason for it here.

For example, similar Mono-specific issue in casting logic was fixed just a few days ago: #103841

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the behavior is different among all three runtimes. NativeAOT allows conversion between any pointers.

The current coreclr behavior is really complex to support. Can we make a breaking change instead to support only exactly same pointers?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current coreclr behavior is really complex to support.

Why is it complex to support?

Can we make a breaking change instead to support only exactly same pointers?

I do not see how we would justify this breaking change.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #104197 for mono.

@huoyaoyuan

Copy link
Copy Markdown
MemberAuthor

Anything remaining for this?

@jkotas

Copy link
Copy Markdown
Member

Cleaning up the cache lookups #104103 (comment) ?

Comment threadsrc/coreclr/vm/comutilnative.cpp Outdated
return bResult;
}

extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
extern"C"BOOLQCALLTYPETypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd)
extern"C"BOOLQCALLTYPETypeHandle_CanCastToNoCacheLookup(void* fromTypeHnd, void* toTypeHnd)

I think this would be a better name for the QCall to make it clear what it does. Matches convention used for cast helpers (ChkCastAny_NoCacheLookup, etc.)

public bool CanCastTo(TypeHandle destTH)
{
if (m_asTAddr == destTH.m_asTAddr)
return true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering whether this logic should live in CastHelpers.cs next to all other casting logic, so that it is not missed if there are any bug fixes.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They look quite inconsistent...Methods in CastHelpers are HCalls in jithelpers.
BTW does it make sense to convert such methods to QCall, and move out of jithelpers since they are not directly used by JIT?

@jkotasjkotasJul 7, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to convert such methods to QCall

Yes. We want to get rid of all FCalls with HELPER_METHOD_FRAME.

not directly used by JIT

Those are slow path for helpers used by the JIT. Fast paths of those helpers are either in assembly code or in C#.

The split between JIT helpers and other helpers is blurry. It is not unusual for the two to have overlapping logic. We even have methods that are used as JIT helpers, but they are used for other purposes as well. I do not have strong opinions about the best source file split. Anything we come up with will have some downsides.

This can be worked on in a follow up.

@jkotasjkotas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@jkotas

Copy link
Copy Markdown
Member

/ba-g All failures have known issues opened for them. I am not able to tell why BA is not able to match them

@jkotas
jkotas merged commit 7b71281 into dotnet:mainJul 7, 2024
@huoyaoyuan
huoyaoyuan deleted the array-assign-type branch July 8, 2024 01:51
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 7, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-VM-coreclrcommunity-contributionIndicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@huoyaoyuan@jkotas