From 9ccdf7282e4c7c5d5c0aa0ac9d3a95fc43c0dab5 Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Fri, 22 Jan 2021 04:58:08 -0800 Subject: [PATCH] Added VarArgs --- BeefLibs/corlib/src/Internal.bf | 32 +++++++ IDEHelper/Backend/BeMCContext.cpp | 120 ++++++++++++++++++++----- IDEHelper/Backend/BeMCContext.h | 1 + IDEHelper/Compiler/BfExprEvaluator.cpp | 12 ++- IDEHelper/Compiler/BfIRBuilder.h | 5 +- IDEHelper/Compiler/BfIRCodeGen.cpp | 16 ++++ IDEHelper/Tests/BeefSpace.toml | 1 - IDEHelper/Tests/src/VarArgs.bf | 67 +++++++++++++- 8 files changed, 228 insertions(+), 26 deletions(-) diff --git a/BeefLibs/corlib/src/Internal.bf b/BeefLibs/corlib/src/Internal.bf index 815dd2b0..99294b40 100644 --- a/BeefLibs/corlib/src/Internal.bf +++ b/BeefLibs/corlib/src/Internal.bf @@ -21,6 +21,38 @@ namespace System } } + struct VarArgs + { + void* mVAList; + + [Intrinsic("va_start")] + static extern void Start(void* vaList); + [Intrinsic("va_end")] + static extern void End(void* vaList); + [Intrinsic("va_arg")] + static extern void Arg(void* vaList, void* destPtr, int32 typeId); + + [Inline] + public mixin Start() mut + { + Start(&mVAList); + } + + [Inline] + public mixin End() mut + { + End(&mVAList); + } + + [Inline] + public mixin Get() mut + { + T val = ?; + Arg(&mVAList, &val, (.)typeof(T).TypeId); + val + } + } + [AlwaysInclude] static class Internal { diff --git a/IDEHelper/Backend/BeMCContext.cpp b/IDEHelper/Backend/BeMCContext.cpp index 17e3e557..294543b3 100644 --- a/IDEHelper/Backend/BeMCContext.cpp +++ b/IDEHelper/Backend/BeMCContext.cpp @@ -1932,6 +1932,7 @@ BeMCContext::BeMCContext(BeCOFFObject* coffObject) : mOut(coffObject->mTextSect. mCurVRegsInit = NULL; mCurVRegsLive = NULL; mUseBP = false; + mHasVAStart = false; mInsertInstIdxRef = NULL; mNativeIntType = mCOFFObject->mBeModule->mContext->GetPrimitiveType(BeTypeCode_Int64); mDebugging = false; @@ -8477,7 +8478,7 @@ void BeMCContext::DoFrameObjPass() } mActiveBlock = mBlocks[0]; - + if (mUseBP) { AllocInst(BeMCInstKind_Unwind_SetBP, 0); @@ -8519,6 +8520,36 @@ void BeMCContext::DoFrameObjPass() } } + if (mHasVAStart) + { + for (int i = 0; i < 4; i++) + { + auto regSaveVReg = AllocVirtualReg(mModule->mContext->GetPointerTo(mModule->mContext->GetPrimitiveType(BeTypeCode_Int64))); + auto vregInfo = GetVRegInfo(regSaveVReg); + vregInfo->mRelTo = BeMCOperand::FromReg(X64Reg_RSP); + vregInfo->mRelOffset = BeMCOperand::FromImmediate(i * 8 + 8); + vregInfo->mIsExpr = true; + + X64CPURegister reg; + switch (i) + { + case 0: + reg = X64Reg_RCX; + break; + case 1: + reg = X64Reg_RDX; + break; + case 2: + reg = X64Reg_R8; + break; + case 3: + reg = X64Reg_R9; + break; + } + AllocInst(BeMCInstKind_Mov, BeMCOperand::ToLoad(regSaveVReg), BeMCOperand::FromReg(reg), 0); + } + } + BeMCOperand restoreBPVal; if (mUseBP) { @@ -15263,7 +15294,7 @@ void BeMCContext::HandleParams() int regIdxOfs = 0; int paramOfs = 0; auto retType = mBeFunction->GetFuncType()->mReturnType; - + X64CPURegister compositeRetReg = X64Reg_None; bool flipFirstRegs = false; if (mBeFunction->HasStructRet()) @@ -15801,7 +15832,7 @@ void BeMCContext::Generate(BeFunction* function) mDbgPreferredRegs[32] = X64Reg_R8;*/ //mDbgPreferredRegs[8] = X64Reg_RAX; - //mDebugging = (function->mName == "?ConvertType@TestC@BeefTest@bf@@SAPEAVType@System@3@PEAVString@53@@Z"); + //mDebugging = (function->mName == "?Zoips@TestProgram@BeefTest@bf@@SAXXZ"); // || (function->mName == "?MethodA@TestProgram@BeefTest@bf@@CAXXZ"); // || (function->mName == "?Hey@Blurg@bf@@SAXXZ") // ; @@ -15871,23 +15902,18 @@ void BeMCContext::Generate(BeFunction* function) break; case BeCallInst::TypeId: { -// auto castedInst = (BeCallInst*)inst; -// -// if (auto intrin = BeValueDynCast(castedInst->mFunc)) -// { -// // Not a real call -// switch (intrin->mKind) -// { -// case BfIRIntrinsic_Malloc: -// case BfIRIntrinsic_MemCpy: -// case BfIRIntrinsic_MemMove: -// case BfIRIntrinsic_MemSet: -// mMaxCallParamCount = BF_MAX(mMaxCallParamCount, 4); -// break; -// } -// } -// else -// mMaxCallParamCount = BF_MAX(mMaxCallParamCount, (int)castedInst->mArgs.size()); + auto castedInst = (BeCallInst*)inst; + + if (auto intrin = BeValueDynCast(castedInst->mFunc)) + { + // Not a real call + switch (intrin->mKind) + { + case BfIRIntrinsic_VAStart: + mHasVAStart = true; + break; + } + } } break; case BeMemSetInst::TypeId: @@ -17495,6 +17521,60 @@ void BeMCContext::Generate(BeFunction* function) useAltArgs = true; } break; + case BfIRIntrinsic_VAArg: + { + auto mcListPtr = GetOperand(castedInst->mArgs[0].mValue); + auto mcDestVoidPtr = GetOperand(castedInst->mArgs[1].mValue); + auto mcType = GetOperand(castedInst->mArgs[2].mValue); + + BeType* beType = mModule->mBeIRCodeGen->GetBeTypeById((int32)mcType.mImmediate); + + auto mcList = AllocVirtualReg(mModule->mContext->GetPointerTo(mModule->mContext->GetPrimitiveType(BeTypeCode_NullPtr))); + CreateDefineVReg(mcList); + auto listVRegInfo = GetVRegInfo(mcList); + listVRegInfo->mRelTo = mcListPtr; + listVRegInfo->mIsExpr = true; + + auto mcSrc = AllocVirtualReg(mModule->mContext->GetPointerTo(beType)); + CreateDefineVReg(mcSrc); + auto srcVRegInfo = GetVRegInfo(mcSrc); + srcVRegInfo->mRelTo = BeMCOperand::ToLoad(mcList); + srcVRegInfo->mIsExpr = true; + + auto mcDest = AllocVirtualReg(mModule->mContext->GetPointerTo(beType)); + CreateDefineVReg(mcDest); + auto destVRegInfo = GetVRegInfo(mcDest); + destVRegInfo->mRelTo = mcDestVoidPtr; + destVRegInfo->mIsExpr = true; + + AllocInst(BeMCInstKind_Mov, BeMCOperand::ToLoad(mcDest), BeMCOperand::ToLoad(mcSrc)); + AllocInst(BeMCInstKind_Add, BeMCOperand::ToLoad(mcList), BeMCOperand::FromImmediate(8)); + } + break; + case BfIRIntrinsic_VAEnd: + break; + case BfIRIntrinsic_VAStart: + { + auto mcTarget = GetOperand(castedInst->mArgs[0].mValue); + + auto destVal = AllocVirtualReg(mModule->mContext->GetPointerTo(mModule->mContext->GetPrimitiveType(BeTypeCode_NullPtr))); + auto destVRegInfo = GetVRegInfo(destVal); + destVRegInfo->mRelTo = mcTarget; + destVRegInfo->mIsExpr = true; + CreateDefineVReg(destVal); + + auto nullPtrType = mModule->mContext->GetPrimitiveType(BeTypeCode_NullPtr); + auto vaStartVal = AllocVirtualReg(nullPtrType); + auto vRegInfo = GetVRegInfo(vaStartVal); + vRegInfo->mMustExist = true; + vRegInfo->mForceMem = true; + vRegInfo->mFrameOffset = (int)mBeFunction->mParams.size() * 8 + 8; + vRegInfo->mRefCount++; + CreateDefineVReg(vaStartVal); + + AllocInst(BeMCInstKind_Mov, BeMCOperand::ToLoad(destVal), BeMCOperand::FromVRegAddr(vaStartVal.mVRegIdx)); + } + break; default: SoftFail(StrFormat("Intrinsic not handled: '%s'", intrin->mName.c_str()), castedInst->mDbgLoc); break; diff --git a/IDEHelper/Backend/BeMCContext.h b/IDEHelper/Backend/BeMCContext.h index 415f902d..aa43f01e 100644 --- a/IDEHelper/Backend/BeMCContext.h +++ b/IDEHelper/Backend/BeMCContext.h @@ -1330,6 +1330,7 @@ public: int mStackSize; bool mUseBP; + bool mHasVAStart; int mCurLabelIdx; int mCurPhiIdx; int mMaxCallParamCount; // -1 if we have no calls diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 485f9291..3f8c2694 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -770,8 +770,11 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp anyIsExtension = true; } - bool hadEnoughArgs = newMethodInstance->GetParamCount() - newImplicitParamCount < (int)mArguments.size(); - bool prevHadEnoughArgs = prevMethodInstance->GetParamCount() - prevImplicitParamCount < (int)mArguments.size(); + int newMethodParamCount = newMethodInstance->GetParamCount(); + int prevMethodParamCount = prevMethodInstance->GetParamCount(); + + bool hadEnoughArgs = newMethodParamCount - newImplicitParamCount < (int)mArguments.size(); + bool prevHadEnoughArgs = prevMethodParamCount - prevImplicitParamCount < (int)mArguments.size(); RETURN_BETTER_OR_WORSE(hadEnoughArgs, prevHadEnoughArgs); bool chainSkip = (newMethodInstance->mChainType == BfMethodChainType_ChainMember) || (newMethodInstance->mChainType == BfMethodChainType_ChainSkip); @@ -808,6 +811,11 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp int newArgIdx = argIdx + newImplicitParamCount; int prevArgIdx = argIdx + prevImplicitParamCount; + if (newArgIdx >= newMethodParamCount) + break; + if (prevArgIdx >= prevMethodParamCount) + break; + bool wasGenericParam = (newArgIdx >= 0) && newMethodInstance->WasGenericParam(newArgIdx); bool prevWasGenericParam = (prevArgIdx >= 0) && prevMethodInstance->WasGenericParam(prevArgIdx); diff --git a/IDEHelper/Compiler/BfIRBuilder.h b/IDEHelper/Compiler/BfIRBuilder.h index b9bdb861..a81e851b 100644 --- a/IDEHelper/Compiler/BfIRBuilder.h +++ b/IDEHelper/Compiler/BfIRBuilder.h @@ -456,7 +456,10 @@ enum BfIRIntrinsic : uint8 BfIRIntrinsic_Shuffle, BfIRIntrinsic_Sin, BfIRIntrinsic_Sqrt, - BfIRIntrinsic_Sub, + BfIRIntrinsic_Sub, + BfIRIntrinsic_VAArg, + BfIRIntrinsic_VAEnd, + BfIRIntrinsic_VAStart, BfIRIntrinsic_Xor, BfIRIntrinsic_COUNT, diff --git a/IDEHelper/Compiler/BfIRCodeGen.cpp b/IDEHelper/Compiler/BfIRCodeGen.cpp index 8aca6db6..06799093 100644 --- a/IDEHelper/Compiler/BfIRCodeGen.cpp +++ b/IDEHelper/Compiler/BfIRCodeGen.cpp @@ -189,6 +189,9 @@ static const BuiltinEntry gIntrinEntries[] = {"sin"}, {"sqrt"}, {"sub"}, + {"va_arg"}, + {"va_end"}, + {"va_start"}, {"xor"}, }; @@ -2631,6 +2634,9 @@ void BfIRCodeGen::HandleNextCmd() { llvm::Intrinsic::sin, 0, -1}, { llvm::Intrinsic::sqrt, 0, -1}, { (llvm::Intrinsic::ID)-2, -1}, // sub, + { (llvm::Intrinsic::ID)-2, -1}, // va_arg, + { llvm::Intrinsic::vaend, -1}, // va_end, + { llvm::Intrinsic::vastart, -1}, // va_start, { (llvm::Intrinsic::ID)-2, -1}, // xor }; BF_STATIC_ASSERT(BF_ARRAY_COUNT(intrinsics) == BfIRIntrinsic_COUNT); @@ -3451,6 +3457,16 @@ void BfIRCodeGen::HandleNextCmd() } } break; + case BfIRIntrinsic_VAArg: + { + auto constInt = llvm::dyn_cast(args[2]); + auto argType = GetLLVMTypeById((int)constInt->getSExtValue()); + auto vaArgVal = mIRBuilder->CreateVAArg(args[0], argType); + + auto resultPtr = mIRBuilder->CreateBitCast(args[1], argType->getPointerTo()); + mIRBuilder->CreateStore(vaArgVal, resultPtr); + } + break; default: FatalError("Unhandled intrinsic"); } diff --git a/IDEHelper/Tests/BeefSpace.toml b/IDEHelper/Tests/BeefSpace.toml index 394fb56b..e1560daa 100644 --- a/IDEHelper/Tests/BeefSpace.toml +++ b/IDEHelper/Tests/BeefSpace.toml @@ -6,7 +6,6 @@ Unlocked = ["corlib"] StartupProject = "Tests" [Configs.Debug.Win64] -BfOptimizationLevel = "O0" IntermediateType = "ObjectAndIRCode" [Configs.Test.Win64] diff --git a/IDEHelper/Tests/src/VarArgs.bf b/IDEHelper/Tests/src/VarArgs.bf index 1bcbfefa..bb2efe11 100644 --- a/IDEHelper/Tests/src/VarArgs.bf +++ b/IDEHelper/Tests/src/VarArgs.bf @@ -1,8 +1,10 @@ +#pragma warning disable 168 + using System; namespace Tests { - class VarArgs + class VarArgsTests { #if BF_PLATFORM_WINDOWS [CLink, Import("msvcrt.dll")] @@ -11,14 +13,75 @@ namespace Tests #endif public static extern int32 sprintf(char8* dest, char8* fmt, ...); +#if BF_PLATFORM_WINDOWS + [CLink, Import("msvcrt.dll")] +#else + [CLink] +#endif + public static extern int32 vsprintf(char8* dest, char8* fmt, VarArgs varArgs); + + public static (int, int, int) MethodA(...) + { + VarArgs vaArgs = .(); + vaArgs.Start!(); + int val = vaArgs.Get!(); + int val2 = vaArgs.Get!(); + int val3 = (int)vaArgs.Get!(); + vaArgs.End!(); + + return (val, val2, val3); + } + + public static (int, int, int) MethodB(int a, ...) + { + VarArgs vaArgs = .(); + vaArgs.Start!(); + int val = vaArgs.Get!(); + int val2 = vaArgs.Get!(); + int val3 = (int)vaArgs.Get!(); + vaArgs.End!(); + + return (val, val2, val3); + } + + public static (int, int, int) MethodC(int a, ...) + { + uint8* data = scope uint8[a]*; + + VarArgs vaArgs = .(); + vaArgs.Start!(); + int val = vaArgs.Get!(); + int val2 = vaArgs.Get!(); + int val3 = (int)vaArgs.Get!(); + vaArgs.End!(); + + return (val, val2, val3); + } + + public static int32 SPrintF(char8* dest, char8* fmt, ...) + { + VarArgs vaArgs = .(); + vaArgs.Start!(); + int32 result = vsprintf(dest, fmt, vaArgs); + vaArgs.End!(); + return result; + } + [Test] public static void TestBasics() { char8[256] cStr; sprintf(&cStr, "Test %d %0.1f %0.1f", 123, 1.2f, 2.3f); - String str = scope .(&cStr); Test.Assert(str == "Test 123 1.2 2.3"); + + SPrintF(&cStr, "Test %d %0.1f %0.1f", 223, 2.2f, 3.3f); + str.Set(StringView(&cStr)); + Test.Assert(str == "Test 223 2.2 3.3"); + + Test.Assert(MethodA( 12, 23, 123.0f) == (12, 23, 123)); + Test.Assert(MethodB( 9, 22, 33, 223.0f) == (22, 33, 223)); + Test.Assert(MethodC(11, 32, 43, 323.0f) == (32, 43, 323)); } } }