Browse Source

Support for marking append-allocated objects (mHasAppendWantMark)

Brian Fiete 7 months ago
parent
commit
9baf0ead21

+ 207 - 15
BeefLibs/corlib/src/Internal.bf

@@ -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()

+ 1 - 1
BeefLibs/corlib/src/Reflection/TypeInstance.bf

@@ -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);

+ 12 - 1
BeefLibs/corlib/src/Runtime.bf

@@ -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;

+ 2 - 1
BeefLibs/corlib/src/Type.bf

@@ -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

+ 39 - 11
BeefRT/dbg/DbgInternal.cpp

@@ -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
 }
 

+ 5 - 2
BeefRT/dbg/gc.cpp

@@ -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);

+ 27 - 21
BeefRT/dbg/gc_raw.cpp

@@ -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);
 	}

+ 12 - 13
BeefRT/rt/BfObjects.h

@@ -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;
 
-		class Type : public Object
+		enum BfTypeFlags : uint32
 		{
-		public:
-			int32 mSize;
-			TypeId mTypeId;
-			TypeId mBoxedId;
-			uint16 mTypeFlags;
-			int32 mMemberDataOffset;
-			uint8 mTypeCode;
-			uint8 mAlign;
+			BfTypeFlag_HasAppendWantMark = 0x800000
+		};
 
+		class Type : public Object
+		{
+		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;

+ 8 - 0
BeefRT/rt/Object.cpp

@@ -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));
+}

+ 1 - 0
IDEHelper/Compiler/BfContext.cpp

@@ -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;
 

+ 13 - 9
IDEHelper/Compiler/BfDefBuilder.cpp

@@ -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)

+ 27 - 10
IDEHelper/Compiler/BfExprEvaluator.cpp

@@ -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)

+ 86 - 7
IDEHelper/Compiler/BfModule.cpp

@@ -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);

+ 6 - 5
IDEHelper/Compiler/BfModule.h

@@ -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);

+ 21 - 6
IDEHelper/Compiler/BfResolvedTypeUtils.cpp

@@ -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)

+ 6 - 0
IDEHelper/Compiler/BfResolvedTypeUtils.h

@@ -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;
 };

+ 1 - 0
IDEHelper/Compiler/BfSystem.h

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

+ 1 - 1
IDEHelper/WinDebugger.cpp

@@ -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)