1) Add new form of generic lookup for generic non-GC static base.

2) Fix previously missing generic lookup for fields.

2) Fix treatment of generic fields according to CoreCLR.

Thanks

Tomas
This commit is contained in:
Tomáš Rylek 2019-01-24 14:32:55 +01:00 коммит произвёл GitHub
Родитель ffce8c20d4
Коммит 5346e8d329
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 247 добавлений и 97 удалений

Просмотреть файл

@ -30,6 +30,7 @@ namespace ILCompiler.DependencyAnalysis
// The following helpers are used for generic lookups only
TypeHandle,
NecessaryTypeHandle,
DeclaringTypeHandle,
MethodHandle,
FieldHandle,
MethodDictionary,

Просмотреть файл

@ -20,6 +20,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
private readonly MethodWithToken _methodArgument;
private readonly FieldDesc _fieldArgument;
private readonly GenericContext _methodContext;
private readonly SignatureContext _signatureContext;
@ -29,6 +31,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
ReadyToRunFixupKind fixupKind,
TypeDesc typeArgument,
MethodWithToken methodArgument,
FieldDesc fieldArgument,
GenericContext methodContext,
SignatureContext signatureContext)
{
@ -36,6 +39,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
_fixupKind = fixupKind;
_typeArgument = typeArgument;
_methodArgument = methodArgument;
_fieldArgument = fieldArgument;
_methodContext = methodContext;
_signatureContext = signatureContext;
}
@ -86,6 +90,10 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
isUnboxingStub: false,
isInstantiatingStub: true);
}
else if (_fieldArgument != null)
{
dataBuilder.EmitFieldSignature(_fieldArgument, _signatureContext);
}
else
{
throw new NotImplementedException();

Просмотреть файл

@ -448,42 +448,57 @@ namespace ILCompiler.DependencyAnalysis
throw new NotImplementedException();
}
protected override ISymbolNode CreateGenericLookupFromDictionaryNode(ReadyToRunGenericHelperKey helperKey)
private ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper GetGenericStaticHelper(ReadyToRunHelperId helperId)
{
switch (helperKey.HelperId)
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper r2rHelper;
switch (helperId)
{
case ReadyToRunHelperId.GetGCStaticBase:
return new DelayLoadHelperImport(
this,
HelperImports,
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_GenericGcStaticBase,
new TypeFixupSignature(
ReadyToRunFixupKind.READYTORUN_FIXUP_Invalid,
(TypeDesc)helperKey.Target,
InputModuleContext));
r2rHelper = ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_GenericGcStaticBase;
break;
case ReadyToRunHelperId.GetNonGCStaticBase:
r2rHelper = ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_GenericNonGcStaticBase;
break;
case ReadyToRunHelperId.GetThreadStaticBase:
r2rHelper = ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_GenericGcTlsBase;
break;
case ReadyToRunHelperId.GetThreadNonGcStaticBase:
r2rHelper = ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_GenericNonGcTlsBase;
break;
default:
throw new NotImplementedException();
}
return r2rHelper;
}
protected override ISymbolNode CreateGenericLookupFromDictionaryNode(ReadyToRunGenericHelperKey helperKey)
{
return new DelayLoadHelperImport(
this,
HelperImports,
GetGenericStaticHelper(helperKey.HelperId),
new TypeFixupSignature(
ReadyToRunFixupKind.READYTORUN_FIXUP_Invalid,
(TypeDesc)helperKey.Target,
InputModuleContext));
}
protected override ISymbolNode CreateGenericLookupFromTypeNode(ReadyToRunGenericHelperKey helperKey)
{
switch (helperKey.HelperId)
{
case ReadyToRunHelperId.GetGCStaticBase:
return new DelayLoadHelperImport(
this,
HelperImports,
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_GenericGcStaticBase,
new TypeFixupSignature(
ReadyToRunFixupKind.READYTORUN_FIXUP_Invalid,
(TypeDesc)helperKey.Target,
InputModuleContext));
default:
throw new NotImplementedException();
}
return new DelayLoadHelperImport(
this,
HelperImports,
GetGenericStaticHelper(helperKey.HelperId),
new TypeFixupSignature(
ReadyToRunFixupKind.READYTORUN_FIXUP_Invalid,
(TypeDesc)helperKey.Target,
InputModuleContext));
}
private Dictionary<string, SectionStartNode> _sectionStartNodes = new Dictionary<string, SectionStartNode>();

Просмотреть файл

