Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.
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
48 changes: 48 additions & 0 deletions src/Common/src/TypeSystem/Common/FieldLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,26 @@ out byteCount
return algorithm._computedLayout;
}

#region Runtime specific adjustements to the static field layout
// TODO: these should be factored out to make the static field layout algorithm more general purpose

private static void PrepareRuntimeSpecificStaticFieldLayout(TypeSystemContext context, ref ComputedStaticFieldLayout layout)
{
// GC statics start with a pointer to the "EEType" that signals the size and GCDesc to the GC
layout.GcStatics.Size = context.Target.PointerSize;
}

private static void FinalizeRuntimeSpecificStaticFieldLayout(TypeSystemContext context, ref ComputedStaticFieldLayout layout)
{
// If the size of GCStatics is equal to the size set in PrepareRuntimeSpecificStaticFieldLayout, we
// don't have any GC statics
if (layout.GcStatics.Size == context.Target.PointerSize)
{
layout.GcStatics.Size = 0;
}
}
#endregion

public static unsafe ComputedStaticFieldLayout ComputeStaticFieldLayout(MetadataType type)
{
int numStaticFields = 0;
Expand All @@ -178,6 +198,8 @@ public static unsafe ComputedStaticFieldLayout ComputeStaticFieldLayout(Metadata

result.Offsets = new FieldAndOffset[numStaticFields];

PrepareRuntimeSpecificStaticFieldLayout(type.Context, ref result);

int index = 0;

foreach (var field in type.GetFields())
Expand Down Expand Up @@ -206,6 +228,8 @@ public static unsafe ComputedStaticFieldLayout ComputeStaticFieldLayout(Metadata
index++;
}

FinalizeRuntimeSpecificStaticFieldLayout(type.Context, ref result);

return result;
}

Expand Down Expand Up @@ -538,6 +562,30 @@ public int NonGCStaticFieldAlignment
}
}

public int GCStaticFieldSize
{
get
{
if (!_fieldLayoutFlags.HasFlags(FieldLayoutFlags.HasStaticFieldLayout))
{
ComputeStaticFieldLayout();
}
return _staticBlockInfo == null ? 0 : _staticBlockInfo.GcStatics.Size;
}
}

public int GCStaticFieldAlignment
{
get
{
if (!_fieldLayoutFlags.HasFlags(FieldLayoutFlags.HasStaticFieldLayout))
{
ComputeStaticFieldLayout();
}
return _staticBlockInfo == null ? 0 : _staticBlockInfo.GcStatics.LargestAlignment;
}
}

internal void ComputeInstanceFieldLayout()
{
var computedLayout = FieldLayoutAlgorithm.ComputeInstanceFieldLayout(this);
Expand Down
87 changes: 84 additions & 3 deletions src/ILToNative.Compiler/src/Compiler/AsmWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@ void OutputCode()
Out.WriteLine();

OutputReadyToHelpers();

OutputEETypes();

Out.WriteLine();
Out.WriteLine(".data");

OutputStatics();
Out.WriteLine();
Out.WriteLine("// Non-GC statics");
OutputNonGCStatics();

Out.WriteLine();
Out.WriteLine("// GC statics");
OutputGCStatics();

Out.Dispose();
}
Expand Down Expand Up @@ -248,6 +253,15 @@ void OutputReadyToHelpers()
Out.WriteLine("ret");
break;

case ReadyToRunHelperId.GetGCStaticBase:
Out.Write("leaq __GCStaticBase_");
Out.Write(NameMangler.GetMangledTypeName((TypeDesc)helper.Target));
Out.WriteLine("(%rip), %rax");
Out.WriteLine("mov (%rax), %rax");
Out.WriteLine("mov (%rax), %rax"); // RAX is now a GC pointer
Out.WriteLine("ret");
break;

default:
throw new NotImplementedException();
}
Expand Down Expand Up @@ -296,7 +310,7 @@ void OutputEETypes()
}
}

void OutputStatics()
void OutputNonGCStatics()
{
foreach (var t in _registeredTypes.Values)
{
Expand All @@ -323,6 +337,73 @@ void OutputStatics()
}
}

