Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6522,6 +6522,50 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, const B
return 1 + sizeof(mdToken);
}
}
else if (boxHelper == CORINFO_HELP_BOX_NULLABLE)
{
// For nullable we're going to fold it to "ldfld hasValue + brtrue/brfalse" or
// "ldc.i4.0 + brtrue/brfalse" in case if the underlying type is not castable to
// the target type.
CORINFO_RESOLVED_TOKEN isInstResolvedToken;
impResolveToken(codeAddr + 1, &isInstResolvedToken, CORINFO_TOKENKIND_Casting);

CORINFO_CLASS_HANDLE nullableCls = pResolvedToken->hClass;
CORINFO_CLASS_HANDLE underlyingCls = info.compCompHnd->getTypeForBox(nullableCls);

TypeCompareState castResult =
info.compCompHnd->compareTypesForCast(underlyingCls,
isInstResolvedToken.hClass);

if (castResult == TypeCompareState::Must)
{
const CORINFO_FIELD_HANDLE hasValueFldHnd =
info.compCompHnd->getFieldInClass(nullableCls, 0);

assert(info.compCompHnd->getFieldOffset(hasValueFldHnd) == 0);
assert(!strcmp(info.compCompHnd->getFieldName(hasValueFldHnd, nullptr),
"hasValue"));

GenTree* objToBox = impPopStack().val;

// Spill struct to get its address (to access hasValue field)
objToBox =
impGetStructAddr(objToBox, nullableCls, (unsigned)CHECK_SPILL_ALL, true);

impPushOnStack(gtNewFieldRef(TYP_BOOL, hasValueFldHnd, objToBox, 0),
typeInfo(TI_INT));

JITDUMP("\n Importing BOX; ISINST; BR_TRUE/FALSE as nullableVT.hasValue\n");
return 1 + sizeof(mdToken);
}
else if (castResult == TypeCompareState::MustNot)
{
impPopStack();
impPushOnStack(gtNewIconNode(0), typeInfo(TI_INT));
JITDUMP("\n Importing BOX; ISINST; BR_TRUE/FALSE as constant (false)\n");
return 1 + sizeof(mdToken);
}
}
}
}
break;
Expand Down
102 changes: 102 additions & 0 deletions src/tests/JIT/Generics/Conversions/Boxing/box_isinst_br_nullable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;

public static class Tests
{
private static int returnCode = 100;

[MethodImpl(MethodImplOptions.NoInlining)]
public static int BoxIsInstBr1<T>(T t) => t is int ? 1 : 0;

[MethodImpl(MethodImplOptions.NoInlining)]
public static int BoxIsInstBr2<T>(T t) => t is string ? 1 : 0;

[MethodImpl(MethodImplOptions.NoInlining)]
public static int BoxIsInstBr3<T>(T t) => t is Struct1<int> ? 1 : 0;

[MethodImpl(MethodImplOptions.NoInlining)]
public static int BoxIsInstBr4<T>(T t) => t is Struct1<IDisposable> ? 1 : 0;

[MethodImpl(MethodImplOptions.NoInlining)]
public static int BoxIsInstBr5<T>(T t) => t is Class1<int> ? 1 : 0;

[MethodImpl(MethodImplOptions.NoInlining)]
public static int BoxIsInstBr6<T>(T t) => t is RefBase ? 1 : 0;

[MethodImpl(MethodImplOptions.NoInlining)]
public static int BoxIsInstBr7<T>(T t) => t is object[] ? 1 : 0;

public static void Expect(this int actual, int expected, [CallerLineNumber] int line = 0)
{
if (expected != actual)
{
Console.WriteLine($"{actual} != {expected}, line {line}.");
returnCode++;
}
}

public static int Main()
{
BoxIsInstBr1<int?>(1).Expect(1);
BoxIsInstBr1<int?>(null).Expect(0);
BoxIsInstBr1<uint?>(1).Expect(0);
BoxIsInstBr1<string>(null).Expect(0);
BoxIsInstBr1<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(0);
BoxIsInstBr1<Struct1<DateTime>?>(new Struct1<DateTime> { a = DateTime.Now }).Expect(0);

BoxIsInstBr2<int?>(1).Expect(0);
BoxIsInstBr2<int?>(null).Expect(0);
BoxIsInstBr2<uint?>(1).Expect(0);
BoxIsInstBr2<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(0);
BoxIsInstBr1<Struct1<DateTime>?>(new Struct1<DateTime> { a = DateTime.Now }).Expect(0);

BoxIsInstBr3<int?>(1).Expect(0);
BoxIsInstBr3<int?>(null).Expect(0);
BoxIsInstBr3<uint?>(1).Expect(0);
BoxIsInstBr3<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(1);
BoxIsInstBr1<Struct1<DateTime>?>(new Struct1<DateTime> { a = DateTime.Now }).Expect(0);

BoxIsInstBr4<int?>(1).Expect(0);
BoxIsInstBr4<int?>(null).Expect(0);
BoxIsInstBr4<uint?>(1).Expect(0);

BoxIsInstBr5<int?>(1).Expect(0);
BoxIsInstBr5<int?>(null).Expect(0);
BoxIsInstBr5<uint?>(1).Expect(0);
BoxIsInstBr5<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(0);
BoxIsInstBr1<Struct1<DateTime>?>(new Struct1<DateTime> { a = DateTime.Now }).Expect(0);

BoxIsInstBr6<int?>(1).Expect(0);
BoxIsInstBr6<int?>(null).Expect(0);
BoxIsInstBr6<uint?>(1).Expect(0);
BoxIsInstBr6<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(0);
BoxIsInstBr1<Struct1<DateTime>?>(new Struct1<DateTime> { a = DateTime.Now }).Expect(0);

BoxIsInstBr7<int?>(1).Expect(0);
BoxIsInstBr7<int?>(null).Expect(0);
BoxIsInstBr7<uint?>(1).Expect(0);
BoxIsInstBr7<Struct1<int>?>(new Struct1<int> { a = 1 }).Expect(0);
BoxIsInstBr1<Struct1<DateTime>?>(new Struct1<DateTime> { a = DateTime.Now }).Expect(0);

return returnCode;
}
}

public struct Struct1<T>
{
public T a;
}

public class RefBase : IDisposable
{
public int a;
public void Dispose() { }
}

public class Class1<T> : RefBase
{
public T b;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="box_isinst_br_nullable.cs" />
</ItemGroup>
</Project>