@ -647,7 +647,7 @@ namespace ILCompiler.DependencyAnalysis
if (!_delegateCtors.TryGetValue(ctorKey, out ISymbolNode ctorNode))
{
IMethodNode targetMethodNode = _codegenNodeFactory.MethodEntrypoint(
targetMethod,
targetMethod,
constrainedType: null,
originalMethod: null,
methodToken: methodToken,
@ -697,6 +697,7 @@ namespace ILCompiler.DependencyAnalysis
public readonly ReadyToRunFixupKind FixupKind;
public readonly TypeDesc TypeArgument;
public readonly MethodWithToken MethodArgument;
public readonly FieldDesc FieldArgument;
public readonly GenericContext MethodContext;
public GenericLookupKey(
@ -704,12 +705,14 @@ namespace ILCompiler.DependencyAnalysis
ReadyToRunFixupKind fixupKind,
TypeDesc typeArgument,
MethodWithToken methodArgument,
FieldDesc fieldArgument,
GenericContext methodContext)
{
LookupKind = lookupKind;
FixupKind = fixupKind;
TypeArgument = typeArgument;
MethodArgument = methodArgument;
FieldArgument = fieldArgument;
MethodContext = methodContext;
}
@ -719,6 +722,7 @@ namespace ILCompiler.DependencyAnalysis
FixupKind == other.FixupKind &&
RuntimeDeterminedTypeHelper.Equals(TypeArgument, other.TypeArgument) &&
RuntimeDeterminedTypeHelper.Equals(MethodArgument?.Method ?? null, other.MethodArgument?.Method ?? null) &&
RuntimeDeterminedTypeHelper.Equals(FieldArgument, other.FieldArgument) &&
MethodContext.Equals(other.MethodContext);
}
@ -733,6 +737,7 @@ namespace ILCompiler.DependencyAnalysis
(int)FixupKind +
(TypeArgument != null ? 31 * RuntimeDeterminedTypeHelper.GetHashCode(TypeArgument) : 0) +
(MethodArgument != null ? 31 * RuntimeDeterminedTypeHelper.GetHashCode(MethodArgument.Method) : 0) +
(FieldArgument != null ? 31 * RuntimeDeterminedTypeHelper.GetHashCode(FieldArgument) : 0) +
MethodContext.GetHashCode());
}
}
@ -788,6 +793,14 @@ namespace ILCompiler.DependencyAnalysis
methodContext,
signatureContext);
case ReadyToRunHelperId.FieldHandle:
return GenericLookupFieldHelper(
runtimeLookupKind,
ReadyToRunFixupKind.READYTORUN_FIXUP_FieldHandle,
(FieldDesc)helperArgument,
methodContext,
signatureContext);
default:
throw new NotImplementedException();
}
@ -800,7 +813,7 @@ namespace ILCompiler.DependencyAnalysis
GenericContext methodContext,
SignatureContext signatureContext)
{
GenericLookupKey key = new GenericLookupKey(runtimeLookupKind, fixupKind, typeArgument, methodArgument: null, methodContext);
GenericLookupKey key = new GenericLookupKey(runtimeLookupKind, fixupKind, typeArgument, methodArgument: null, fieldArgument: null, methodContext);
ISymbolNode node;
if (!_genericLookupHelpers.TryGetValue(key, out node))
{
@ -808,7 +821,28 @@ namespace ILCompiler.DependencyAnalysis
_codegenNodeFactory,
_codegenNodeFactory.HelperImports,
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_Helper,
new GenericLookupSignature(runtimeLookupKind, fixupKind, typeArgument, methodArgument: null, methodContext, signatureContext));
new GenericLookupSignature(runtimeLookupKind, fixupKind, typeArgument, methodArgument: null, fieldArgument: null, methodContext, signatureContext));
_genericLookupHelpers.Add(key, node);
}
return node;
}
private ISymbolNode GenericLookupFieldHelper(
CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind,
ReadyToRunFixupKind fixupKind,
FieldDesc fieldArgument,
GenericContext methodContext,
SignatureContext signatureContext)
{
GenericLookupKey key = new GenericLookupKey(runtimeLookupKind, fixupKind, typeArgument: null, methodArgument: null, fieldArgument: fieldArgument, methodContext);
ISymbolNode node;
if (!_genericLookupHelpers.TryGetValue(key, out node))
{
node = new DelayLoadHelperImport(
_codegenNodeFactory,
_codegenNodeFactory.HelperImports,
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_Helper,
new GenericLookupSignature(runtimeLookupKind, fixupKind, typeArgument: null, methodArgument: null, fieldArgument: fieldArgument, methodContext, signatureContext));
_genericLookupHelpers.Add(key, node);
}
return node;
@ -821,7 +855,7 @@ namespace ILCompiler.DependencyAnalysis
GenericContext methodContext,
SignatureContext signatureContext)
{
GenericLookupKey key = new GenericLookupKey(runtimeLookupKind, fixupKind, typeArgument: null, methodArgument, methodContext);
GenericLookupKey key = new GenericLookupKey(runtimeLookupKind, fixupKind, typeArgument: null, methodArgument, fieldArgument: null, methodContext);
ISymbolNode node;
if (!_genericLookupHelpers.TryGetValue(key, out node))
{
@ -829,7 +863,7 @@ namespace ILCompiler.DependencyAnalysis
_codegenNodeFactory,
_codegenNodeFactory.HelperImports,
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_Helper,
new GenericLookupSignature(runtimeLookupKind, fixupKind, typeArgument: null, methodArgument, methodContext, signatureContext));
new GenericLookupSignature(runtimeLookupKind, fixupKind, typeArgument: null, methodArgument, fieldArgument: null, methodContext, signatureContext));
_genericLookupHelpers.Add(key, node);
}
return node;

