1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00

Support for marking append-allocated objects (mHasAppendWantMark)

This commit is contained in:
Brian Fiete 2025-01-28 14:49:15 -08:00
parent 9ae172c43f
commit 9baf0ead21
18 changed files with 475 additions and 103 deletions

View file

@ -78,6 +78,33 @@ namespace System
[AlwaysInclude]
static class Internal
{
enum BfObjectFlags : uint8
{
None = 0,
Mark1 = 0x01,
Mark2 = 0x02,
Mark3 = 0x03,
Allocated = 0x04,
StackAlloc = 0x08,
AppendAlloc = 0x10,
AllocInfo = 0x20,
AllocInfo_Short = 0x40,
Deleted = 0x80
};
struct AppendAllocEntry
{
public enum Kind
{
case None;
case Object(Object obj);
case Raw(void* ptr, DbgRawAllocData* allocData);
}
public Kind mKind;
public AppendAllocEntry* mNext;
}
[Intrinsic("cast")]
public static extern Object UnsafeCastToObject(void* ptr);
[Intrinsic("cast")]
@ -273,11 +300,11 @@ namespace System
[CallingConvention(.Cdecl)]
public static extern int Dbg_PrepareStackTrace(int baseAllocSize, int maxStackTraceDepth);
[CallingConvention(.Cdecl)]
public static extern void Dbg_ObjectStackInit(Object object, ClassVData* classVData);
public static extern void Dbg_ObjectStackInit(Object object, ClassVData* classVData, int size, uint8 allocFlags);
[CallingConvention(.Cdecl)]
public static extern Object Dbg_ObjectAlloc(TypeInstance typeInst, int size);
[CallingConvention(.Cdecl)]
public static extern Object Dbg_ObjectAlloc(ClassVData* classVData, int size, int align, int maxStackTraceDepth);
public static extern Object Dbg_ObjectAlloc(ClassVData* classVData, int size, int align, int maxStackTraceDepth, uint8 flags);
[CallingConvention(.Cdecl)]
public static extern void Dbg_ObjectPreDelete(Object obj);
[CallingConvention(.Cdecl)]
@ -293,6 +320,183 @@ namespace System
[CallingConvention(.Cdecl)]
public static extern void Dbg_RawFree(void* ptr);
#if BF_ENABLE_OBJECT_DEBUG_FLAGS
static void AddAppendInfo(Object rootObj, AppendAllocEntry.Kind kind)
{
Compiler.Assert(sizeof(AppendAllocEntry) <= sizeof(int)*4);
void Handle(AppendAllocEntry* headAllocEntry)
{
if (headAllocEntry.mKind case .None)
{
headAllocEntry.mKind = kind;
}
else
{
AppendAllocEntry* newAppendAllocEntry = (.)new uint8[sizeof(AppendAllocEntry)]*;
newAppendAllocEntry.mKind = kind;
newAppendAllocEntry.mNext = headAllocEntry.mNext;
headAllocEntry.mNext = newAppendAllocEntry;
}
}
if (rootObj.[Friend]mClassVData & (int)BfObjectFlags.AllocInfo_Short != 0)
{
var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo;
uint8 allocFlag = (.)(dbgAllocInfo >> 8);
Debug.Assert(allocFlag == 1);
if ((allocFlag & 1) != 0)
{
int allocSize = (.)(dbgAllocInfo >> 16);
int capturedTraceCount = (uint8)(dbgAllocInfo);
uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj);
ptr += allocSize + capturedTraceCount * sizeof(int);
Handle((.)ptr);
}
}
else if (rootObj.[Friend]mClassVData & (int)BfObjectFlags.AllocInfo != 0)
{
var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo;
int allocSize = dbgAllocInfo;
uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj);
int info = *(int*)(ptr + allocSize);
int capturedTraceCount = info >> 8;
uint8 allocFlag = (.)info;
Debug.Assert(allocFlag == 1);
if ((allocFlag & 1) != 0)
{
ptr += allocSize + capturedTraceCount * sizeof(int) + sizeof(int);
Handle((.)ptr);
}
}
}
#endif
public static void Dbg_ObjectAppended(Object rootObj, Object appendObj)
{
#if BF_ENABLE_OBJECT_DEBUG_FLAGS
AddAppendInfo(rootObj, .Object(appendObj));
#endif
}
public static void Dbg_RawAppended(Object rootObj, void* ptr, DbgRawAllocData* rawAllocData)
{
#if BF_ENABLE_OBJECT_DEBUG_FLAGS
AddAppendInfo(rootObj, .Raw(ptr, rawAllocData));
#endif
}
public static void Dbg_MarkAppended(Object rootObj)
{
#if BF_ENABLE_OBJECT_DEBUG_FLAGS
void Handle(AppendAllocEntry* checkAllocEntry)
{
var checkAllocEntry;
while (checkAllocEntry != null)
{
switch (checkAllocEntry.mKind)
{
case .Object(let obj):
obj.[Friend]GCMarkMembers();
case .Raw(let rawPtr, let allocData):
((function void(void*))allocData.mMarkFunc)(rawPtr);
default:
}
checkAllocEntry = checkAllocEntry.mNext;
}
}
if (rootObj.[DisableObjectAccessChecks, Friend]mClassVData & (int)BfObjectFlags.AllocInfo_Short != 0)
{
var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo;
uint8 allocFlag = (.)(dbgAllocInfo >> 8);
if ((allocFlag & 1) != 0)
{
int allocSize = (.)(dbgAllocInfo >> 16);
int capturedTraceCount = (uint8)(dbgAllocInfo);
uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj);
ptr += allocSize + capturedTraceCount * sizeof(int);
Handle((.)ptr);
}
}
else if (rootObj.[DisableObjectAccessChecks, Friend]mClassVData & (int)BfObjectFlags.AllocInfo != 0)
{
var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo;
int allocSize = dbgAllocInfo;
uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj);
int info = *(int*)(ptr + allocSize);
int capturedTraceCount = info >> 8;
uint8 allocFlag = (.)info;
if ((allocFlag & 1) != 0)
{
ptr += allocSize + capturedTraceCount * sizeof(int) + sizeof(int);
Handle((.)ptr);
}
}
#endif
}
public static void Dbg_AppendDeleted(Object rootObj)
{
#if BF_ENABLE_OBJECT_DEBUG_FLAGS
void Handle(AppendAllocEntry* headAllocEntry)
{
AppendAllocEntry* checkAllocEntry = headAllocEntry;
while (checkAllocEntry != null)
{
switch (checkAllocEntry.mKind)
{
case .Object(let obj):
#unwarn
if (!obj.[DisableObjectAccessChecks]IsDeleted())
{
if (obj.GetType().HasDestructor)
Debug.FatalError("Appended object not deleted with 'delete:append'");
}
case .Raw(let rawPtr, let allocData):
default:
}
var nextAllocEntry = checkAllocEntry.mNext;
if (checkAllocEntry == headAllocEntry)
*checkAllocEntry = default;
else
delete (uint8*)checkAllocEntry;
checkAllocEntry = nextAllocEntry;
}
}
if (rootObj.[DisableObjectAccessChecks, Friend]mClassVData & (int)BfObjectFlags.AllocInfo_Short != 0)
{
var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo;
uint8 allocFlag = (.)(dbgAllocInfo >> 8);
if ((allocFlag & 1) != 0)
{
int allocSize = (.)(dbgAllocInfo >> 16);
int capturedTraceCount = (uint8)(dbgAllocInfo);
uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj);
ptr += allocSize + capturedTraceCount * sizeof(int);
Handle((.)ptr);
}
}
else if (rootObj.[DisableObjectAccessChecks, Friend]mClassVData & (int)BfObjectFlags.AllocInfo != 0)
{
var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo;
int allocSize = dbgAllocInfo;
uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj);
int info = *(int*)(ptr + allocSize);
int capturedTraceCount = info >> 8;
uint8 allocFlag = (.)info;
if ((allocFlag & 1) != 0)
{
ptr += allocSize + capturedTraceCount * sizeof(int) + sizeof(int);
Handle((.)ptr);
}
}
#endif
}
[CallingConvention(.Cdecl)]
static extern void Shutdown_Internal();
@ -304,19 +508,7 @@ namespace System
}
#else
enum BfObjectFlags : uint8
{
None = 0,
Mark1 = 0x01,
Mark2 = 0x02,
Mark3 = 0x03,
Allocated = 0x04,
StackAlloc = 0x08,
AppendAlloc = 0x10,
AllocInfo = 0x20,
AllocInfo_Short = 0x40,
Deleted = 0x80
};
[NoReturn]
static void Crash()

