Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Import isinst/castclass Nullable as underlying type #87241

Merged
merged 13 commits into from Jun 21, 2023
17 changes: 17 additions & 0 deletions src/coreclr/jit/importer.cpp
Expand Up @@ -5426,6 +5426,23 @@ GenTree* Compiler::impCastClassOrIsInstToTree(
bool shouldExpandInline = true;
bool isClassExact = impIsClassExact(pResolvedToken->hClass);

// ECMA-335 III.4.3: If typeTok is a nullable type, Nullable<T>, it is interpreted as "boxed" T
// We can convert constant-ish tokens of nullable to its underlying type.
// However, when the type is shared generic parameter like Nullable<Struct<__Canon>>, the actual type will require
// runtime lookup. It's too complex to add another level of indirection in op2, fallback to the cast helper instead.
if (isClassExact && !(info.compCompHnd->getClassAttribs(pResolvedToken->hClass) & CORINFO_FLG_SHAREDINST))
{
CORINFO_CLASS_HANDLE hClass = info.compCompHnd->getTypeForBox(pResolvedToken->hClass);

if (hClass != pResolvedToken->hClass)
{
bool runtimeLookup;
pResolvedToken->hClass = hClass;
op2 = impTokenToHandle(pResolvedToken, &runtimeLookup);
assert(!runtimeLookup);
}
}

// Profitability check.
//
// Don't bother with inline expansion when jit is trying to generate code quickly
Expand Down
60 changes: 60 additions & 0 deletions src/tests/JIT/opt/Casts/castclass_valuetype.il
@@ -0,0 +1,60 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.


.assembly extern mscorlib { auto }
.assembly extern xunit.core {}
.assembly extern System.Console { auto }
.assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) }

.assembly 'castclass_valuetype' { }

.class public auto Program extends [System.Runtime]System.Object
{
.method public hidebysig static int32 Main() cil managed
{
.custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = (
Copy link
Member

Choose a reason for hiding this comment

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

I assume the [Fact] attribute is not needed here (or then .entrypoint is not needed if it's part of the merged runtime tests)?

I'm not asking to remove it, just curious

Copy link
Member

Choose a reason for hiding this comment

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

[Fact] is needed as JIT/opt is a merged test group. The .entrypoint will have no impact on merged test group execution (this test is a library that is referenced by the merged test group, so this .entrypoint isn't the overall entry).

We've been keeping the .entrypoint annotations in ilproj tests. I'm not entirely sure of the details, but my guess is that the basic wrappers (for BuildAsStandalone or RequiresProcessIsolation) have C#-specific aspects to them.

Copy link
Member

Choose a reason for hiding this comment

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

@markples I see, thanks, I thought that .entrypoint will confuse a merged test group

01 00 00 00
)
.entrypoint
.maxstack 8

.try
{
// castclass valuetype
ldc.i4 1234
box int32
castclass int32
unbox int32
call instance string [System.Runtime]System.Int32::ToString()
call void [System.Console]System.Console::WriteLine(class [System.Runtime]System.String)

// castclass nullable
ldc.i4 1234
box int32
castclass valuetype [System.Runtime]System.Nullable`1<int32>
unbox int32
call instance string [System.Runtime]System.Int32::ToString()
call void [System.Console]System.Console::WriteLine(class [System.Runtime]System.String)

leave.s SUCCESS
}
catch [System.Runtime]System.Object
{
pop
leave.s FAIL
}

SUCCESS:
ldstr "SUCCESS"
call void [System.Console]System.Console::WriteLine(class [System.Runtime]System.String)
ldc.i4 100
ret

FAIL:
ldstr "FAILED"
call void [System.Console]System.Console::WriteLine(class [System.Runtime]System.String)
ldc.i4 0
ret
}
}
11 changes: 11 additions & 0 deletions src/tests/JIT/opt/Casts/castclass_valuetype.ilproj
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<CLRTestPriority>1</CLRTestPriority>
</PropertyGroup>
<PropertyGroup>
<DebugType>PdbOnly</DebugType>
</PropertyGroup>
<ItemGroup>
<Compile Include="castclass_valuetype.il" />
</ItemGroup>
</Project>