Просмотреть файл

@ -109,6 +109,17 @@ namespace ILCompiler
return true;
}
public static bool Equals(FieldDesc field1, FieldDesc field2)
{
if (field1 == null || field2 == null)
{
return field1 == null && field2 == null;
}
return field1.Name == field2.Name &&
RuntimeDeterminedTypeHelper.Equals(field1.OwningType, field2.OwningType) &&
RuntimeDeterminedTypeHelper.Equals(field1.FieldType, field2.FieldType);
}
public static int GetHashCode(Instantiation instantiation)
{
int hashcode = unchecked(instantiation.Length << 24);
@ -136,6 +147,11 @@ namespace ILCompiler
method.GetTypicalMethodDefinition().GetHashCode() + 31 * GetHashCode(method.Instantiation)));
}
public static int GetHashCode(FieldDesc field)
{
return unchecked(GetHashCode(field.OwningType) + 97 * GetHashCode(field.FieldType) + 31 * field.Name.GetHashCode());
}
public static void WriteTo(Instantiation instantiation, Utf8StringBuilder sb)
{
sb.Append("<");

Просмотреть файл

@ -99,47 +99,49 @@ namespace Internal.JitInterface
if (_compilation.NeedsRuntimeLookup(helperId, entity))
{
lookup.lookupKind.needsRuntimeLookup = true;
lookup.runtimeLookup.signature = null;
MethodDesc contextMethod = methodFromContext(pResolvedToken.tokenContext);
// Do not bother computing the runtime lookup if we are inlining. The JIT is going
// to abort the inlining attempt anyway.
if (contextMethod != MethodBeingCompiled)
return;
GenericDictionaryLookup genericLookup = _compilation.ComputeGenericLookup(contextMethod, helperId, entity);
if (genericLookup.UseHelper)
if (contextMethod != _methodCodeNode.Method)
{
lookup.runtimeLookup.indirections = CORINFO.USEHELPER;
lookup.lookupKind.runtimeLookupFlags = (ushort)genericLookup.HelperId;
lookup.lookupKind.runtimeLookupArgs = (void*)ObjectToHandle(genericLookup.HelperObject);
return;
}
// There is a pathological case where invalid IL refereces __Canon type directly, but there is no dictionary available to store the lookup.
// All callers of ComputeRuntimeLookupForSharedGenericToken have to filter out this case. We can't do much about it here.
Debug.Assert(contextMethod.IsSharedByGenericInstantiations);
if (contextMethod.RequiresInstMethodDescArg())
{
lookup.lookupKind.runtimeLookupKind = CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_METHODPARAM;
}
else
{
if (genericLookup.ContextSource == GenericContextSource.MethodParameter)
if (contextMethod.RequiresInstMethodTableArg())
{
lookup.runtimeLookup.helper = CorInfoHelpFunc.CORINFO_HELP_RUNTIMEHANDLE_METHOD;
lookup.lookupKind.runtimeLookupKind = CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_CLASSPARAM;
}
else
{
lookup.runtimeLookup.helper = CorInfoHelpFunc.CORINFO_HELP_RUNTIMEHANDLE_CLASS;
lookup.lookupKind.runtimeLookupKind = CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ;
}
lookup.runtimeLookup.indirections = (ushort)genericLookup.NumberOfIndirections;
lookup.runtimeLookup.offset0 = (IntPtr)genericLookup[0];
if (genericLookup.NumberOfIndirections > 1)
lookup.runtimeLookup.offset1 = (IntPtr)genericLookup[1];
lookup.runtimeLookup.testForFixup = false; // TODO: this will be needed in true multifile
lookup.runtimeLookup.testForNull = false;
lookup.runtimeLookup.indirectFirstOffset = false;
lookup.runtimeLookup.indirectSecondOffset = false;
lookup.lookupKind.runtimeLookupFlags = 0;
lookup.lookupKind.runtimeLookupArgs = null;
}
lookup.lookupKind.runtimeLookupKind = GetLookupKindFromContextSource(genericLookup.ContextSource);
// Unless we decide otherwise, just do the lookup via a helper function
lookup.runtimeLookup.indirections = CORINFO.USEHELPER;
lookup.lookupKind.runtimeLookupFlags = (ushort)helperId;
lookup.lookupKind.runtimeLookupArgs = (void*)ObjectToHandle(entity);
lookup.runtimeLookup.indirectFirstOffset = false;
lookup.runtimeLookup.indirectSecondOffset = false;
// For R2R compilations, we don't generate the dictionary lookup signatures (dictionary lookups are done in a
// different way that is more version resilient... plus we can't have pointers to existing MTs/MDs in the sigs)
}
else
{
@ -246,36 +248,9 @@ namespace Internal.JitInterface
MethodDesc targetMethod = HandleToObject(pTargetMethod.hMethod);
TypeDesc delegateTypeDesc = HandleToObject(delegateType);
if (targetMethod.IsSharedByGenericInstantiations)
{
// If the method is not exact, fetch it as a runtime determined method.
targetMethod = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pTargetMethod);
}
/* TODO
bool isLdvirtftn = pTargetMethod.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Ldvirtftn;
DelegateCreationInfo delegateInfo = _compilation.GetDelegateCtor(delegateTypeDesc, targetMethod, isLdvirtftn);
if (delegateInfo.NeedsRuntimeLookup)
{
pLookup.lookupKind.needsRuntimeLookup = true;
MethodDesc contextMethod = methodFromContext(pTargetMethod.tokenContext);
// We should not be inlining these. RyuJIT should have aborted inlining already.
Debug.Assert(contextMethod == MethodBeingCompiled);
pLookup.lookupKind.runtimeLookupKind = GetGenericRuntimeLookupKind(contextMethod);
pLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.DelegateCtor;
pLookup.lookupKind.runtimeLookupArgs = (void*)ObjectToHandle(delegateInfo);
}
else
*/
{
pLookup.lookupKind.needsRuntimeLookup = false;
pLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.DelegateCtor(
pLookup.lookupKind.needsRuntimeLookup = false;
pLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.DelegateCtor(
delegateTypeDesc, targetMethod, new ModuleToken(_tokenContext, (mdToken)pTargetMethod.token), _signatureContext));
}
}
private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
@ -338,6 +313,24 @@ namespace Internal.JitInterface
id = ReadyToRunHelper.Ldelema_Ref;
break;
case CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_GCSTATIC_BASE:
id = ReadyToRunHelper.GenericGcStaticBase;
break;
case CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE:
id = ReadyToRunHelper.GenericNonGcStaticBase;
break;
case CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE:
id = ReadyToRunHelper.GenericGcTlsBase;
break;
case CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE:
id = ReadyToRunHelper.GenericNonGcTlsBase;
break;
case CorInfoHelpFunc.CORINFO_HELP_MEMSET:
id = ReadyToRunHelper.MemSet;
break;