View file

@ -178,7 +178,7 @@ namespace System.Reflection
int32 stackCount = Compiler.Options.AllocStackCount;
if (mAllocStackCountOverride != 0)
stackCount = mAllocStackCountOverride;
obj = Internal.Dbg_ObjectAlloc(mTypeClassVData, allocSize, mInstAlign, stackCount);
obj = Internal.Dbg_ObjectAlloc(mTypeClassVData, allocSize, mInstAlign, stackCount, 0);
#else
void* mem = new [Align(16)] uint8[allocSize]* (?);
obj = Internal.UnsafeCastToObject(mem);

View file

@ -111,7 +111,7 @@ namespace System
function void* (int size) mAlloc;
function void (void* ptr) mFree;
function void (Object obj) mObject_Delete;
void* mUnused0;
function void* (ClassVData* vdataPtr) mClassVData_GetTypeData;
function Type (Object obj) mObject_GetType;
function void (Object obj) mObject_GCMarkMembers;
function Object (Object obj, int32 typeId) mObject_DynamicCastToTypeId;
@ -152,6 +152,16 @@ namespace System
delete obj;
}
static void* ClassVData_GetTypeData(ClassVData* classVData)
{
#if BF_32_BIT
Type type = Type.[Friend]GetType_(classVData.mType2);
#else
Type type = Type.[Friend]GetType_((.)(classVData.mType >> 32));
#endif
return &type.[Friend]mSize;
}
static Type Object_GetType(Object obj)
{
#if BF_DBG_RUNTIME
@ -259,6 +269,7 @@ namespace System
mAlloc = => Alloc;
mFree = => Free;
mObject_Delete = => Object_Delete;
mClassVData_GetTypeData = => ClassVData_GetTypeData;
mObject_GetType = => Object_GetType;
mObject_GCMarkMembers = => Object_GCMarkMembers;
mObject_DynamicCastToTypeId = => Object_DynamicCastToTypeId;

View file

@ -1660,7 +1660,7 @@ namespace System.Reflection
int32 stackCount = Compiler.Options.AllocStackCount;
if (mAllocStackCountOverride != 0)
stackCount = mAllocStackCountOverride;
obj = Internal.Dbg_ObjectAlloc([Friend]mTypeClassVData, arraySize, [Friend]mInstAlign, stackCount);
obj = Internal.Dbg_ObjectAlloc([Friend]mTypeClassVData, arraySize, [Friend]mInstAlign, stackCount, 0);
#else
void* mem = new [Align(16)] uint8[arraySize]* (?);
obj = Internal.UnsafeCastToObject(mem);
@ -1720,6 +1720,7 @@ namespace System.Reflection
Static = 0x200000,
Abstract = 0x400000,
HasAppendWantMark = 0x800000,
}
public enum FieldFlags : uint16

View file