void OutputGCStatics()
{
// Emit an array of GCHandle-sized elements for each type with GC statics
// Each element will be initially pointing at the pseudo EEType for the static.
// At runtime, it will be replaced by a GC handle to the GC-heap allocated object.

Out.WriteLine(".global __GCStaticRegionStart");
Out.WriteLine("__GCStaticRegionStart:");

foreach (var t in _registeredTypes.Values)
{
if (!t.IncludedInCompilation)
continue;

var type = t.Type as MetadataType;
if (type == null)
continue;

if (type.GCStaticFieldSize > 0)
{
Out.Write("__GCStaticBase_");
Out.Write(NameMangler.GetMangledTypeName(type));
Out.WriteLine(":");
Out.Write(".quad ");
Out.Write("__GCStaticEEType_");
Out.Write(NameMangler.GetMangledTypeName(type));
Out.WriteLine();
}
}

Out.WriteLine(".global __GCStaticRegionEnd");
Out.WriteLine("__GCStaticRegionEnd:");

// Next emit a GCDesc followed by the size of the region described by the GCDesc
// for each type with GC statics.

// It should be possible to intern these at some point.

foreach (var t in _registeredTypes.Values)
{
if (!t.IncludedInCompilation)
continue;

var type = t.Type as MetadataType;
if (type == null)
continue;

if (type.GCStaticFieldSize > 0)
{
// numSeries
Out.WriteLine(".quad 0");

Out.Write("__GCStaticEEType_");
Out.Write(NameMangler.GetMangledTypeName(type));
Out.WriteLine(":");
Out.WriteLine(".int 0");
Out.Write(".int ");

// GC requires a minimum object size
int minimumObjectSize = type.Context.Target.PointerSize * 3;
int gcStaticSize = Math.Max(type.GCStaticFieldSize, minimumObjectSize);

Out.WriteLine(gcStaticSize);
}
}
}

void OutputVirtualSlots(TypeDesc implType, TypeDesc declType)
{
var baseType = declType.BaseType;
Expand Down
4 changes: 4 additions & 0 deletions src/ILToNative.Compiler/src/Compiler/JitHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace ILToNative
public enum JitHelperId
{
RngChkFail,
AssignRef,
}

class JitHelper
Expand All @@ -35,6 +36,9 @@ public string MangledName
case JitHelperId.RngChkFail:
return "__range_check_fail";

case JitHelperId.AssignRef:
return "WriteBarrier";

default:
throw new NotImplementedException();
}
Expand Down
3 changes: 3 additions & 0 deletions src/ILToNative.Compiler/src/Compiler/ReadyToRunHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public enum ReadyToRunHelperId
IsInstanceOf,
CastClass,
GetNonGCStaticBase,
GetGCStaticBase,
}