Просмотреть файл

@ -1983,6 +1983,21 @@ namespace Internal.JitInterface
else if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any))
{
// The JIT wants to know how to access a static field on a generic type. We need a runtime lookup.
#if READYTORUN
fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_STATIC_GENERICS_STATIC_HELPER;
if (field.IsThreadStatic)
{
pResult->helper = (field.HasGCStaticBase ?
CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE:
CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE);
}
else
{
pResult->helper = (field.HasGCStaticBase ?
CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_GCSTATIC_BASE:
CorInfoHelpFunc.CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE);
}
#else
fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_STATIC_READYTORUN_HELPER;
pResult->helper = CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE;
@ -1998,18 +2013,7 @@ namespace Internal.JitInterface
// Find out what kind of base do we need to look up.
if (field.IsThreadStatic)
{
#if READYTORUN
if (field.HasGCStaticBase)
{
helperId = ReadyToRunHelperId.GetThreadStaticBase;
}
else
{
helperId = ReadyToRunHelperId.GetThreadNonGcStaticBase;
}
#else
helperId = ReadyToRunHelperId.GetThreadStaticBase;
#endif
}
else if (field.HasGCStaticBase)
{
@ -2036,6 +2040,7 @@ namespace Internal.JitInterface
pResult->fieldLookup = CreateConstLookupToSymbol(helper);
}
#endif // READYTORUN
}
else
{
@ -2553,9 +2558,9 @@ namespace Internal.JitInterface
{
Debug.Assert(fEmbedParent);
if (obj is MethodDesc)
if (obj is MethodDesc objAsMethod)
{
target = ((MethodDesc)obj).OwningType;
target = objAsMethod.OwningType;
}
else
{
@ -2587,7 +2592,7 @@ namespace Internal.JitInterface
}
Debug.Assert(pResult.compileTimeHandle != null);
ComputeLookup(ref pResolvedToken, target, helperId, ref pResult.lookup);
}

Просмотреть файл

@ -697,6 +697,80 @@ internal class Program
return true;
}
class MyGen<T>
{
public static string GcValue;
public static int NonGcValue;
[ThreadStatic]
public static string TlsGcValue;
[ThreadStatic]
public static int TlsNonGcValue;
}
private static void SetGenericGcStatic<U, V>(string uValue, string vValue)
{
MyGen<U>.GcValue = uValue;
MyGen<V>.GcValue = vValue;
}
private static void SetGenericNonGcStatic<U, V>(int uValue, int vValue)
{
MyGen<U>.NonGcValue = uValue;
MyGen<V>.NonGcValue = vValue;
}
private static void SetGenericTlsGcStatic<U, V>(string uValue, string vValue)
{
MyGen<U>.TlsGcValue = uValue;
MyGen<V>.TlsGcValue = vValue;
}
private static void SetGenericTlsNonGcStatic<U, V>(int uValue, int vValue)
{
MyGen<U>.TlsNonGcValue = uValue;
MyGen<V>.TlsNonGcValue = vValue;
}
private static bool SharedGenericGcStaticTest()
{
string objectValue = "Hello";
string stringValue = "World";
SetGenericGcStatic<object, string>(objectValue, stringValue);
Console.WriteLine("Object GC value: {0}, expected {1}", MyGen<object>.GcValue, objectValue);
Console.WriteLine("String GC value: {0}, expected {1}", MyGen<string>.GcValue, stringValue);
return MyGen<object>.GcValue == objectValue && MyGen<string>.GcValue == stringValue;
}
private static bool SharedGenericNonGcStaticTest()
{
int objectValue = 42;
int stringValue = 666;
SetGenericNonGcStatic<object, string>(objectValue, stringValue);
Console.WriteLine("Object non-GC value: {0}, expected {1}", MyGen<object>.NonGcValue, objectValue);
Console.WriteLine("String non-GC value: {0}, expected {1}", MyGen<string>.NonGcValue, stringValue);
return MyGen<object>.NonGcValue == objectValue && MyGen<string>.NonGcValue == stringValue;
}
private static bool SharedGenericTlsGcStaticTest()
{
string objectValue = "Cpaot";
string stringValue = "Rules";
SetGenericTlsGcStatic<object, string>(objectValue, stringValue);
Console.WriteLine("Object TLS GC value: {0}, expected {1}", MyGen<object>.TlsGcValue, objectValue);
Console.WriteLine("String TLS GC value: {0}, expected {1}", MyGen<string>.TlsGcValue, stringValue);
return MyGen<object>.TlsGcValue == objectValue && MyGen<string>.TlsGcValue == stringValue;
}
private static bool SharedGenericTlsNonGcStaticTest()
{
int objectValue = 1234;
int stringValue = 5678;
SetGenericTlsNonGcStatic<object, string>(objectValue, stringValue);
Console.WriteLine("Object TLS non-GC value: {0}, expected {1}", MyGen<object>.TlsNonGcValue, objectValue);
Console.WriteLine("String TLS non-GC value: {0}, expected {1}", MyGen<string>.TlsNonGcValue, stringValue);
return MyGen<object>.TlsNonGcValue == objectValue && MyGen<string>.TlsNonGcValue == stringValue;
}
static bool RVAFieldTest()
{
@ -764,6 +838,10 @@ internal class Program
RunTest("VectorTest", VectorTest());
RunTest("EnumHashValueTest", EnumHashValueTest());
RunTest("RVAFieldTest", RVAFieldTest());
RunTest("SharedGenericGcStaticTest", SharedGenericGcStaticTest());
RunTest("SharedGenericNonGcStaticTest", SharedGenericNonGcStaticTest());
RunTest("SharedGenericTlsGcStaticTest", SharedGenericTlsGcStaticTest());
RunTest("SharedGenericTlsNonGcStaticTest", SharedGenericTlsNonGcStaticTest());
Console.WriteLine($@"{_passedTests.Count} tests pass:");
foreach (string testName in _passedTests)