@ -49,10 +49,10 @@ namespace bf
BFRT_EXPORT static void Dbg_ObjectCreatedEx(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData);
BFRT_EXPORT static void Dbg_ObjectAllocated(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData);
BFRT_EXPORT static void Dbg_ObjectAllocatedEx(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData);
BFRT_EXPORT static Object* Dbg_ObjectAlloc(bf::System::Reflection::TypeInstance* typeInst, intptr size);
BFRT_EXPORT static Object* Dbg_ObjectAlloc(bf::System::ClassVData* classVData, intptr size, intptr align, intptr maxStackTraceDept);
BFRT_EXPORT static Object* Dbg_ObjectAlloc(bf::System::Reflection::TypeInstance* typeInst, intptr size);
BFRT_EXPORT static Object* Dbg_ObjectAlloc(bf::System::ClassVData* classVData, intptr size, intptr align, intptr maxStackTraceDept, uint8 allocFlags);
BFRT_EXPORT static void Dbg_MarkObjectDeleted(bf::System::Object* obj);
BFRT_EXPORT static void Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData);
BFRT_EXPORT static void Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData, intptr size, uint8 allocFlags);
BFRT_EXPORT static void Dbg_ObjectPreDelete(bf::System::Object* obj);
BFRT_EXPORT static void Dbg_ObjectPreCustomDelete(bf::System::Object* obj);
@ -315,20 +315,28 @@ bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::Reflection::TypeInstan
//#define DBG_OBJECTEND
bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData, intptr size, intptr align, intptr maxStackTraceDepth)
bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData, intptr size, intptr align, intptr maxStackTraceDepth, uint8 allocFlags)
{
void* stackTrace[1024];
int capturedTraceCount = 0;
intptr allocSize = size;
bool largeAllocInfo = false;
if ((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0)
{
if (maxStackTraceDepth > 1)
{
capturedTraceCount = BF_CAPTURE_STACK(1, (intptr*)stackTrace, min((int)maxStackTraceDepth, 1024));
const intptr maxSmallObjectSize = ((intptr)1 << ((sizeof(intptr) - 2) * 8)) - 1;
if ((capturedTraceCount > 255) || (size >= maxSmallObjectSize))
bool forceLarge = false;
auto typeData = BFRTCALLBACKS.ClassVData_GetTypeDataPtr(classVData);
if ((typeData->mTypeFlags & BfTypeFlag_HasAppendWantMark) != 0)
{
forceLarge = true;
}
if ((capturedTraceCount > 255) || (size >= maxSmallObjectSize) || (forceLarge))
{
largeAllocInfo = true;
allocSize += (1 + capturedTraceCount) * sizeof(intptr);
@ -336,6 +344,12 @@ bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData
else
allocSize += capturedTraceCount * sizeof(intptr);
}
// Append want mark
if ((allocFlags & 1) != 0)
{
allocSize += 4 * sizeof(intptr);
}
}
#ifdef DBG_OBJECTEND
@ -385,7 +399,7 @@ bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData
intptr dbgAllocInfo;
auto classVDataVal = (intptr)classVData | (intptr)BfObjectFlag_Allocated;
if (maxStackTraceDepth <= 1)
if ((maxStackTraceDepth <= 1) && (allocFlags == 0))
dbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
else
{
@ -393,13 +407,13 @@ bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData
{
classVDataVal |= (intptr)BfObjectFlag_AllocInfo;
dbgAllocInfo = size;
*(intptr*)((uint8*)result + size) = capturedTraceCount;
*(intptr*)((uint8*)result + size) = (capturedTraceCount << 8) | allocFlags;
memcpy((uint8*)result + size + sizeof(intptr), stackTrace, capturedTraceCount * sizeof(intptr));
}
else
{
classVDataVal |= (intptr)BfObjectFlag_AllocInfo_Short;
dbgAllocInfo = (size << 16) | capturedTraceCount;
dbgAllocInfo = (size << 16) | (((intptr)allocFlags) << 8) | capturedTraceCount;
memcpy((uint8*)result + size, stackTrace, capturedTraceCount * sizeof(intptr));
}
}
@ -424,13 +438,27 @@ bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData
return result;
}
void Internal::Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData)
void Internal::Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData, intptr totalSize, uint8 allocFlags)
{
BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0);
result->mClassVData = (intptr)classVData | (intptr)BfObjectFlag_StackAlloc;
#ifndef BFRT_NODBGFLAGS
result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
if ((allocFlags & 1) == 0)
{
result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS;
}
else
{
int size = (int)(totalSize - sizeof(intptr) - sizeof(intptr) * 4);
int capturedTraceCount = 1;
*(intptr*)((uint8*)result + size) = (intptr)BF_RETURN_ADDRESS;
memset((uint8*)result + size + sizeof(intptr), 0, sizeof(intptr) * 4);
result->mDbgAllocInfo = (size << 16) | (((intptr)allocFlags) << 8) | capturedTraceCount;
BF_FULL_MEMORY_FENCE();
result->mClassVData |= (intptr)BfObjectFlag_AllocInfo_Short;
}
#endif
}

View file