class ReadyToRunHelper
Expand Down Expand Up @@ -54,6 +55,8 @@ public string MangledName
return "__CastClass_" + _compilation.NameMangler.GetMangledTypeName((TypeDesc)this.Target);
case ReadyToRunHelperId.GetNonGCStaticBase:
return "__GetNonGCStaticBase_" + _compilation.NameMangler.GetMangledTypeName((TypeDesc)this.Target);
case ReadyToRunHelperId.GetGCStaticBase:
return "__GetGCStaticBase_" + _compilation.NameMangler.GetMangledTypeName((TypeDesc)this.Target);
default:
throw new NotImplementedException();
}
Expand Down
12 changes: 6 additions & 6 deletions src/ILToNative.TypeSystem/tests/StaticFieldLayoutTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ public void TestHasPointers()
switch (field.Name)
{
case "string1":
Assert.Equal(0, field.Offset);
Assert.Equal(8, field.Offset);
break;
case "class1":
Assert.Equal(8, field.Offset);
Assert.Equal(16, field.Offset);
break;
default:
throw new Exception(field.Name);
Expand All @@ -152,19 +152,19 @@ public void TestMixPointersAndNonPointers()
switch (field.Name)
{
case "string1":
Assert.Equal(0, field.Offset);
Assert.Equal(8, field.Offset);
break;
case "int1":
Assert.Equal(0, field.Offset);
break;
case "class1":
Assert.Equal(8, field.Offset);
Assert.Equal(16, field.Offset);
break;
case "int2":
Assert.Equal(4, field.Offset);
break;
case "string2":
Assert.Equal(16, field.Offset);
Assert.Equal(24, field.Offset);
break;
default:
throw new Exception(field.Name);
Expand Down Expand Up @@ -193,7 +193,7 @@ public void TestEnsureInheritanceResetsStaticOffsets()
Assert.Equal(0, field.Offset);
break;
case "string3":
Assert.Equal(0, field.Offset);
Assert.Equal(8, field.Offset);
break;
default:
throw new Exception(field.Name);
Expand Down
45 changes: 45 additions & 0 deletions src/ILToNative/reproNative/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,24 @@ void __reverse_pinvoke_return(ReversePInvokeFrame* pRevFrame)
#endif // USE_MRT
}

extern "C" void* __GCStaticRegionStart;
extern "C" void* __GCStaticRegionEnd;

void __register_module(SimpleModuleHeader* pModule)
{
#if USE_MRT
RhpRegisterSimpleModule(pModule);
#endif // USE_MRT


// Initialize GC statics in the module
// TODO: emit a ModuleHeader and use it here

for (void** currentBlock = &__GCStaticRegionStart; currentBlock < &__GCStaticRegionEnd; currentBlock++)
{
Object* gcBlock = __allocate_object((MethodTable*)*currentBlock);
*currentBlock = CreateGlobalHandle(ObjectToOBJECTREF(gcBlock));
}
}

namespace mscorlib { namespace System {
Expand Down Expand Up @@ -231,6 +244,38 @@ extern "C" Object * __allocate_array(size_t elements, MethodTable * pMT)
#endif
}

#if defined(_WIN64)
// Card byte shift is different on 64bit.
#define card_byte_shift 11
#else
#define card_byte_shift 10
#endif

#define card_byte(addr) (((size_t)(addr)) >> card_byte_shift)

inline void ErectWriteBarrier(Object ** dst, Object * ref)
{
// if the dst is outside of the heap (unboxed value classes) then we
// simply exit
if (((BYTE*)dst < g_lowest_address) || ((BYTE*)dst >= g_highest_address))
return;

if ((BYTE*)ref >= g_ephemeral_low && (BYTE*)ref < g_ephemeral_high)
{
// volatile is used here to prevent fetch of g_card_table from being reordered
// with g_lowest/highest_address check above. See comment in code:gc_heap::grow_brick_card_tables.
BYTE* pCardByte = (BYTE *)*(volatile BYTE **)(&g_card_table) + card_byte((BYTE *)dst);
if (*pCardByte != 0xFF)
*pCardByte = 0xFF;
}
}

extern "C" void WriteBarrier(Object ** dst, Object * ref)
{
*dst = ref;
ErectWriteBarrier(dst, ref);
}

void __throw_exception(void * pEx)
{
// TODO: Exception throwing
Expand Down
8 changes: 7 additions & 1 deletion src/JitInterface/src/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -861,10 +861,14 @@ void getFieldInfo(IntPtr _this, ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORIN
pResult.helper = CorInfoHelpFunc.CORINFO_HELP_READYTORUN_STATIC_BASE;

ReadyToRunHelperId helperId;
if (field.IsThreadStatic || field.HasGCStaticBase)
if (field.IsThreadStatic)
{
throw new NotImplementedException();
}
else if (field.HasGCStaticBase)
{
helperId = ReadyToRunHelperId.GetGCStaticBase;
}
else
{
helperId = ReadyToRunHelperId.GetNonGCStaticBase;
Expand Down Expand Up @@ -1066,6 +1070,8 @@ uint getThreadTLSIndex(IntPtr _this, ref void* ppIndirection)
{
case CorInfoHelpFunc.CORINFO_HELP_RNGCHKFAIL:
return (void*)ObjectToHandle(_compilation.GetJitHelper(JitHelperId.RngChkFail));
case CorInfoHelpFunc.CORINFO_HELP_ASSIGN_REF:
return (void*)ObjectToHandle(_compilation.GetJitHelper(JitHelperId.AssignRef));
default:
throw new NotImplementedException();
}
Expand Down