1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-25 19:18:01 +02:00
Beef/BeefLibs/corlib/src/Runtime.bf
2025-01-17 10:17:19 -08:00

763 lines
No EOL
19 KiB
Beef

using System.Threading;
using System.Collections;
#if (BF_ENABLE_OBJECT_DEBUG_FLAGS || BF_DEBUG_ALLOC) && !BF_RUNTIME_DISABLE
#define BF_DBG_RUNTIME
#endif
using internal System.Threading.Thread;
namespace System
{
struct RuntimeFeatures
{
public bool SSE, SSE2;
public bool AVX, AVX2, AVX512;
}
[StaticInitPriority(201)]
static class Runtime
{
const int32 cVersion = 10;
[CRepr]
struct BfDebugMessageData
{
enum MessageType : int32
{
None = 0,
Error = 1,
ProfilerCmd = 2
};
MessageType mMessageType;
int32 mStackWindbackCount;
int32 mBufferParamLen;
char8* mBufferParam;
void* mPCOverride;
char8* mBufferPtr = null;
int mStrSize = 0;
[CLink]
public static BfDebugMessageData gBfDebugMessageData;
public static ~this()
{
if (gBfDebugMessageData.mBufferPtr != null)
{
Internal.Free(gBfDebugMessageData.mBufferPtr);
gBfDebugMessageData.mBufferPtr = null;
gBfDebugMessageData.mStrSize = 0;
}
}
public void SetupError(char8* str, int32 stackWindbackCount = 0) mut
{
mMessageType = .Error;
mStackWindbackCount = stackWindbackCount;
int size = Internal.CStrLen(str) + 1;
if (mStrSize < size)
{
if (mBufferPtr != null)
Internal.Free(mBufferPtr);
mStrSize = size;
mBufferPtr = (char8*)Internal.Malloc(mStrSize);
}
Internal.MemCpy(mBufferPtr, str, size);
mBufferParam = mBufferPtr;
mBufferParamLen = (int32)size - 1;
mPCOverride = null;
}
public void SetupProfilerCmd(char8* str) mut
{
mMessageType = .ProfilerCmd;
mStackWindbackCount = 0;
int size = Internal.CStrLen(str) + 1;
if (mStrSize < size)
{
if (mBufferPtr != null)
Internal.Free(mBufferPtr);
mStrSize = size;
mBufferPtr = (char8*)Internal.Malloc(mStrSize);
}
Internal.MemCpy(mBufferPtr, str, size);
mBufferParam = mBufferPtr;
mBufferParamLen = (int32)size - 1;
mPCOverride = null;
}
public void Fatal() mut
{
var str = scope String();
str.Reference(mBufferPtr, mBufferParamLen, 0);
Internal.FatalError(str, -1);
}
public void Clear() mut
{
mMessageType = .None;
if (mBufferPtr != null)
mBufferPtr[0] = 0;
mBufferParamLen = 0;
}
}
struct BfRtCallbacks
{
public static BfRtCallbacks sCallbacks;
function void* (int size) mAlloc;
function void (void* ptr) mFree;
function void (Object obj) mObject_Delete;
void* mUnused0;
function Type (Object obj) mObject_GetType;
function void (Object obj) mObject_GCMarkMembers;
function Object (Object obj, int32 typeId) mObject_DynamicCastToTypeId;
function void (Type type, String str) mType_GetFullName;
function String () mString_Alloc;
function StringView (String str) mString_ToStringView;
function Object () mThread_Alloc;
function Object () mThread_GetMainThread;
function void (Object thread) mThread_ThreadProc;
function void* (Object thread) mThread_GetInternalThread;
function void (Object thread, void* internalThread) mThread_SetInternalThread;
function bool (Object thread) mThread_IsAutoDelete;
function void (Object thread) mThread_AutoDelete;
function int32 (Object thread) mThread_GetMaxStackSize;
function void () mThread_Exiting;
function void () mGC_MarkAllStaticMembers;
function bool () mGC_CallRootCallbacks;
function void () mGC_Shutdown;
function void (char8* str) mSetErrorString;
function void (char8* str, int32 stackWindbackCount) mDebugMessageData_SetupError;
function void (char8* str) mDebugMessageData_SetupProfilerCmd;
function void () mDebugMessageData_Fatal;
function void () mDebugMessageData_Clear;
function int32 (char8* kind, char8* arg1, char8* arg2, int arg3) mCheckErrorHandler;
static void* Alloc(int size)
{
return Internal.Malloc(size);
}
static void Free(void* ptr)
{
Internal.Free(ptr);
}
static void Object_Delete(Object obj)
{
delete obj;
}
static Type Object_GetType(Object obj)
{
#if BF_DBG_RUNTIME
return obj.[Friend, DisableObjectAccessChecks]RawGetType();
#else
return null;
#endif
}
static void Object_GCMarkMembers(Object obj)
{
#if BF_ENABLE_REALTIME_LEAK_CHECK
obj.[Friend, DisableObjectAccessChecks]GCMarkMembers();
#endif
}
static Object Object_DynamicCastToTypeId(Object obj, int32 typeId)
{
#if BF_DYNAMIC_CAST_CHECK
return obj.DynamicCastToTypeId(typeId);
#else
return null;
#endif
}
static void Type_GetFullName(Type type, String str)
{
#if BF_DBG_RUNTIME
type.ToString(str);
#else
//
#endif
}
static String String_Alloc()
{
return new String();
}
static StringView String_ToStringView(String str)
{
return str;
}
static void GC_MarkAllStaticMembers()
{
#if BF_ENABLE_REALTIME_LEAK_CHECK
GC.[Friend]MarkAllStaticMembers();
#endif
}
static bool GC_CallRootCallbacks()
{
#if BF_ENABLE_REALTIME_LEAK_CHECK
return GC.[Friend]CallRootCallbacks();
#else
return true;
#endif
}
static void GC_Shutdown()
{
#if BF_DBG_RUNTIME
GC.Shutdown();
#endif
}
static void DebugMessageData_SetupError(char8* str, int32 stackWindbackCount)
{
#if !BF_RUNTIME_REDUCED
BfDebugMessageData.gBfDebugMessageData.SetupError(str, stackWindbackCount);
#endif
}
static void DebugMessageData_SetupProfilerCmd(char8* str)
{
#if !BF_RUNTIME_REDUCED
BfDebugMessageData.gBfDebugMessageData.SetupProfilerCmd(str);
#endif
}
static void DebugMessageData_Fatal()
{
#if !BF_RUNTIME_REDUCED
BfDebugMessageData.gBfDebugMessageData.Fatal();
#endif
}
static void DebugMessageData_Clear()
{
#if !BF_RUNTIME_REDUCED
BfDebugMessageData.gBfDebugMessageData.Clear();
#endif
}
static int32 CheckErrorHandle(char8* kind, char8* arg1, char8* arg2, int arg3)
{
if (Runtime.CheckErrorHandler != null)
return Runtime.CheckErrorHandler(kind, arg1, arg2, arg3);
return 0;
}
public void Init() mut
{
mAlloc = => Alloc;
mFree = => Free;
mObject_Delete = => Object_Delete;
mObject_GetType = => Object_GetType;
mObject_GCMarkMembers = => Object_GCMarkMembers;
mObject_DynamicCastToTypeId = => Object_DynamicCastToTypeId;
mType_GetFullName = => Type_GetFullName;
mString_Alloc = => String_Alloc;
mString_ToStringView = => String_ToStringView;
mGC_MarkAllStaticMembers = => GC_MarkAllStaticMembers;
mGC_CallRootCallbacks = => GC_CallRootCallbacks;
mGC_Shutdown = => GC_Shutdown;
mSetErrorString = => SetErrorString;
mDebugMessageData_SetupError = => DebugMessageData_SetupError;
mDebugMessageData_SetupProfilerCmd = => DebugMessageData_SetupProfilerCmd;
mDebugMessageData_Fatal = => DebugMessageData_Fatal;
mDebugMessageData_Clear = => DebugMessageData_Clear;
mCheckErrorHandler = => CheckErrorHandler;
}
};
#if !BF_RUNTIME_DISABLE
private static extern void Init(int32 version, int32 flags, BfRtCallbacks* callbacks);
private static extern void InitCrashCatcher(int32 flags);
private static extern void ShutdownCrashCatcher();
private static extern void AddCrashInfoFunc(void* func);
private static extern void Dbg_Init(int32 version, int32 flags, BfRtCallbacks* callbacks);
private static extern void SetErrorString(char8* error);
private static extern void* Dbg_GetCrashInfoFunc();
public static extern void SetCrashReportKind(RtCrashReportKind crashReportKind);
#else
private static void Init(int32 version, int32 flags, BfRtCallbacks* callbacks) {}
private static void AddCrashInfoFunc(void* func) {}
private static void Dbg_Init(int32 version, int32 flags, BfRtCallbacks* callbacks) {}
private static void SetErrorString(char8* error) {}
private static void* Dbg_GetCrashInfoFunc() => null;
public static void SetCrashReportKind(RtCrashReportKind crashReportKind) {}
#endif
public enum RtCrashReportKind : int32
{
Default,
GUI,
Console,
PrintOnly,
None,
System
}
enum RtFlags : int32
{
ObjectHasDebugFlags = 1,
LeakCheck = 2,
SilentCrash = 4,
DebugAlloc = 8,
NoThreadExitWait = 0x10
}
public enum ErrorHandlerResult
{
ContinueFailure,
Ignore,
}
public class Error
{
}
public class FatalError : Error
{
public String mError ~ delete _;
}
public class LoadSharedLibraryError : Error
{
public String mPath ~ delete _;
}
public class GetSharedProcAddressError : Error
{
public String mPath ~ delete _;
public String mProcName ~ delete _;
}
public class AssertError : Error
{
public enum Kind
{
Debug,
Runtime,
Test
}
public Kind mKind;
public String mError ~ delete _;
public String mFilePath ~ delete _;
public int mLineNum;
public this(Kind kind, String error, String filePath, int lineNum)
{
mKind = kind;
mError = new .(error);
mFilePath = new .(filePath);
mLineNum = lineNum;
}
}
public enum ErrorStage
{
PreFail,
Fail
}
static struct ErrorHandlerData
{
public delegate ErrorHandlerResult ErrorHandler(ErrorStage stage, Error error);
public static AllocWrapper<Monitor> sMonitor ~ _.Dispose();
public static List<ErrorHandler> sErrorHandlers ~ DeleteContainerAndItems!(_);
public static bool sInsideErrorHandler;
}
static RtFlags sExtraFlags;
static bool sQueriedFeatures = false;
static RuntimeFeatures sFeatures;
static function void() sThreadInit;
public static this()
{
#if !BF_RUNTIME_DISABLE
BfRtCallbacks.sCallbacks.Init();
RtFlags flags = sExtraFlags;
#if BF_ENABLE_OBJECT_DEBUG_FLAGS
flags |= .ObjectHasDebugFlags;
#endif
#if BF_ENABLE_REALTIME_LEAK_CHECK
flags |= .LeakCheck;
#endif
#if BF_DEBUG_ALLOC
flags |= .DebugAlloc;
#endif
Init(cVersion, (int32)flags, &BfRtCallbacks.sCallbacks);
#if !BF_RUNTIME_REDUCED && BF_PLATFORM_WINDOWS
InitCrashCatcher((int32)flags);
#endif
#if BF_DBG_RUNTIME
Dbg_Init(cVersion, (int32)flags, &BfRtCallbacks.sCallbacks);
#endif
if (sThreadInit != null)
sThreadInit();
#endif
}
[NoReturn]
public static void FatalError(String msg = "Fatal error encountered", String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum)
{
#if !BF_RUNTIME_REDUCED
String failStr = scope .()..Append(msg, " at line ");
line.ToString(failStr);
failStr.Append(" in ", filePath);
Internal.FatalError(failStr, 1);
#else
Internal.FatalError("Fatal error", 1);
#endif
}
[NoReturn]
public static void NotImplemented(String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum)
{
String failStr = scope .()..Append("Not implemented at line ");
line.ToString(failStr);
failStr.Append(" in ", filePath);
Internal.FatalError(failStr, 1);
}
public static void Assert(bool condition, String error = Compiler.CallerExpression[0], String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum)
{
if (!condition)
{
if ((!Compiler.IsComptime) && (Runtime.CheckAssertError != null) && (Runtime.CheckAssertError(.Runtime, error, filePath, line) == .Ignore))
return;
#if !BF_RUNTIME_REDUCED
String failStr = scope .()..Append("Assert failed: ", error, " at line ");
line.ToString(failStr);
failStr.Append(" in ", filePath);
Internal.FatalError(failStr, 1);
#else
Internal.FatalError("Assert failed", 1);
#endif
}
}
public static void AddErrorHandler(ErrorHandlerData.ErrorHandler handler)
{
if (Compiler.IsComptime)
return;
using (ErrorHandlerData.sMonitor.Val.Enter())
{
if (CheckAssertError == null)
{
CheckAssertError = => CheckAssertError_Impl;
CheckErrorHandler = => CheckErrorHandler_Impl;
}
if (ErrorHandlerData.sErrorHandlers == null)
ErrorHandlerData.sErrorHandlers = new .();
ErrorHandlerData.sErrorHandlers.Add(handler);
}
}
public static Result<void> RemoveErrorHandler(ErrorHandlerData.ErrorHandler handler)
{
if (Compiler.IsComptime)
return .Ok;
using (ErrorHandlerData.sMonitor.Val.Enter())
{
if (ErrorHandlerData.sErrorHandlers.RemoveStrict(handler))
return .Ok;
}
return .Err;
}
public static function ErrorHandlerResult(AssertError.Kind kind, String error, String filePath, int lineNum) CheckAssertError;
public static function int32(char8* kind, char8* arg1, char8* arg2, int arg3) CheckErrorHandler;
public static function void*(char8* filePath) LibraryLoadCallback;
static ErrorHandlerResult CheckAssertError_Impl(AssertError.Kind kind, String error, String filePath, int lineNum)
{
return CheckErrorHandlers(scope AssertError(kind, error, filePath, lineNum));
}
static int32 CheckErrorHandler_Impl(char8* kind, char8* arg1, char8* arg2, int arg3)
{
Error error = null;
switch (StringView(kind))
{
case "FatalError":
error = scope:: FatalError() { mError = new .(arg1) };
case "LoadSharedLibrary":
error = scope:: LoadSharedLibraryError() { mPath = new .(arg1) };
case "GetSharedProcAddress":
error = scope:: GetSharedProcAddressError() { mPath = new .(arg1), mProcName = new .(arg2) };
}
if (error == null)
return 0;
return (int32)CheckErrorHandlers(error);
}
static ErrorHandlerResult CheckErrorHandlers(Error error)
{
if (Compiler.IsComptime)
return .ContinueFailure;
using (ErrorHandlerData.sMonitor.Val.Enter())
{
if (ErrorHandlerData.sInsideErrorHandler)
return .ContinueFailure;
ErrorHandlerData.sInsideErrorHandler = true;
defer { ErrorHandlerData.sInsideErrorHandler = false; }
for (int pass = 0; pass < 2; pass++)
{
int idx = (ErrorHandlerData.sErrorHandlers?.Count).GetValueOrDefault() - 1;
while (idx >= 0)
{
if (idx < ErrorHandlerData.sErrorHandlers.Count)
{
var handler = ErrorHandlerData.sErrorHandlers[idx];
var result = handler((pass == 0) ? .PreFail : .Fail, error);
if (result == .Ignore)
{
if (pass == 1)
Internal.FatalError("Can only ignore error on prefail");
return .Ignore;
}
}
idx--;
}
}
}
return .ContinueFailure;
}
public static RuntimeFeatures Features
{
get
{
if (!sQueriedFeatures)
{
#if BF_MACHINE_X86 || BF_MACHINE_X64
QueryFeaturesX86();
#else
sFeatures = .();
sQueriedFeatures = true;
#endif
}
return sFeatures;
}
}
#if BF_MACHINE_X86 || BF_MACHINE_X64
private static void QueryFeaturesX86()
{
sFeatures = .();
sQueriedFeatures = true;
uint32 _ = 0;
// 0: Basic information
uint32 maxBasicLeaf = 0;
cpuid(0, 0, &maxBasicLeaf, &_, &_, &_);
if (maxBasicLeaf < 1)
{
// Earlier Intel 486, CPUID not implemented
return;
}
// 1: Processor Info and Feature Bits
uint32 procInfoEcx = 0;
uint32 procInfoEdx = 0;
cpuid(1, 0, &_, &_, &procInfoEcx, &procInfoEdx);
sFeatures.SSE = (procInfoEdx & (1 << 25)) != 0;
sFeatures.SSE2 = (procInfoEdx & (1 << 26)) != 0;
// 7: Extended Features
uint32 extendedFeaturesEbx = 0;
cpuid(7, 0, &_, &extendedFeaturesEbx, &_, &_);
// `XSAVE` and `AVX` support:
if ((procInfoEcx & (1 << 26)) != 0)
{
// Here the CPU supports `XSAVE`
// Detect `OSXSAVE`, that is, whether the OS is AVX enabled and
// supports saving the state of the AVX/AVX2 vector registers on
// context-switches
if ((procInfoEcx & (1 << 27)) != 0)
{
// The OS must have signaled the CPU that it supports saving and restoring the
uint64 xcr0 = xgetbv(0);
bool avxSupport = (xcr0 & 6) == 6;
bool avx512Support = (xcr0 & 224) == 224;
// Only if the OS and the CPU support saving/restoring the AVX registers we enable `xsave` support
if (avxSupport)
{
sFeatures.AVX = (procInfoEcx & (1 << 28)) != 0;
sFeatures.AVX2 = (extendedFeaturesEbx & (1 << 5)) != 0;
// For AVX-512 the OS also needs to support saving/restoring
// the extended state, only then we enable AVX-512 support:
if (avx512Support)
sFeatures.AVX512 = (extendedFeaturesEbx & (1 << 16)) != 0;
}
}
}
}
[Intrinsic("cpuid")]
private static extern void cpuid(uint32 leaf, uint32 subleaf, uint32* eax, uint32* ebx, uint32* ecx, uint32* edx);
[Intrinsic("xgetbv")]
private static extern uint64 xgetbv(uint32 xcr);
#endif
public static void Shutdown()
{
#if !BF_RUNTIME_REDUCED && BF_PLATFORM_WINDOWS
ShutdownCrashCatcher();
#endif
}
}
}
#if BF_RUNTIME_DISABLE && !BF_CRT_DISABLE
namespace System
{
[AlwaysInclude, StaticInitPriority(1000)]
static class MinRuntime
{
static function void*(int) sMallocFunc;
static function void(void*) sFreeFunc;
static function void(char8) sPutChar;
static this()
{
var lib = Windows.LoadLibraryA("msvcrt.dll");
sMallocFunc = (.)Windows.GetProcAddress(lib, "malloc");
sFreeFunc = (.)Windows.GetProcAddress(lib, "free");
sPutChar = (.)Windows.GetProcAddress(lib, "putchar");
}
/*[LinkName(.C), AlwaysInclude]
static void __chkstk()
{
}*/
[LinkName(.C), AlwaysInclude]
static void* malloc(int size)
{
return sMallocFunc(size);
}
[LinkName(.C), AlwaysInclude]
static void free(void* ptr)
{
sFreeFunc(ptr);
}
[LinkName(.C), AlwaysInclude]
static void putchar(char8 c)
{
sPutChar(c);
}
[LinkName(.C), AlwaysInclude]
static void memset(void* dest, uint8 val, int size)
{
uint8* outPtr = (.)dest;
for (int i < size)
*(outPtr++) = val;
}
[LinkName(.C), AlwaysInclude]
static void memcpy(void* dest, void* src, int size)
{
uint8* destPtr = (.)dest;
uint8* srcPtr = (.)src;
if (destPtr < srcPtr)
{
for (int i < size)
*(destPtr++) = *(srcPtr++);
}
else
{
destPtr += size;
srcPtr += size;
for (int i < size)
*(--destPtr) = *(--srcPtr);
}
}
[LinkName(.C), AlwaysInclude]
static void memmove(void* dest, void* src, int size)
{
uint8* destPtr = (.)dest;
uint8* srcPtr = (.)src;
if (destPtr < srcPtr)
{
for (int i < size)
*(destPtr++) = *(srcPtr++);
}
else
{
destPtr += size;
srcPtr += size;
for (int i < size)
*(--destPtr) = *(--srcPtr);
}
}
[LinkName(.C), AlwaysInclude]
static double strtod(char8* str, char8** endPtr)
{
return 0;
}
[LinkName(.C), AlwaysInclude]
static extern void WinMain(void* module, void* prevModule, char8* args, int32 showCmd);
[LinkName(.C), AlwaysInclude]
static extern int32 main(int argc, char8** argv);
[LinkName(.C), AlwaysInclude]
static void mainCRTStartup()
{
main(0, null);
}
/*[LinkName(.C), AlwaysInclude]
static void WinMainCRTStartup()
{
//WinMain(null, null, "hi", 1);
}*/
[LinkName(.C), Export]
static int32 _tls_index;
[LinkName(.C), Export]
static bool _fltused;
}
}
#endif