@ -2670,10 +2670,13 @@ void BFGC::WriteDebugDumpState()
{
//const bf::System::Type* bfTypeRootData = ((bf::System::Type*)obj->GetTypeSafe())->mTypeRootData;
bf::System::Type* bfType = obj->GetTypeSafe();
while ((int) debugInfoVector.size() <= bfType->mTypeId)
auto typeData = bfType->GetTypeData();
while ((int) debugInfoVector.size() <= typeData->mTypeId)
debugInfoVector.push_back(DebugInfo());
DebugInfo* debugInfo = &debugInfoVector[bfType->mTypeId];
DebugInfo* debugInfo = &debugInfoVector[typeData->mTypeId];
debugInfo->mType = obj->GetTypeSafe();
debugInfo->mCount++;
int objSize = BFGetObjectSize(obj);

View file

@ -115,16 +115,18 @@ void BFGC::RawMarkSpan(tcmalloc_raw::Span* span, int expectedStartPage)
if (rawAllocData != NULL)
{
if (rawAllocData->mMarkFunc != NULL)
{
{
intptr extraDataSize = sizeof(intptr);
if (rawAllocData->mMaxStackTrace == 1)
{
extraDataSize += sizeof(intptr);
extraDataSize += sizeof(intptr) + 2;
extraDataSize += *(uint16*)((uint8*)spanPtr + elementSize - sizeof(intptr) - sizeof(intptr) - 2);
}
else if (rawAllocData->mMaxStackTrace > 1)
{
intptr stackTraceCount = *(intptr*)((uint8*)spanPtr + elementSize - sizeof(intptr) - sizeof(intptr));
extraDataSize += (1 + stackTraceCount) * sizeof(intptr);
extraDataSize += (1 + stackTraceCount) * sizeof(intptr) + 2;
extraDataSize += *(uint16*)((uint8*)spanPtr + elementSize - sizeof(intptr) - sizeof(intptr) - stackTraceCount * sizeof(intptr) - 2);
}
struct MarkTarget
@ -132,17 +134,21 @@ void BFGC::RawMarkSpan(tcmalloc_raw::Span* span, int expectedStartPage)
};
typedef void(MarkTarget::*MarkFunc)();
MarkFunc markFunc = *(MarkFunc*)&rawAllocData->mMarkFunc;
// It's possible we can overestimate elemCount, particularly for large allocations. This doesn't cause a problem
// because we can safely mark on complete random memory -- pointer values are always validated before being followed
intptr elemStride = BF_ALIGN(rawAllocData->mType->mSize, rawAllocData->mType->mAlign);
if (elemStride > 0)
auto typeData = rawAllocData->mType->GetTypeData();
if (typeData != NULL)
{
intptr dataSize = elementSize - extraDataSize;
intptr elemCount = dataSize / elemStride;
for (intptr elemIdx = 0; elemIdx < elemCount; elemIdx++)
// It's possible we can overestimate elemCount, particularly for large allocations. This doesn't cause a problem
// because we can safely mark on complete random memory -- pointer values are always validated before being followed
intptr elemStride = BF_ALIGN(typeData->mSize, typeData->mAlign);
if (elemStride > 0)
{
(((MarkTarget*)((uint8*)spanPtr + elemIdx * elemStride))->*markFunc)();
intptr dataSize = elementSize - extraDataSize;
intptr elemCount = dataSize / elemStride;
for (intptr elemIdx = 0; elemIdx < elemCount; elemIdx++)
{
(((MarkTarget*)((uint8*)spanPtr + elemIdx * elemStride))->*markFunc)();
}
}
}
}
@ -282,13 +288,13 @@ void BFGC::RawReportHandleSpan(tcmalloc_raw::Span* span, int expectedStartPage,
if (rawAllocData->mType != NULL)
{
intptr typeSize;
if ((gBfRtDbgFlags & BfRtFlags_ObjectHasDebugFlags) != 0)
typeSize = rawAllocData->mType->mSize;
else
typeSize = ((bf::System::Type_NOFLAGS*)rawAllocData->mType)->mSize;
if (typeSize > 0)
rawLeakInfo.mDataCount = (elementSize - extraDataSize) / typeSize;
bf::System::Type_NOFLAGS* typeData = rawAllocData->mType->GetTypeData();
if (typeData != NULL)
{
intptr typeSize = typeData->mSize;
if (typeSize > 0)
rawLeakInfo.mDataCount = (elementSize - extraDataSize) / typeSize;
}
}
else
rawLeakInfo.mDataCount = 1;
@ -558,8 +564,8 @@ void* BfRawAllocate(intptr size, bf::System::DbgRawAllocData* rawAllocData, void
markOffsetPtr = (uint16*)((uint8*)result + totalSize - sizeof(intptr) - sizeof(intptr) - 2);
}
else if (rawAllocData->mMaxStackTrace > 1)
{
memcpy((uint8*)result + totalSize - sizeof(intptr) - sizeof(intptr), &stackTraceCount, sizeof(intptr));
{
*(intptr*)((uint8*)result + totalSize - sizeof(intptr) - sizeof(intptr)) = stackTraceCount;
memcpy((uint8*)result + totalSize - sizeof(intptr) - sizeof(intptr) - stackTraceCount*sizeof(intptr), stackTraceInfo, stackTraceCount*sizeof(intptr));
markOffsetPtr = (uint16*)((uint8*)result + totalSize - sizeof(intptr) - sizeof(intptr) - stackTraceCount * sizeof(intptr) - 2);
}

View file

@ -59,6 +59,8 @@ namespace bf
{
namespace System
{
class Type_NOFLAGS;
struct DbgRawAllocData
{
Type* mType;
@ -84,7 +86,7 @@ namespace bf
void*(*Alloc)(intptr size);
void(*Free)(void* ptr);
void(*Object_Delete)(bf::System::Object* obj);
void* mUnused0;
Type_NOFLAGS*(*ClassVData_GetTypeDataPtr)(bf::System::ClassVData* classVData);
bf::System::Type* (*Object_GetType)(bf::System::Object* obj);
void(*Object_GCMarkMembers)(bf::System::Object* obj);
bf::System::Object* (*Object_DynamicCastToTypeId)(bf::System::Object* obj, int typeId);
@ -196,28 +198,25 @@ namespace bf
typedef int32 TypeId;
enum BfTypeFlags : uint32
{
BfTypeFlag_HasAppendWantMark = 0x800000
};
class Type : public Object
{
public:
int32 mSize;
TypeId mTypeId;
TypeId mBoxedId;
uint16 mTypeFlags;
int32 mMemberDataOffset;
uint8 mTypeCode;
uint8 mAlign;
public:
Beefy::String GetFullName();
Type_NOFLAGS* GetTypeData();
};
class Type_NOFLAGS
{
public:
intptr mClassVData;
public:
int32 mSize;
TypeId mTypeId;
TypeId mBoxedId;
uint16 mTypeFlags;
BfTypeFlags mTypeFlags;
int32 mMemberDataOffset;
uint8 mTypeCode;
uint8 mAlign;

View file

@ -20,3 +20,11 @@ Beefy::String bf::System::Type::GetFullName()
BFRTCALLBACKS.Object_Delete(strObj);
return str;
}
bf::System::Type_NOFLAGS* bf::System::Type::GetTypeData()
{
if ((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0)
return BFRTCALLBACKS.ClassVData_GetTypeDataPtr((bf::System::ClassVData*)(mClassVData & ~0xFF));
else
return BFRTCALLBACKS.ClassVData_GetTypeDataPtr((bf::System::ClassVData*)(mClassVData));
}

View file

@ -1232,6 +1232,7 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild
typeInst->mHasPackingHoles = false;
typeInst->mWantsGCMarking = false;
typeInst->mHasDeclError = false;
typeInst->mHasAppendWantMark = false;
delete typeInst->mTypeInfoEx;
typeInst->mTypeInfoEx = NULL;

View file

@ -2067,6 +2067,15 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString)
BfMethodDef* strictEqualsMethod = NULL;
bool needsStaticInit = false;
if (mCurTypeDef->IsExtension())
needsDefaultCtor = false;
bool needsDtor = false;
bool needsStaticDtor = false;
bool hasStaticField = false;
bool hasNonStaticField = false;
bool hasThreadStatics = false;
for (int methodIdx = 0; methodIdx < (int)mCurTypeDef->mMethods.size(); methodIdx++)
{
auto method = mCurTypeDef->mMethods[methodIdx];
@ -2080,6 +2089,9 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString)
if (method->mMethodType == BfMethodType_Ctor)
{
if (method->HasAppend())
needsDtor = true;
if (method->mIsStatic)
{
if ((staticCtor != NULL) && (staticCtor->mMethodDeclaration != NULL))
@ -2239,15 +2251,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString)
if ((method->mImportKind == BfImportKind_Import_Dynamic) || (method->mImportKind == BfImportKind_Import_Unknown))
needsStaticInit = true;
}
if (mCurTypeDef->IsExtension())
needsDefaultCtor = false;
bool needsDtor = false;
bool needsStaticDtor = false;
bool hasStaticField = false;
bool hasNonStaticField = false;
bool hasThreadStatics = false;
for (auto field : mCurTypeDef->mFields)
{
if (field->mIsStatic)

View file

@ -7492,7 +7492,7 @@ void BfExprEvaluator::PushThis(BfAstNode* targetSrc, BfTypedValue argVal, BfMeth
else
allowThisSplatting = methodInstance->AllowsSplatting(-1);
if ((!allowThisSplatting) || (methodDef->mIsMutating))
if ((!allowThisSplatting) || (methodDef->mIsMutating) || (methodInstance->mCallingConvention == BfCallingConvention_Cdecl))
{
argVal = mModule->MakeAddressable(argVal);
irArgs.push_back(argVal.mValue);
@ -16569,7 +16569,8 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs
int allocAlign = resolvedTypeRef->mAlign;
if (typeInstance != NULL)
allocAlign = typeInstance->mInstAlign;
int appendAllocAlign = 0;
int appendAllocAlign = 0;
BfAllocFlags allocFlags = BfAllocFlags_None;
if ((bindResult.mMethodInstance != NULL) && (bindResult.mMethodInstance->mMethodDef->HasAppend()))
{
@ -16605,6 +16606,9 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs
appendSizeValue = appendSizeTypedValue.mValue;
allocAlign = BF_MAX(allocAlign, calcAppendMethodModule.mMethodInstance->mAppendAllocAlign);
appendAllocAlign = calcAppendMethodModule.mMethodInstance->mAppendAllocAlign;
if (calcAppendMethodModule.mMethodInstance->mHasAppendWantMark)
allocFlags = (BfAllocFlags)(allocFlags | BfAllocFlags_HasAppendWantMark);
}
if (appendAllocAlign != 0)
@ -16616,6 +16620,12 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs
appendSizeValue = mModule->mBfIRBuilder->CreateAdd(appendSizeValue, mModule->GetConstValue(extraSize));
}
}
if ((allocFlags & BfAllocFlags_HasAppendWantMark) != 0)
{
int markInfoSize = sizeof(intptr) + sizeof(intptr) * 4; // Stack trace, MarkAppendEntry
appendSizeValue = mModule->mBfIRBuilder->CreateAdd(appendSizeValue, mModule->GetConstValue(markInfoSize));
}
}
// WTF? I'm not even sure this is correct - add more tests
@ -16635,7 +16645,7 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs
}
else
{
allocValue = mModule->AllocFromType(resolvedTypeRef, allocTarget, appendSizeValue, BfIRValue(), 0, BfAllocFlags_None, allocAlign);
allocValue = mModule->AllocFromType(resolvedTypeRef, allocTarget, appendSizeValue, BfIRValue(), 0, allocFlags, allocAlign);
}
if (((mBfEvalExprFlags & BfEvalExprFlags_Comptime) != 0) && (mModule->mCompiler->mCeMachine != NULL))
{
@ -16710,14 +16720,21 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs
{
auto impMethodInstance = (BfMethodInstance*)vtableEntry.mImplementingMethod;
bool needsCall = false;
if (impMethodInstance != NULL)
{
needsCall = impMethodInstance->mMethodDef->mBody != NULL;
}
else
{
if (allocFlags & BfAllocFlags_HasAppendWantMark)
needsCall = true;
BF_ASSERT(vtableEntry.mImplementingMethod.mKind == BfMethodRefKind_AmbiguousRef);
if (!needsCall)
{
if (impMethodInstance != NULL)
{
needsCall = impMethodInstance->mMethodDef->mBody != NULL;
}
else
{
needsCall = true;
BF_ASSERT(vtableEntry.mImplementingMethod.mKind == BfMethodRefKind_AmbiguousRef);
}
}
if (!needsCall)

View file

@ -475,6 +475,7 @@ public:
BfTypedValue mConstAccum;
bool mFailed;
bool mIsFirstConstPass;
bool mHasAppendWantMark;
int mCurAppendAlign;
public:
@ -482,6 +483,7 @@ public:
{
mFailed = false;
mIsFirstConstPass = false;
mHasAppendWantMark = false;
mCurAppendAlign = 1;
}
@ -654,6 +656,9 @@ public:
return;
}
if (origResolvedTypeRef->WantsGCMarking())
mHasAppendWantMark = true;
bool isGenericParam = origResolvedTypeRef->IsGenericParam();
auto resolvedTypeRef = origResolvedTypeRef;
auto resultType = resolvedTypeRef;
@ -6272,6 +6277,8 @@ BfIRValue BfModule::GetTypeTypeData(BfType* type, BfCreateTypeDataContext& ctx,
typeFlags |= BfTypeFlags_Static;
if ((typeInstance != NULL) && (typeInstance->mTypeDef->mIsAbstract))
typeFlags |= BfTypeFlags_Abstract;
if ((typeInstance != NULL) && (typeInstance->mHasAppendWantMark))
typeFlags |= BfTypeFlags_HasAppendWantMark;
return typeTypeData;
}
@ -9534,7 +9541,7 @@ BfTypedValue BfModule::GetOrCreateVarAddr(BfExpression* expr)
}
// Clear memory, set classVData, call init. Actual ctor is called elsewhere.
void BfModule::InitTypeInst(BfTypedValue typedValue, BfScopeData* scopeData, bool zeroMemory, BfIRValue sizeValue)
void BfModule::InitTypeInst(BfTypedValue typedValue, BfScopeData* scopeData, bool zeroMemory, BfIRValue sizeValue, BfAllocFlags allocFlags)
{
auto typeInstance = typedValue.mType->ToTypeInstance();
if (zeroMemory)
@ -9581,6 +9588,8 @@ void BfModule::InitTypeInst(BfTypedValue typedValue, BfScopeData* scopeData, boo
SizedArray<BfIRValue, 4> llvmArgs;
llvmArgs.push_back(objectPtr);
llvmArgs.push_back(vDataRef);
llvmArgs.push_back(sizeValue);
llvmArgs.push_back(mBfIRBuilder->CreateConst(BfTypeCode_Int8, allocFlags));
auto objectStackInitMethod = GetInternalMethod("Dbg_ObjectStackInit");
if (objectStackInitMethod)
@ -10213,7 +10222,7 @@ BfIRValue BfModule::AllocFromType(BfType* type, const BfAllocTarget& allocTarget
doCondAlloca = !wasDynAlloc && isDynAlloc && mCurMethodState->mInConditionalBlock;
AddStackAlloc(typedVal, arraySize, NULL, scopeData, doCondAlloca, true);
}
InitTypeInst(typedVal, scopeData, zeroMemory, sizeValue);
InitTypeInst(typedVal, scopeData, zeroMemory, sizeValue, allocFlags);
return typedVal.mValue;
}
@ -10443,7 +10452,8 @@ BfIRValue BfModule::AllocFromType(BfType* type, const BfAllocTarget& allocTarget
llvmArgs.push_back(sizeValue);
llvmArgs.push_back(GetConstValue(typeInstance->mAlign));
llvmArgs.push_back(GetConstValue(stackCount));
auto moduleMethodInstance = GetInternalMethod("Dbg_ObjectAlloc", 4);
llvmArgs.push_back(GetConstValue(allocFlags, GetPrimitiveType(BfTypeCode_Int8)));
auto moduleMethodInstance = GetInternalMethod("Dbg_ObjectAlloc", 5);
BfIRValue objectVal = mBfIRBuilder->CreateCall(moduleMethodInstance.mFunc, llvmArgs);
result = mBfIRBuilder->CreateBitCast(objectVal, mBfIRBuilder->MapType(typeInstance));
}
@ -10647,6 +10657,9 @@ BfIRValue BfModule::AppendAllocFromType(BfType* type, BfIRValue appendSizeValue,
BfIRType toType;
if (typeInst != NULL)
{
mBfIRBuilder->PopulateType(typeInst);
if (typeInst->WantsGCMarking())
mCurTypeInstance->mHasAppendWantMark = true;
EmitAppendAlign(typeInst->mInstAlign, typeInst->mInstSize);
sizeValue = GetConstValue(typeInst->mInstSize);
toType = mBfIRBuilder->MapTypeInstPtr(typeInst);
@ -10665,6 +10678,35 @@ BfIRValue BfModule::AppendAllocFromType(BfType* type, BfIRValue appendSizeValue,
retTypeInstance = typeInst;
retValue = mBfIRBuilder->CreateIntToPtr(curIdxVal, toType);
if ((typeInst != NULL) && (typeInst->WantsGCMarking()) && (mCompiler->mOptions.mDebugAlloc))
{
auto curThis = GetThis();
auto thisObj = Cast(mCurMethodInstance->mMethodDef->GetRefNode(), curThis, mContext->mBfObjectType);
if (typeInst->IsObject())
{
auto appendedObj = Cast(mCurMethodInstance->mMethodDef->GetRefNode(), BfTypedValue(retValue, typeInst), mContext->mBfObjectType);
BfModuleMethodInstance allocMethod = GetInternalMethod("Dbg_ObjectAppended", 2);
SizedArray<BfIRValue, 2> llvmArgs;
llvmArgs.push_back(thisObj.mValue);
llvmArgs.push_back(appendedObj.mValue);
mBfIRBuilder->CreateCall(allocMethod.mFunc, llvmArgs);
}
else
{
// Raw
BfIRValue allocPtr = mBfIRBuilder->CreateBitCast(retValue, mBfIRBuilder->MapType(voidPtrType));
BfIRValue allocData = GetDbgRawAllocData(type);
BfModuleMethodInstance allocMethod = GetInternalMethod("Dbg_RawAppended", 3);
SizedArray<BfIRValue, 3> llvmArgs;
llvmArgs.push_back(thisObj.mValue);
llvmArgs.push_back(allocPtr);
llvmArgs.push_back(allocData);
mBfIRBuilder->CreateCall(allocMethod.mFunc, llvmArgs);
}
}
}
if ((retTypeInstance != NULL) && (retTypeInstance->IsObject()))
@ -15871,7 +15913,7 @@ BfTypedValue BfModule::GetThis(bool markUsing)
auto curMethodOwner = mCurMethodInstance->mMethodInstanceGroup->mOwner;
if ((curMethodOwner->IsStruct()) || (curMethodOwner->IsTypedPrimitive()))
{
if ((localDef->mResolvedType->IsTypedPrimitive()) && (!mCurMethodInstance->mMethodDef->mIsMutating))
if ((localDef->mResolvedType->IsTypedPrimitive()) && (!mCurMethodInstance->mMethodDef->mIsMutating) && (mCurMethodInstance->mCallingConvention != BfCallingConvention_Cdecl))
{
return BfTypedValue(thisValue, useMethodState->mLocals[0]->mResolvedType, BfTypedValueKind_ReadOnlyThisValue);
}
@ -17411,12 +17453,15 @@ BfTypedValue BfModule::TryConstCalcAppend(BfMethodInstance* methodInst, SizedArr
appendAllocVisitor.mFailed = true;
if (!appendAllocVisitor.mFailed)
constValue = appendAllocVisitor.mConstAccum;
if (appendAllocVisitor.mHasAppendWantMark)
methodInst->mHasAppendWantMark = true;
if (isFirstRun)
{
mCurMethodInstance->mEndingAppendAllocAlign = appendAllocVisitor.mCurAppendAlign;
if (mCurMethodInstance->mAppendAllocAlign <= 0)
mCurMethodInstance->mAppendAllocAlign = 1;
}
}
if (isFirstRun)
{
@ -18090,6 +18135,19 @@ void BfModule::EmitDtorBody()
}
}
}
// If there are appends then we just need the rootmost append type to do the Dbg_AppendDeleted
if ((!mIsComptimeModule) && (!methodDef->mIsStatic) && (mCompiler->mOptions.mDebugAlloc) &&
(mCurTypeInstance->HasAppendCtor()) && (!mCurTypeInstance->BaseHasAppendCtor()))
{
auto thisValue = GetThis();
auto appendedObj = mBfIRBuilder->CreateBitCast(thisValue.mValue, mBfIRBuilder->MapType(mContext->mBfObjectType));
BfModuleMethodInstance allocMethod = GetInternalMethod("Dbg_AppendDeleted", 1);
SizedArray<BfIRValue, 1> llvmArgs;
llvmArgs.push_back(appendedObj);
if (allocMethod)
mBfIRBuilder->CreateCall(allocMethod.mFunc, llvmArgs);
}
}
BfIRValue BfModule::CreateDllImportGlobalVar(BfMethodInstance* methodInstance, bool define)
@ -20534,6 +20592,19 @@ void BfModule::EmitGCMarkMembers()
CallChainedMethods(mCurMethodInstance, false);
}
}
// If there are appends then we just need the rootmost append type to do the Dbg_MarkAppended
if ((!mIsComptimeModule) && (!methodDef->mIsStatic) && (mCompiler->mOptions.mDebugAlloc) &&
(mCurTypeInstance->HasAppendCtor()) && (!mCurTypeInstance->BaseHasAppendCtor()))
{
auto thisValue = GetThis();
auto appendedObj = mBfIRBuilder->CreateBitCast(thisValue.mValue, mBfIRBuilder->MapType(mContext->mBfObjectType));
BfModuleMethodInstance allocMethod = GetInternalMethod("Dbg_MarkAppended", 1);
SizedArray<BfIRValue, 1> llvmArgs;
llvmArgs.push_back(appendedObj);
if (allocMethod)
mBfIRBuilder->CreateCall(allocMethod.mFunc, llvmArgs);
}
}
void BfModule::EmitGCFindTLSMembers()
@ -21452,7 +21523,8 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup,
else
{
bool wantPtr = (thisType->IsComposite()) && (!paramVar->mIsLowered);
if ((thisType->IsTypedPrimitive()) && (methodDef->HasNoThisSplat()))
if ((thisType->IsTypedPrimitive()) &&
((methodDef->HasNoThisSplat()) || (methodInstance->mCallingConvention == BfCallingConvention_Cdecl)))
wantPtr = true;
if (wantPtr)
@ -21582,7 +21654,8 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup,
{
diType = mBfIRBuilder->DbgGetType(paramVar->mResolvedType);
bool wantRef = paramVar->mResolvedType->IsComposite();
if ((paramVar->mResolvedType->IsTypedPrimitive()) && (methodDef->HasNoThisSplat()))
if ((paramVar->mResolvedType->IsTypedPrimitive()) &&
((methodDef->HasNoThisSplat()) || (methodInstance->mCallingConvention == BfCallingConvention_Cdecl)))
wantRef = true;
if (wantRef)
@ -23897,6 +23970,12 @@ void BfModule::GetMethodCustomAttributes(BfMethodInstance* methodInstance)
}
methodInstance->mCallingConvention = methodDef->mCallingConvention;
if ((typeInstance->IsValueType()) && (methodDef->mIsOverride) && (methodDef->mName == BF_METHODNAME_MARKMEMBERS))
{
// Make sure we we pass 'this' as a pointer into GCMarkMembers so it's compatible with the mark function pointer
methodInstance->mCallingConvention = BfCallingConvention_Cdecl;
}
if (customAttributes != NULL)
{
auto linkNameAttr = customAttributes->Get(mCompiler->mCallingConventionAttributeTypeDef);

View file

@ -128,10 +128,11 @@ enum BfCastResultFlags : int8
enum BfAllocFlags : int8
{
BfAllocFlags_None = 0,
BfAllocFlags_RawArray = 1,
BfAllocFlags_ZeroMemory = 2,
BfAllocFlags_NoDtorCall = 4,
BfAllocFlags_NoDefaultToMalloc = 8
BfAllocFlags_HasAppendWantMark = 1,
BfAllocFlags_RawArray = 2,
BfAllocFlags_ZeroMemory = 4,
BfAllocFlags_NoDtorCall = 8,
BfAllocFlags_NoDefaultToMalloc = 0x10,
};
enum BfProtectionCheckFlags : int8
@ -1736,7 +1737,7 @@ public:
void CleanupFileInstances();
void AssertErrorState();
void AssertParseErrorState();
void InitTypeInst(BfTypedValue typedValue, BfScopeData* scope, bool zeroMemory, BfIRValue dataSize);
void InitTypeInst(BfTypedValue typedValue, BfScopeData* scope, bool zeroMemory, BfIRValue dataSize, BfAllocFlags allocFlags = BfAllocFlags_None);
bool IsAllocatorAligned();
BfIRValue AllocBytes(BfAstNode* refNode, const BfAllocTarget& allocTarget, BfType* type, BfIRValue sizeValue, BfIRValue alignValue, BfAllocFlags allocFlags/*bool zeroMemory, bool defaultToMalloc*/);
BfIRValue GetMarkFuncPtr(BfType* type);

View file

@ -1381,11 +1381,6 @@ int BfMethodInstance::DbgGetVirtualMethodNum()
void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType, SizedArrayImpl<BfIRType>& paramTypes, bool forceStatic)
{
if (mMethodDef->mName == "Test4")
{
NOP;
}
BfModule* resolveModule = module->mContext->mUnreifiedModule;
resolveModule->PopulateType(mReturnType);
@ -1458,7 +1453,11 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType,
bool doSplat = false;
if (paramIdx == -1)
{
if ((!mMethodDef->mIsMutating) && (checkType->IsTypedPrimitive()))
if (mCallingConvention == BfCallingConvention_Cdecl)
{
// Pass by pointer even for typed primitives
}
else if ((!mMethodDef->mIsMutating) && (checkType->IsTypedPrimitive()))
{
checkType = checkType->GetUnderlyingType();
}
@ -2468,6 +2467,22 @@ bool BfTypeInstance::IsAnonymousInitializerType()
return (mTypeDef->mTypeDeclaration != NULL) && (mTypeDef->mTypeDeclaration->IsAnonymousInitializerType());
}
bool BfTypeInstance::HasAppendCtor()
{
return mTypeDef->mHasAppendCtor;
}
bool BfTypeInstance::BaseHasAppendCtor()
{
if (mBaseType != NULL)
{
if (mBaseType->HasAppendCtor())
return true;
return mBaseType->BaseHasAppendCtor();
}
return false;
}
void BfTypeInstance::ReportMemory(MemReporter* memReporter)
{
if (mGenericTypeInfo != NULL)

View file

@ -908,6 +908,7 @@ public:
bool mInCEMachine:1;
bool mCeCancelled:1;
bool mIsDisposed:1;
bool mHasAppendWantMark:1;
BfMethodChainType mChainType;
BfComptimeFlags mComptimeFlags;
BfCallingConvention mCallingConvention;
@ -951,6 +952,7 @@ public:
mInCEMachine = false;
mCeCancelled = false;
mIsDisposed = false;
mHasAppendWantMark = false;
mChainType = BfMethodChainType_None;
mComptimeFlags = BfComptimeFlag_None;
mCallingConvention = BfCallingConvention_Unspecified;
@ -2064,6 +2066,7 @@ public:
bool mHasPackingHoles;
bool mWantsGCMarking;
bool mHasDeclError;
bool mHasAppendWantMark;
public:
BfTypeInstance()
@ -2116,6 +2119,7 @@ public:
mWantsGCMarking = false;
mHasParameterizedBase = false;
mHasDeclError = false;
mHasAppendWantMark = false;
mMergedFieldDataCount = 0;
mConstHolder = NULL;
}
@ -2213,6 +2217,8 @@ public:
bool DefineStateAllowsStaticMethods() { return mDefineState >= BfTypeDefineState_HasInterfaces_Direct; }
bool IsAnonymous();
bool IsAnonymousInitializerType();
bool HasAppendCtor();
bool BaseHasAppendCtor();
virtual void ReportMemory(MemReporter* memReporter) override;
};

View file

@ -239,6 +239,7 @@ enum BfTypeFlags
BfTypeFlags_Static = 0x200000,
BfTypeFlags_Abstract = 0x400000,
BfTypeFlags_HasAppendWantMark = 0x800000,
};
enum BfMethodFlags

View file

@ -8513,7 +8513,7 @@ String WinDebugger::DbgTypedValueToString(const DbgTypedValue& origTypedValue, c
{
addr_target objectSize = ReadMemory<addr_target>(ptrVal + sizeof(addr_target));
addr_target largeAllocInfo = ReadMemory<addr_target>(ptrVal + objectSize);
stackTraceLen = largeAllocInfo & 0xFFFF;
stackTraceLen = (largeAllocInfo >> 8) & 0xFFFF;
stackTraceAddr = ptrVal + objectSize + sizeof(addr_target);
}
else if ((bfObjectFlags & BfObjectFlag_AllocInfo_Short) != 0)