mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 11:38:21 +02:00
8136 lines
No EOL
260 KiB
C++
8136 lines
No EOL
260 KiB
C++
#include "BfCompiler.h"
|
|
#include "BfSystem.h"
|
|
#include "BfParser.h"
|
|
#include "BfCodeGen.h"
|
|
#include "BfExprEvaluator.h"
|
|
#include <fcntl.h>
|
|
#include "BfConstResolver.h"
|
|
#include "BfMangler.h"
|
|
#include "BeefySysLib/util/PerfTimer.h"
|
|
#include "BeefySysLib/util/BeefPerf.h"
|
|
#include "BeefySysLib/util/StackHelper.h"
|
|
#include "BfSourceClassifier.h"
|
|
#include "BfAutoComplete.h"
|
|
#include "BfDemangler.h"
|
|
#include "BfResolvePass.h"
|
|
#include "BfFixits.h"
|
|
#include "BfIRCodeGen.h"
|
|
#include "BfDefBuilder.h"
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4141)
|
|
#pragma warning(disable:4146)
|
|
#pragma warning(disable:4291)
|
|
#pragma warning(disable:4244)
|
|
#pragma warning(disable:4267)
|
|
#pragma warning(disable:4624)
|
|
#pragma warning(disable:4800)
|
|
#pragma warning(disable:4996)
|
|
|
|
#include "llvm/IR/DIBuilder.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/GlobalValue.h"
|
|
#include "llvm/IR/GlobalVariable.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/IR/InlineAsm.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
|
|
#include "BeefySysLib/util/AllocDebug.h"
|
|
|
|
#pragma warning(pop)
|
|
|
|
USING_NS_BF;
|
|
|
|
bool BfModule::AddDeferredCallEntry(BfDeferredCallEntry* deferredCallEntry, BfScopeData* scopeData)
|
|
{
|
|
if ((((mCompiler->mIsResolveOnly) && (!mIsComptimeModule)) ||
|
|
(mBfIRBuilder->mIgnoreWrites)) && (deferredCallEntry->mDeferredBlock == NULL))
|
|
{
|
|
// For resolve entries, we only keep deferred blocks because we need to process them later so we can
|
|
// resolve inside of them. This is also required for lambda bind scan-pass
|
|
delete deferredCallEntry;
|
|
return false;
|
|
}
|
|
|
|
if (mBfIRBuilder->mIgnoreWrites)
|
|
{
|
|
deferredCallEntry->mIgnored = true;
|
|
scopeData->mDeferredCallEntries.PushBack(deferredCallEntry);
|
|
return true;
|
|
}
|
|
|
|
// We don't need to do a "clear handlers" if we're just adding another dyn to an existing dyn list
|
|
bool isDyn = mCurMethodState->mCurScope->IsDyn(scopeData);
|
|
|
|
if (mCurMethodState->mPendingNullConditional != NULL)
|
|
isDyn = true;
|
|
|
|
deferredCallEntry->mOrigScopeArgs = deferredCallEntry->mScopeArgs;
|
|
|
|
if (!isDyn)
|
|
{
|
|
mCurMethodState->mCurScope->ClearHandlers(scopeData);
|
|
|
|
if (!mBfIRBuilder->mIgnoreWrites)
|
|
{
|
|
if ((IsTargetingBeefBackend()) && (deferredCallEntry->mModuleMethodInstance.mMethodInstance != NULL) &&
|
|
(!mContext->IsSentinelMethod(deferredCallEntry->mModuleMethodInstance.mMethodInstance)))
|
|
{
|
|
SizedArray<BfIRType, 8> origParamTypes;
|
|
BfIRType origReturnType;
|
|
deferredCallEntry->mModuleMethodInstance.mMethodInstance->GetIRFunctionInfo(this, origReturnType, origParamTypes);
|
|
|
|
int sretIdx = deferredCallEntry->mModuleMethodInstance.mMethodInstance->GetStructRetIdx();
|
|
BF_ASSERT(origParamTypes.size() == deferredCallEntry->mScopeArgs.size() + ((sretIdx != -1) ? 1 : 0));
|
|
|
|
int argIdx = 0;
|
|
int paramIdx = 0;
|
|
for (int argIdx = 0; argIdx < (int)deferredCallEntry->mScopeArgs.size(); argIdx++, paramIdx++)
|
|
{
|
|
if (argIdx == sretIdx)
|
|
paramIdx++;
|
|
|
|
auto scopeArg = deferredCallEntry->mScopeArgs[argIdx];
|
|
if ((scopeArg.IsConst()) || (scopeArg.IsFake()))
|
|
continue;
|
|
|
|
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
|
|
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
|
|
auto allocaInst = mBfIRBuilder->CreateAlloca(origParamTypes[paramIdx]);
|
|
mBfIRBuilder->ClearDebugLocation_Last();
|
|
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
|
|
if (WantsLifetimes())
|
|
{
|
|
mBfIRBuilder->CreateLifetimeStart(allocaInst);
|
|
mBfIRBuilder->ClearDebugLocation_Last();
|
|
}
|
|
mBfIRBuilder->CreateStore(scopeArg, allocaInst);
|
|
mBfIRBuilder->ClearDebugLocation_Last();
|
|
deferredCallEntry->mScopeArgs[argIdx] = allocaInst;
|
|
if (WantsLifetimes())
|
|
scopeData->mDeferredLifetimeEnds.push_back(allocaInst);
|
|
}
|
|
deferredCallEntry->mArgsNeedLoad = true;
|
|
}
|
|
}
|
|
|
|
scopeData->mDeferredCallEntries.PushFront(deferredCallEntry);
|
|
return true;
|
|
}
|
|
bool isLooped = mCurMethodState->mCurScope->IsLooped(scopeData);
|
|
|
|
BfDeferredCallEntry* listEntry = NULL;
|
|
if (!scopeData->mDeferredCallEntries.IsEmpty())
|
|
{
|
|
listEntry = scopeData->mDeferredCallEntries.mHead;
|
|
if (!listEntry->IsDynList())
|
|
listEntry = NULL;
|
|
}
|
|
|
|
auto deferredCallEntryType = ResolveTypeDef(mCompiler->mDeferredCallTypeDef);
|
|
AddDependency(deferredCallEntryType, mCurTypeInstance, BfDependencyMap::DependencyFlag_Allocates);
|
|
mBfIRBuilder->PopulateType(deferredCallEntryType);
|
|
auto deferredCallEntryTypePtr = CreatePointerType(deferredCallEntryType);
|
|
|
|
UpdateSrcPos(mCurMethodInstance->mMethodDef->GetRefNode(), BfSrcPosFlag_NoSetDebugLoc);
|
|
|
|
if (listEntry == NULL)
|
|
{
|
|
listEntry = new BfDeferredCallEntry();
|
|
|
|
if (!mBfIRBuilder->mIgnoreWrites)
|
|
{
|
|
listEntry->mDynCallTail = CreateAlloca(deferredCallEntryTypePtr, false, "deferredCallTail");
|
|
if (WantsLifetimes())
|
|
scopeData->mDeferredLifetimeEnds.push_back(listEntry->mDynCallTail);
|
|
|
|
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
|
|
mBfIRBuilder->SaveDebugLocation();
|
|
mBfIRBuilder->SetInsertPointAtStart(mCurMethodState->mIRInitBlock);
|
|
|
|
auto scopeHead = &mCurMethodState->mHeadScope;
|
|
if (scopeHead->mDIScope)
|
|
mBfIRBuilder->SetCurrentDebugLocation(mCurFilePosition.mCurLine + 1, 0, scopeHead->mDIScope, BfIRMDNode());
|
|
|
|
if (WantsLifetimes())
|
|
mBfIRBuilder->CreateLifetimeStart(listEntry->mDynCallTail);
|
|
auto storeInst = mBfIRBuilder->CreateStore(GetDefaultValue(deferredCallEntryTypePtr), listEntry->mDynCallTail);
|
|
mBfIRBuilder->ClearDebugLocation(storeInst);
|
|
|
|
if (WantsDebugInfo())
|
|
{
|
|
auto deferredCallEntryType = ResolveTypeDef(mCompiler->mDeferredCallTypeDef);
|
|
auto deferredCallEntryTypePtr = CreatePointerType(deferredCallEntryType);
|
|
|
|
String varName = StrFormat("__deferred%d", mCurMethodState->mDeferredLoopListCount);
|
|
|
|
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRInitBlock);
|
|
|
|
if (scopeHead->mDIScope)
|
|
mBfIRBuilder->SetCurrentDebugLocation(mCurFilePosition.mCurLine + 1, 0, scopeHead->mDIScope, BfIRMDNode());
|
|
mBfIRBuilder->CreateStatementStart();
|
|
|
|
//TODO: Make this work for LLVM - we need a proper debug location
|
|
//if (IsTargetingBeefBackend())
|
|
{
|
|
auto diVariable = mBfIRBuilder->DbgCreateAutoVariable(scopeHead->mDIScope, varName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, mBfIRBuilder->DbgGetType(deferredCallEntryTypePtr));
|
|
mBfIRBuilder->DbgInsertDeclare(listEntry->mDynCallTail, diVariable);
|
|
}
|
|
mCurMethodState->mDeferredLoopListCount++;
|
|
}
|
|
|
|
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
|
|
mBfIRBuilder->RestoreDebugLocation();
|
|
}
|
|
|
|
mCurMethodState->mCurScope->ClearHandlers(scopeData);
|
|
scopeData->mDeferredCallEntries.PushFront(listEntry);
|
|
}
|
|
|
|
BfIRValue deferredAlloca;
|
|
SizedArray<BfType*, 4> types;
|
|
SizedArray<String, 8> memberNames;
|
|
int instAlign = 1;
|
|
int dataPos = 0;
|
|
int instSize = 0;
|
|
Array<int> memberPositions;
|
|
String typeName;
|
|
BfDeferredMethodCallData* deferredMethodCallData = NULL;
|
|
|
|
if (deferredCallEntry->mDeferredBlock != NULL)
|
|
{
|
|
HashContext hashCtx;
|
|
hashCtx.Mixin(deferredCallEntry->mDeferredBlock->GetSrcStart());
|
|
|
|
auto parserData = deferredCallEntry->mDeferredBlock->GetParserData();
|
|
if (parserData != NULL)
|
|
hashCtx.MixinStr(parserData->mFileName);
|
|
|
|
int64 blockId = BfDeferredMethodCallData::GenerateMethodId(this, hashCtx.Finish64());
|
|
deferredCallEntry->mBlockId = blockId;
|
|
|
|
auto deferType = deferredCallEntryType;
|
|
|
|
BfIRType deferIRType;
|
|
auto int64Type = GetPrimitiveType(BfTypeCode_Int64);
|
|
|
|
types.push_back(int64Type);
|
|
memberNames.push_back("__methodId");
|
|
|
|
types.push_back(deferredCallEntryTypePtr);
|
|
memberNames.push_back("__next");
|
|
|
|
for (auto& capture : deferredCallEntry->mCaptures)
|
|
{
|
|
BfType* type = capture.mValue.mType;
|
|
types.push_back(type);
|
|
memberNames.push_back(capture.mName);
|
|
}
|
|
|
|
SizedArray<BfIRType, 4> llvmTypes;
|
|
SizedArray<BfIRMDNode, 8> diFieldTypes;
|
|
|
|
typeName = StrFormat("_BF_DeferredData_%s", BfTypeUtils::HashEncode64(blockId).c_str());
|
|
|
|
auto valueType = ResolveTypeDef(mCompiler->mValueTypeTypeDef);
|
|
llvmTypes.push_back(mBfIRBuilder->MapType(valueType));
|
|
|
|
//int dataPos = 0;
|
|
for (int i = 0; i < (int)memberNames.size(); i++)
|
|
{
|
|
auto type = types[i];
|
|
auto memberName = memberNames[i];
|
|
|
|
if (!type->IsValuelessType())
|
|
{
|
|
llvmTypes.push_back(mBfIRBuilder->MapType(type));
|
|
|
|
instAlign = BF_MAX(instAlign, (int)type->mAlign);
|
|
int alignSize = (int)type->mAlign;
|
|
int dataSize = type->mSize;
|
|
if (alignSize > 1)
|
|
dataPos = (dataPos + (alignSize - 1)) & ~(alignSize - 1);
|
|
|
|
memberPositions.push_back(dataPos);
|
|
dataPos += type->mSize;
|
|
}
|
|
}
|
|
instSize = dataPos;
|
|
|
|
deferIRType = mBfIRBuilder->CreateStructType(typeName);
|
|
mBfIRBuilder->StructSetBody(deferIRType, llvmTypes, instSize, instAlign, false);
|
|
|
|
auto prevInsertPoint = mBfIRBuilder->GetInsertBlock();
|
|
if (!isLooped)
|
|
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
|
|
deferredAlloca = mBfIRBuilder->CreateAlloca(deferIRType);
|
|
mBfIRBuilder->SetAllocaAlignment(deferredAlloca, instAlign);
|
|
mBfIRBuilder->SetAllocaNoChkStkHint(deferredAlloca);
|
|
if (!isLooped)
|
|
mBfIRBuilder->SetInsertPoint(prevInsertPoint);
|
|
|
|
auto gepInstance = mBfIRBuilder->CreateInBoundsGEP(deferredAlloca, 0, 2); // mNext
|
|
auto prevVal = mBfIRBuilder->CreateLoad(listEntry->mDynCallTail);
|
|
mBfIRBuilder->CreateStore(prevVal, gepInstance);
|
|
gepInstance = mBfIRBuilder->CreateInBoundsGEP(deferredAlloca, 0, 1); // mMethodId
|
|
mBfIRBuilder->CreateStore(GetConstValue64(blockId), gepInstance);
|
|
|
|
if (!deferredCallEntry->mCaptures.empty())
|
|
{
|
|
int dataIdx = 3;
|
|
for (int captureIdx = 0; captureIdx < (int)deferredCallEntry->mCaptures.size(); captureIdx++)
|
|
{
|
|
auto& capture = deferredCallEntry->mCaptures[captureIdx];
|
|
if (!capture.mValue.mType->IsValuelessType())
|
|
{
|
|
auto gepInstance = mBfIRBuilder->CreateInBoundsGEP(deferredAlloca, 0, dataIdx);
|
|
mBfIRBuilder->CreateStore(capture.mValue.mValue, gepInstance);
|
|
dataIdx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
mBfIRBuilder->CreateStore(mBfIRBuilder->CreateBitCast(deferredAlloca, mBfIRBuilder->MapType(deferredCallEntryTypePtr)), listEntry->mDynCallTail);
|
|
|
|
deferredCallEntry->mDeferredAlloca = deferredAlloca;
|
|
listEntry->mDynList.PushFront(deferredCallEntry);
|
|
}
|
|
else
|
|
{
|
|
auto& llvmArgs = deferredCallEntry->mScopeArgs;
|
|
auto moduleMethodInstance = deferredCallEntry->mModuleMethodInstance;
|
|
auto methodInstance = moduleMethodInstance.mMethodInstance;
|
|
auto methodDef = methodInstance->mMethodDef;
|
|
auto owningType = methodInstance->mMethodInstanceGroup->mOwner;
|
|
|
|
auto voidType = GetPrimitiveType(BfTypeCode_None);
|
|
auto voidPtrType = CreatePointerType(voidType);
|
|
|
|
BfDeferredMethodCallData** deferredMethodCallDataPtr = NULL;
|
|
if (mDeferredMethodCallData.TryGetValue(methodInstance, &deferredMethodCallDataPtr))
|
|
{
|
|
deferredMethodCallData = *deferredMethodCallDataPtr;
|
|
}
|
|
else
|
|
{
|
|
deferredMethodCallData = new BfDeferredMethodCallData();
|
|
mDeferredMethodCallData[methodInstance] = deferredMethodCallData;
|
|
deferredMethodCallData->mMethodId = BfDeferredMethodCallData::GenerateMethodId(this, methodInstance->mIdHash);
|
|
|
|
auto int64Type = GetPrimitiveType(BfTypeCode_Int64);
|
|
auto methodDef = moduleMethodInstance.mMethodInstance->mMethodDef;
|
|
auto thisType = moduleMethodInstance.mMethodInstance->mMethodInstanceGroup->mOwner;
|
|
|
|
types.push_back(int64Type);
|
|
memberNames.push_back("__methodId");
|
|
|
|
types.push_back(deferredCallEntryTypePtr);
|
|
memberNames.push_back("__next");
|
|
|
|
if (!methodDef->mIsStatic)
|
|
{
|
|
types.push_back(thisType);
|
|
memberNames.push_back("__this");
|
|
}
|
|
|
|
for (int paramIdx = 0; paramIdx < (int)methodInstance->GetParamCount(); paramIdx++)
|
|
{
|
|
if (methodInstance->IsParamSkipped(paramIdx))
|
|
paramIdx++;
|
|
types.push_back(methodInstance->GetParamType(paramIdx));
|
|
memberNames.push_back(methodInstance->GetParamName(paramIdx));
|
|
}
|
|
|
|
SizedArray<BfIRType, 4> llvmTypes;
|
|
|
|
//String typeName;
|
|
typeName += StrFormat("_BF_DeferredData_%s", BfTypeUtils::HashEncode64(deferredMethodCallData->mMethodId).c_str());
|
|
BfLogSysM("Building type: %s from methodInstance:%p\n", typeName.c_str(), methodInstance);
|
|
|
|
//Array<int> memberPositions;
|
|
|
|
//int instAlign = 1;
|
|
//int dataPos = 0;
|
|
BF_ASSERT(types.size() == memberNames.size());
|
|
for (int i = 0; i < (int)types.size(); i++)
|
|
{
|
|
auto type = types[i];
|
|
auto memberName = memberNames[i];
|
|
|
|
if (!type->IsValuelessType())
|
|
{
|
|
llvmTypes.push_back(mBfIRBuilder->MapType(type));
|
|
|
|
instAlign = BF_MAX(instAlign, (int)type->mAlign);
|
|
int alignSize = (int)type->mAlign;
|
|
int dataSize = type->mSize;
|
|
if (alignSize > 1)
|
|
dataPos = (dataPos + (alignSize - 1)) & ~(alignSize - 1);
|
|
|
|
memberPositions.push_back(dataPos);
|
|
dataPos += type->mSize;
|
|
}
|
|
}
|
|
instSize = dataPos;
|
|
|
|
deferredMethodCallData->mAlign = instAlign;
|
|
deferredMethodCallData->mSize = instSize;
|
|
deferredMethodCallData->mDeferType = mBfIRBuilder->CreateStructType(typeName);
|
|
mBfIRBuilder->StructSetBody(deferredMethodCallData->mDeferType, llvmTypes, instSize, instAlign, false);
|
|
deferredMethodCallData->mDeferTypePtr = mBfIRBuilder->GetPointerTo(deferredMethodCallData->mDeferType);
|
|
}
|
|
|
|
auto deferType = deferredMethodCallData->mDeferType;
|
|
|
|
auto prevInsertPoint = mBfIRBuilder->GetInsertBlock();
|
|
if (!isLooped)
|
|
mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock);
|
|
deferredAlloca = mBfIRBuilder->CreateAlloca(BfIRType(deferredMethodCallData->mDeferType));
|
|
mBfIRBuilder->ClearDebugLocation(deferredAlloca);
|
|
mBfIRBuilder->SetAllocaAlignment(deferredAlloca, deferredMethodCallData->mAlign);
|
|
mBfIRBuilder->SetAllocaNoChkStkHint(deferredAlloca);
|
|
if (!isLooped)
|
|
mBfIRBuilder->SetInsertPoint(prevInsertPoint);
|
|
|
|
auto gepInstance = mBfIRBuilder->CreateInBoundsGEP(deferredAlloca, 0, 1);
|
|
auto prevVal = mBfIRBuilder->CreateLoad(listEntry->mDynCallTail);
|
|
mBfIRBuilder->CreateStore(prevVal, gepInstance);
|
|
gepInstance = mBfIRBuilder->CreateInBoundsGEP(deferredAlloca, 0, 0);
|
|
mBfIRBuilder->CreateStore(GetConstValue64(deferredMethodCallData->mMethodId), gepInstance);
|
|
|
|
int dataIdx = 2;
|
|
int argIdx = 0;
|
|
if ((!methodDef->mIsStatic) && (!owningType->IsValuelessType()))
|
|
{
|
|
gepInstance = mBfIRBuilder->CreateInBoundsGEP(deferredAlloca, 0, 2);
|
|
if (owningType->IsValueType())
|
|
{
|
|
if ((!methodDef->mIsMutating) && (owningType->IsSplattable()))
|
|
{
|
|
BfTypedValue splatVal(llvmArgs[0], owningType, BfTypedValueKind_ThisSplatHead);
|
|
BfTypedValue aggVal = AggregateSplat(splatVal, &llvmArgs[0]);
|
|
aggVal = LoadValue(aggVal);
|
|
mBfIRBuilder->CreateStore(aggVal.mValue, gepInstance);
|
|
BfTypeUtils::SplatIterate([&](BfType* checkType) { argIdx++; }, owningType);
|
|
}
|
|
else
|
|
{
|
|
auto thisArg = mBfIRBuilder->CreateLoad(llvmArgs[0]);
|
|
mBfIRBuilder->CreateStore(thisArg, gepInstance);
|
|
argIdx++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mBfIRBuilder->CreateStore(llvmArgs[0], gepInstance);
|
|
argIdx++;
|
|
}
|
|
dataIdx++;
|
|
}
|
|
|
|
for (int paramIdx = 0; paramIdx < (int)methodInstance->GetParamCount(); paramIdx++, dataIdx++)
|
|
{
|
|
if (methodInstance->IsParamSkipped(paramIdx))
|
|
paramIdx++;
|
|
auto paramType = methodInstance->GetParamType(paramIdx);
|
|
bool paramIsSplat = methodInstance->GetParamIsSplat(paramIdx);
|
|
gepInstance = mBfIRBuilder->CreateInBoundsGEP(deferredAlloca, 0, dataIdx/*, methodDef->mParams[paramIdx]->mName*/);
|
|
if (paramType->IsStruct())
|
|
{
|
|
if (paramIsSplat)
|
|
{
|
|
BfTypedValue splatVal(llvmArgs[argIdx], paramType, BfTypedValueKind_SplatHead);
|
|
BfTypedValue aggVal = AggregateSplat(splatVal, &llvmArgs[argIdx]);
|
|
aggVal = LoadValue(aggVal);
|
|
mBfIRBuilder->CreateStore(aggVal.mValue, gepInstance);
|
|
BfTypeUtils::SplatIterate([&](BfType* checkType) { argIdx++; }, paramType);
|
|
}
|
|
else
|
|
{
|
|
auto val = mBfIRBuilder->CreateLoad(llvmArgs[argIdx]);
|
|
mBfIRBuilder->CreateStore(val, gepInstance);
|
|
argIdx++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mBfIRBuilder->CreateStore(llvmArgs[argIdx], gepInstance);
|
|
argIdx++;
|
|
}
|
|
}
|
|
|
|
mBfIRBuilder->CreateStore(mBfIRBuilder->CreateBitCast(deferredAlloca, mBfIRBuilder->MapType(deferredCallEntryTypePtr)), listEntry->mDynCallTail);
|
|
|
|
deferredCallEntry->mDeferredAlloca = deferredAlloca;
|
|
listEntry->mDynList.PushFront(deferredCallEntry);
|
|
}
|
|
|
|
if ((mBfIRBuilder->DbgHasInfo()) && (mHasFullDebugInfo) && (mCompiler->mOptions.mEmitDebugInfo) && (mCurMethodState->mCurScope->mDIScope))
|
|
{
|
|
auto int64Type = GetPrimitiveType(BfTypeCode_Int64);
|
|
auto moduleMethodInstance = deferredCallEntry->mModuleMethodInstance;
|
|
auto methodInstance = moduleMethodInstance.mMethodInstance;
|
|
BfIRMDNode deferDIType;
|
|
|
|
if ((deferredMethodCallData != NULL) && (deferredMethodCallData->mDeferDIType))
|
|
{
|
|
deferDIType = deferredMethodCallData->mDeferDIType;
|
|
}
|
|
else
|
|
{
|
|
BfIRMDNode diForwardDecl;
|
|
SizedArray<BfIRMDNode, 8> diFieldTypes;
|
|
if ((mBfIRBuilder->DbgHasInfo()) && (mHasFullDebugInfo))
|
|
{
|
|
String dbgTypeName;
|
|
if (mCompiler->mOptions.IsCodeView())
|
|
dbgTypeName += "_bf::";
|
|
dbgTypeName += typeName;
|
|
diForwardDecl = mBfIRBuilder->DbgCreateReplaceableCompositeType(llvm::dwarf::DW_TAG_structure_type, dbgTypeName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mFileInstance->mDIFile,
|
|
mCurFilePosition.mCurLine, instSize * 8, instAlign * 8);
|
|
|
|
if (methodInstance != NULL)
|
|
{
|
|
// We make a fake member to get inserted into the DbgModule data so we can show what method this deferred call goes to
|
|
StringT<128> mangledName;
|
|
BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), methodInstance);
|
|
auto memberType = mBfIRBuilder->DbgCreateMemberType(diForwardDecl, mangledName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine,
|
|
0, 0, -1, 0, mBfIRBuilder->DbgGetType(int64Type));
|
|
diFieldTypes.push_back(memberType);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < (int)types.size(); i++)
|
|
{
|
|
auto type = types[i];
|
|
auto memberName = memberNames[i];
|
|
if ((mBfIRBuilder->DbgHasInfo()) && (mHasFullDebugInfo))
|
|
{
|
|
int memberFlags = 0;
|
|
auto memberType = mBfIRBuilder->DbgCreateMemberType(diForwardDecl, memberName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine,
|
|
type->mSize * 8, type->mAlign * 8, memberPositions[i] * 8, memberFlags, mBfIRBuilder->DbgGetType(type));
|
|
diFieldTypes.push_back(memberType);
|
|
}
|
|
}
|
|
|
|
int diFlags = 0;
|
|
mBfIRBuilder->DbgMakePermanent(diForwardDecl, BfIRMDNode(), diFieldTypes);
|
|
deferDIType = mBfIRBuilder->DbgCreatePointerType(diForwardDecl);
|
|
if (deferredMethodCallData != NULL)
|
|
deferredMethodCallData->mDeferDIType = deferDIType;
|
|
}
|
|
|
|
// We don't actually want to see this, and it doesn't emit properly in LLVM CodeView anyway - it only accepts static allocs,
|
|
// not dynamic allocas
|
|
String varName = StrFormat("$__deferredCall_%d", mCurMethodState->mDeferredLoopListEntryCount);
|
|
mCurMethodState->mDeferredLoopListEntryCount++;
|
|
auto diVariable = mBfIRBuilder->DbgCreateAutoVariable(mCurMethodState->mCurScope->mDIScope,
|
|
varName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, deferDIType);
|
|
mBfIRBuilder->DbgInsertDeclare(deferredAlloca, diVariable);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
BfDeferredCallEntry* BfModule::AddDeferredBlock(BfBlock* block, BfScopeData* scopeData, Array<BfDeferredCapture>* captures)
|
|
{
|
|
BfDeferredCallEntry* deferredCallEntry = new BfDeferredCallEntry();
|
|
deferredCallEntry->mDeferredBlock = block;
|
|
if (captures != NULL)
|
|
deferredCallEntry->mCaptures = *captures;
|
|
AddDeferredCallEntry(deferredCallEntry, scopeData);
|
|
return deferredCallEntry;
|
|
}
|
|
|
|
BfDeferredCallEntry* BfModule::AddDeferredCall(const BfModuleMethodInstance& moduleMethodInstance, SizedArrayImpl<BfIRValue>& llvmArgs, BfScopeData* scopeData, BfAstNode* srcNode, bool bypassVirtual, bool doNullCheck, bool isAllocaFunc)
|
|
{
|
|
BfDeferredCallEntry* deferredCallEntry = new BfDeferredCallEntry();
|
|
BF_ASSERT(moduleMethodInstance);
|
|
deferredCallEntry->mModuleMethodInstance = moduleMethodInstance;
|
|
deferredCallEntry->mIsAllocaFunc = isAllocaFunc;
|
|
|
|
for (auto arg : llvmArgs)
|
|
{
|
|
deferredCallEntry->mScopeArgs.push_back(arg);
|
|
}
|
|
deferredCallEntry->mSrcNode = srcNode;
|
|
deferredCallEntry->mBypassVirtual = bypassVirtual;
|
|
deferredCallEntry->mDoNullCheck = doNullCheck;
|
|
if (!AddDeferredCallEntry(deferredCallEntry, scopeData))
|
|
return NULL;
|
|
return deferredCallEntry;
|
|
}
|
|
|
|
void BfModule::EmitDeferredCall(BfModuleMethodInstance moduleMethodInstance, SizedArrayImpl<BfIRValue>& llvmArgs, BfDeferredBlockFlags flags)
|
|
{
|
|
if (moduleMethodInstance.mMethodInstance->GetOwner()->IsInstanceOf(mCompiler->mInternalTypeDef))
|
|
{
|
|
if (moduleMethodInstance.mMethodInstance->mMethodDef->mName.StartsWith("SetDeleted"))
|
|
{
|
|
intptr typeSize = 0;
|
|
intptr typeAlign = 1;
|
|
intptr clearSize = 0;
|
|
bool isDynSize = false;
|
|
|
|
bool mayBeZero = false;
|
|
auto ptrValue = llvmArgs[0];
|
|
BfIRValue arraySize;
|
|
|
|
if ((moduleMethodInstance.mMethodInstance->mMethodDef->mName == "SetDeleted") ||
|
|
(moduleMethodInstance.mMethodInstance->mMethodDef->mName == "SetDeletedArray"))
|
|
{
|
|
auto constant = mBfIRBuilder->GetConstant(llvmArgs[1]);
|
|
if (constant != NULL)
|
|
typeSize = constant->mInt64;
|
|
constant = mBfIRBuilder->GetConstant(llvmArgs[2]);
|
|
if (constant != NULL)
|
|
typeAlign = constant->mInt64;
|
|
if (llvmArgs.size() >= 4)
|
|
arraySize = llvmArgs[3];
|
|
|
|
intptr allocSize = typeSize;
|
|
if (arraySize)
|
|
{
|
|
allocSize = BF_ALIGN(typeSize, typeAlign);
|
|
auto constant = mBfIRBuilder->GetConstant(arraySize);
|
|
if (constant != NULL)
|
|
allocSize = allocSize * (intptr)constant->mInt64;
|
|
else
|
|
{
|
|
isDynSize = true;
|
|
mayBeZero = true;
|
|
}
|
|
}
|
|
|
|
clearSize = BF_MIN(allocSize, mSystem->mPtrSize);
|
|
}
|
|
else if (moduleMethodInstance.mMethodInstance->mMethodDef->mName == "SetDeletedX")
|
|
{
|
|
// Note: this infers that mayBeZero is false still, because the deferred call would not have
|
|
// been added if the array size was zero
|
|
typeSize = 1;
|
|
clearSize = typeSize;
|
|
arraySize = llvmArgs[1];
|
|
isDynSize = true;
|
|
}
|
|
else if (moduleMethodInstance.mMethodInstance->mMethodDef->mName == "SetDeleted1")
|
|
{
|
|
clearSize = 1;
|
|
}
|
|
else if (moduleMethodInstance.mMethodInstance->mMethodDef->mName == "SetDeleted2")
|
|
{
|
|
clearSize = 2;
|
|
}
|
|
else if (moduleMethodInstance.mMethodInstance->mMethodDef->mName == "SetDeleted4")
|
|
{
|
|
clearSize = 4;
|
|
}
|
|
else if (moduleMethodInstance.mMethodInstance->mMethodDef->mName == "SetDeleted8")
|
|
{
|
|
clearSize = 8;
|
|
}
|
|
else if (moduleMethodInstance.mMethodInstance->mMethodDef->mName == "SetDeleted16")
|
|
{
|
|
clearSize = 16;
|
|
}
|
|
|
|
if (clearSize > 0)
|
|
{
|
|
BfTypeCode clearTypeCode = BfTypeCode_Int8;
|
|
if (clearSize >= mSystem->mPtrSize)
|
|
clearTypeCode = BfTypeCode_IntPtr;
|
|
else if (clearSize >= 4)
|
|
clearTypeCode = BfTypeCode_Int32;
|
|
else if (clearSize >= 2)
|
|
clearTypeCode = BfTypeCode_Int16;
|
|
|
|
auto intType = GetPrimitiveType(clearTypeCode);
|
|
auto intPtrType = CreatePointerType(intType);
|
|
|
|
if (isDynSize)
|
|
{
|
|
if (clearSize >= mSystem->mPtrSize)
|
|
{
|
|
auto ddSize1Block = mBfIRBuilder->CreateBlock("DDSize1");
|
|
auto ddDoneBlock = mBfIRBuilder->CreateBlock("DDDone");
|
|
|
|
auto cmp = mBfIRBuilder->CreateCmpGT(arraySize, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 0), true);
|
|
mBfIRBuilder->CreateCondBr(cmp, ddSize1Block, ddDoneBlock);
|
|
|
|
mBfIRBuilder->AddBlock(ddSize1Block);
|
|
mBfIRBuilder->SetInsertPoint(ddSize1Block);
|
|
auto intPtrVal = mBfIRBuilder->CreateBitCast(ptrValue, mBfIRBuilder->MapType(intPtrType));
|
|
mBfIRBuilder->CreateStore(mBfIRBuilder->CreateConst(clearTypeCode, (uint64)0xDDDDDDDDDDDDDDDDULL), intPtrVal);
|
|
mBfIRBuilder->CreateBr(ddDoneBlock);
|
|
|
|
mBfIRBuilder->AddBlock(ddDoneBlock);
|
|
mBfIRBuilder->SetInsertPoint(ddDoneBlock);
|
|
|
|
if ((flags & BfDeferredBlockFlag_MoveNewBlocksToEnd) != 0)
|
|
{
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(ddSize1Block);
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(ddDoneBlock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If we allocate at least this many then we can do an IntPtr-sized marking, otherwise just one element's worth
|
|
int intPtrCount = (int)((mSystem->mPtrSize + typeSize - 1) / typeSize);
|
|
|
|
BfIRBlock ddSizePtrBlock = mBfIRBuilder->CreateBlock("DDSizePtr");
|
|
BfIRBlock ddCheck1Block = mBfIRBuilder->CreateBlock("DDCheck1");
|
|
BfIRBlock ddSize1Block;
|
|
if (mayBeZero)
|
|
ddSize1Block = mBfIRBuilder->CreateBlock("DDSize1");
|
|
BfIRBlock ddDoneBlock = mBfIRBuilder->CreateBlock("DDDone");
|
|
|
|
auto intptrType = GetPrimitiveType(BfTypeCode_IntPtr);
|
|
auto intptrPtrType = CreatePointerType(intptrType);
|
|
|
|
auto cmpPtr = mBfIRBuilder->CreateCmpGTE(arraySize, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, intPtrCount), true);
|
|
mBfIRBuilder->CreateCondBr(cmpPtr, ddSizePtrBlock, ddCheck1Block);
|
|
|
|
mBfIRBuilder->AddBlock(ddSizePtrBlock);
|
|
mBfIRBuilder->SetInsertPoint(ddSizePtrBlock);
|
|
auto intptrPtrVal = mBfIRBuilder->CreateBitCast(ptrValue, mBfIRBuilder->MapType(intptrPtrType));
|
|
mBfIRBuilder->CreateStore(mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, (uint64)0xDDDDDDDDDDDDDDDDULL), intptrPtrVal);
|
|
mBfIRBuilder->CreateBr(ddDoneBlock);
|
|
|
|
mBfIRBuilder->AddBlock(ddCheck1Block);
|
|
mBfIRBuilder->SetInsertPoint(ddCheck1Block);
|
|
if (mayBeZero)
|
|
{
|
|
auto cmp1 = mBfIRBuilder->CreateCmpGT(arraySize, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 0), true);
|
|
mBfIRBuilder->CreateCondBr(cmp1, ddSize1Block, ddDoneBlock);
|
|
|
|
mBfIRBuilder->AddBlock(ddSize1Block);
|
|
mBfIRBuilder->SetInsertPoint(ddSize1Block);
|
|
}
|
|
auto intPtrVal = mBfIRBuilder->CreateBitCast(ptrValue, mBfIRBuilder->MapType(intPtrType));
|
|
mBfIRBuilder->CreateStore(mBfIRBuilder->CreateConst(clearTypeCode, (uint64)0xDDDDDDDDDDDDDDDDULL), intPtrVal);
|
|
mBfIRBuilder->CreateBr(ddDoneBlock);
|
|
|
|
mBfIRBuilder->AddBlock(ddDoneBlock);
|
|
mBfIRBuilder->SetInsertPoint(ddDoneBlock);
|
|
|
|
if ((flags & BfDeferredBlockFlag_MoveNewBlocksToEnd) != 0)
|
|
{
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(ddSizePtrBlock);
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(ddCheck1Block);
|
|
if (mayBeZero)
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(ddSize1Block);
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(ddDoneBlock);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto intPtrVal = mBfIRBuilder->CreateBitCast(ptrValue, mBfIRBuilder->MapType(intPtrType));
|
|
mBfIRBuilder->CreateStore(mBfIRBuilder->CreateConst(clearTypeCode, (uint64)0xDDDDDDDDDDDDDDDDULL), intPtrVal);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (moduleMethodInstance.mMethodInstance == mContext->mValueTypeDeinitSentinel)
|
|
{
|
|
BF_ASSERT(llvmArgs.size() == 3);
|
|
auto sizeConstant = mBfIRBuilder->GetConstant(llvmArgs[1]);
|
|
int clearSize = BF_MIN(sizeConstant->mInt32, 32);
|
|
|
|
auto alignConstant = mBfIRBuilder->GetConstant(llvmArgs[2]);
|
|
int clearAlign = alignConstant->mInt32;
|
|
|
|
mBfIRBuilder->CreateMemSet(llvmArgs[0], GetConstValue8(0xDD), GetConstValue(clearSize), clearAlign);
|
|
return;
|
|
}
|
|
|
|
auto methodInstance = moduleMethodInstance.mMethodInstance;
|
|
auto methodOwner = methodInstance->mMethodInstanceGroup->mOwner;
|
|
bool isDtor = methodInstance->mMethodDef->mMethodType == BfMethodType_Dtor;
|
|
bool isScopeDtor = isDtor && ((flags & BfDeferredBlockFlag_BypassVirtual) != 0);
|
|
|
|
if ((isDtor) && (methodInstance->GetParamCount() != 0))
|
|
{
|
|
// Dtor declared with params
|
|
AssertErrorState();
|
|
return;
|
|
}
|
|
|
|
BfIRBlock nullLabel;
|
|
BfIRBlock notNullLabel;
|
|
if ((flags & BfDeferredBlockFlag_DoNullChecks) != 0)
|
|
{
|
|
nullLabel = mBfIRBuilder->CreateBlock("deferred.isNull");
|
|
notNullLabel = mBfIRBuilder->CreateBlock("deferred.notNull");
|
|
|
|
auto notNullVal = mBfIRBuilder->CreateIsNotNull(llvmArgs[0]);
|
|
mBfIRBuilder->CreateCondBr(notNullVal, notNullLabel, nullLabel);
|
|
|
|
mBfIRBuilder->AddBlock(notNullLabel);
|
|
mBfIRBuilder->SetInsertPoint(notNullLabel);
|
|
}
|
|
|
|
bool skipAccessCheck = false;
|
|
if ((flags & BfDeferredBlockFlag_SkipObjectAccessCheck) != 0)
|
|
skipAccessCheck = true;
|
|
|
|
if ((!methodInstance->mMethodDef->mIsStatic) && (methodOwner->IsObjectOrInterface()) && (!isScopeDtor) &&
|
|
(!skipAccessCheck))
|
|
{
|
|
EmitObjectAccessCheck(BfTypedValue(llvmArgs[0], methodOwner));
|
|
}
|
|
|
|
BfExprEvaluator expressionEvaluator(this);
|
|
|
|
auto func = moduleMethodInstance.mFunc;
|
|
if ((flags & BfDeferredBlockFlag_IsAllocaFunc) != 0)
|
|
func = mBfIRBuilder->CreateLoad(func);
|
|
|
|
expressionEvaluator.CreateCall(NULL, moduleMethodInstance.mMethodInstance, func, ((flags & BfDeferredBlockFlag_BypassVirtual) != 0), llvmArgs);
|
|
|
|
if ((flags & BfDeferredBlockFlag_DoNullChecks) != 0)
|
|
{
|
|
mBfIRBuilder->CreateBr(nullLabel);
|
|
|
|
mBfIRBuilder->AddBlock(nullLabel);
|
|
mBfIRBuilder->SetInsertPoint(nullLabel);
|
|
|
|
if (!mBfIRBuilder->mIgnoreWrites)
|
|
{
|
|
if ((flags & BfDeferredBlockFlag_MoveNewBlocksToEnd) != 0)
|
|
{
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(notNullLabel);
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(nullLabel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BfModule::EmitDeferredCallProcessorInstances(BfScopeData* scopeData)
|
|
{
|
|
BF_ASSERT(scopeData->mDone);
|
|
|
|
for (auto& deferredProcessor : scopeData->mDeferredCallProcessorInstances)
|
|
{
|
|
SetAndRestoreValue<bool> prevHadReturn(mCurMethodState->mHadReturn, false);
|
|
SetAndRestoreValue<bool> prevInPostReturn(mCurMethodState->mInPostReturn, false);
|
|
SetAndRestoreValue<bool> prevLeftBlockUncond(mCurMethodState->mLeftBlockUncond, false);
|
|
|
|
auto prevBlock = mBfIRBuilder->GetInsertBlock();
|
|
|
|
mBfIRBuilder->AddBlock(deferredProcessor.mProcessorBlock);
|
|
mBfIRBuilder->SetInsertPoint(deferredProcessor.mProcessorBlock);
|
|
EmitDeferredCallProcessor(scopeData, deferredProcessor.mDeferredCallEntry->mDynList, deferredProcessor.mDeferredCallEntry->mDynCallTail);
|
|
mBfIRBuilder->CreateBr(deferredProcessor.mContinueBlock);
|
|
|
|
mBfIRBuilder->SetInsertPoint(prevBlock);
|
|
}
|
|
}
|
|
|
|
void BfModule::EmitDeferredCall(BfScopeData* scopeData, BfDeferredCallEntry& deferredCallEntry, bool moveBlocks)
|
|
{
|
|
if ((mCompiler->mIsResolveOnly) && (!mIsComptimeModule) && (deferredCallEntry.mHandlerCount > 0))
|
|
{
|
|
// We only want to process deferred blocks once, otherwise it could significantly slow down autocompletion
|
|
return;
|
|
}
|
|
|
|
deferredCallEntry.mHandlerCount++;
|
|
|
|
if (deferredCallEntry.IsDynList())
|
|
{
|
|
if (scopeData->mDone)
|
|
{
|
|
EmitDeferredCallProcessor(scopeData, deferredCallEntry.mDynList, deferredCallEntry.mDynCallTail);
|
|
}
|
|
else
|
|
{
|
|
BfDeferredCallProcessorInstance deferredProcessor;
|
|
deferredProcessor.mProcessorBlock = mBfIRBuilder->CreateBlock("dyn.processor");
|
|
deferredProcessor.mContinueBlock = mBfIRBuilder->CreateBlock("dyn.continue");
|
|
deferredProcessor.mDeferredCallEntry = &deferredCallEntry;
|
|
scopeData->mDeferredCallProcessorInstances.Add(deferredProcessor);
|
|
mBfIRBuilder->CreateBr(deferredProcessor.mProcessorBlock);
|
|
|
|
mBfIRBuilder->AddBlock(deferredProcessor.mContinueBlock);
|
|
mBfIRBuilder->SetInsertPoint(deferredProcessor.mContinueBlock);
|
|
SetIllegalSrcPos();
|
|
EmitEnsureInstructionAt();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (deferredCallEntry.mDeferredBlock != NULL)
|
|
{
|
|
// Only show warnings on the first pass
|
|
// For errors, show on the first pass OR as long as we haven't gotten any errors within this method. I'm not sure if there's a case
|
|
// where the first emission succeeds but a subsequent one would fail, but we leave this logic to handle that possibility
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, (deferredCallEntry.mHandlerCount > 1) && (mCurMethodInstance->mHasFailed));
|
|
SetAndRestoreValue<bool> prevIgnoreWarnings(mIgnoreWarnings, (deferredCallEntry.mHandlerCount > 1));
|
|
|
|
BfScopeData scopeData;
|
|
mCurMethodState->AddScope(&scopeData);
|
|
NewScopeState();
|
|
for (auto& capture : deferredCallEntry.mCaptures)
|
|
{
|
|
BfLocalVariable* localVar = new BfLocalVariable();
|
|
localVar->mIsReadOnly = true;
|
|
localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
localVar->mReadFromId = 0;
|
|
localVar->mName = capture.mName;
|
|
localVar->mValue = capture.mValue.mValue;
|
|
localVar->mResolvedType = capture.mValue.mType;
|
|
if ((mBfIRBuilder->DbgHasInfo()) && (!localVar->mResolvedType->IsValuelessType()))
|
|
{
|
|
auto addr = CreateAlloca(localVar->mResolvedType);
|
|
mBfIRBuilder->CreateAlignedStore(localVar->mValue, addr, localVar->mResolvedType->mAlign);
|
|
localVar->mAddr = addr;
|
|
}
|
|
AddLocalVariableDef(localVar, true);
|
|
}
|
|
|
|
SetAndRestoreValue<BfAstNode*> prevCustomAttribute(mCurMethodState->mEmitRefNode, deferredCallEntry.mEmitRefNode);
|
|
VisitEmbeddedStatement(deferredCallEntry.mDeferredBlock, NULL, BfEmbeddedStatementFlags_IsDeferredBlock);
|
|
RestoreScopeState();
|
|
return;
|
|
}
|
|
|
|
auto args = deferredCallEntry.mScopeArgs;
|
|
if (deferredCallEntry.mArgsNeedLoad)
|
|
{
|
|
for (auto& arg : args)
|
|
{
|
|
if (!arg.IsConst())
|
|
arg = mBfIRBuilder->CreateLoad(arg);
|
|
}
|
|
}
|
|
if (deferredCallEntry.mCastThis)
|
|
{
|
|
args[0] = mBfIRBuilder->CreateBitCast(args[0], mBfIRBuilder->MapTypeInstPtr(deferredCallEntry.mModuleMethodInstance.mMethodInstance->GetOwner()));
|
|
}
|
|
|
|
BfDeferredBlockFlags flags = BfDeferredBlockFlag_None;
|
|
if (deferredCallEntry.mBypassVirtual)
|
|
flags = (BfDeferredBlockFlags)(flags | BfDeferredBlockFlag_BypassVirtual);
|
|
if (deferredCallEntry.mDoNullCheck)
|
|
flags = (BfDeferredBlockFlags)(flags | BfDeferredBlockFlag_DoNullChecks | BfDeferredBlockFlag_SkipObjectAccessCheck | BfDeferredBlockFlag_MoveNewBlocksToEnd);
|
|
if (moveBlocks)
|
|
flags = (BfDeferredBlockFlags)(flags | BfDeferredBlockFlag_MoveNewBlocksToEnd);
|
|
if (deferredCallEntry.mIsAllocaFunc)
|
|
flags = (BfDeferredBlockFlags)(flags | BfDeferredBlockFlag_IsAllocaFunc);
|
|
|
|
EmitDeferredCall(deferredCallEntry.mModuleMethodInstance, args, flags);
|
|
}
|
|
|
|
void BfModule::EmitDeferredCallProcessor(BfScopeData* scopeData, SLIList<BfDeferredCallEntry*>& callEntries, BfIRValue callTail)
|
|
{
|
|
int64 collisionId = 0;
|
|
|
|
struct _CallInfo
|
|
{
|
|
BfModuleMethodInstance mModuleMethodInstance;
|
|
bool mBypassVirtual;
|
|
bool mIsAllocaFunc;
|
|
};
|
|
|
|
//typedef std::map<int64, _CallInfo> MapType;
|
|
//MapType methodInstanceMap;
|
|
Dictionary<int64, _CallInfo> methodInstanceMap;
|
|
int blockCount = 0;
|
|
|
|
HashSet<BfMethodInstance*> nullCheckMethodSet;
|
|
|
|
BfDeferredCallEntry* deferredCallEntry = callEntries.mHead;
|
|
while (deferredCallEntry != NULL)
|
|
{
|
|
BfModuleMethodInstance moduleMethodInstance = deferredCallEntry->mModuleMethodInstance;
|
|
int64 methodId = 0;
|
|
if (moduleMethodInstance.mMethodInstance != NULL)
|
|
{
|
|
int64 idHash = moduleMethodInstance.mMethodInstance->mIdHash;
|
|
auto deferredMethodCallData = mDeferredMethodCallData[moduleMethodInstance.mMethodInstance];
|
|
BF_ASSERT(deferredMethodCallData->mMethodId != 0);
|
|
//methodInstanceMap[deferredMethodCallData->mMethodId] = moduleMethodInstance;
|
|
_CallInfo* callInfo = NULL;
|
|
if (methodInstanceMap.TryAdd(deferredMethodCallData->mMethodId, NULL, &callInfo))
|
|
{
|
|
callInfo->mModuleMethodInstance = moduleMethodInstance;
|
|
callInfo->mBypassVirtual = deferredCallEntry->mBypassVirtual;
|
|
callInfo->mIsAllocaFunc = deferredCallEntry->mIsAllocaFunc;
|
|
}
|
|
else
|
|
{
|
|
// Only bypass virtual if ALL these calls are devirtualized
|
|
callInfo->mBypassVirtual &= deferredCallEntry->mBypassVirtual;
|
|
}
|
|
}
|
|
else
|
|
blockCount++;
|
|
if (deferredCallEntry->mDoNullCheck)
|
|
nullCheckMethodSet.Add(deferredCallEntry->mModuleMethodInstance.mMethodInstance);
|
|
deferredCallEntry = deferredCallEntry->mNext;
|
|
}
|
|
|
|
bool moveBlocks = mCurMethodState->mCurScope != mCurMethodState->mTailScope;
|
|
|
|
auto valueScopeStart = ValueScopeStart();
|
|
if (valueScopeStart)
|
|
mBfIRBuilder->SetName(valueScopeStart, "deferredScopeVal");
|
|
|
|
BfIRBlock condBB = mBfIRBuilder->CreateBlock("deferCall.cond", true);
|
|
if (moveBlocks)
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(condBB);
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
|
|
auto deferredCallEntryType = ResolveTypeDef(mCompiler->mDeferredCallTypeDef);
|
|
auto deferredCallEntryTypePtr = CreatePointerType(deferredCallEntryType);
|
|
|
|
BfIRBlock bodyBB = mBfIRBuilder->CreateBlock("deferCall.body");
|
|
if (moveBlocks)
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(bodyBB);
|
|
BfIRBlock endBB = mBfIRBuilder->CreateBlock("deferCall.end");
|
|
if (moveBlocks)
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(endBB);
|
|
|
|
BfIRBlock exitBB = endBB;
|
|
|
|
BfIRValue deferredCallTail;
|
|
|
|
mBfIRBuilder->SetInsertPoint(condBB);
|
|
deferredCallTail = mBfIRBuilder->CreateLoad(callTail);
|
|
auto isNotNull = mBfIRBuilder->CreateIsNotNull(deferredCallTail);
|
|
ValueScopeEnd(valueScopeStart);
|
|
mBfIRBuilder->CreateCondBr(isNotNull, bodyBB, exitBB);
|
|
|
|
mBfIRBuilder->AddBlock(bodyBB);
|
|
mBfIRBuilder->SetInsertPoint(bodyBB);
|
|
|
|
BfIRValue switchInst;
|
|
bool wantsSwitch = ((int)methodInstanceMap.size() + blockCount) > 1;
|
|
if (blockCount > 0)
|
|
{
|
|
// A block may embed a switch so we need a switch whenever we have blocks
|
|
wantsSwitch = true;
|
|
}
|
|
if (mCurMethodState->mCancelledDeferredCall)
|
|
wantsSwitch = true;
|
|
if (wantsSwitch)
|
|
{
|
|
if (IsTargetingBeefBackend())
|
|
deferredCallTail = mBfIRBuilder->CreateLoad(callTail);
|
|
auto idPtr = mBfIRBuilder->CreateInBoundsGEP(deferredCallTail, 0, 1); // mMethodId
|
|
auto id = mBfIRBuilder->CreateLoad(idPtr);
|
|
switchInst = mBfIRBuilder->CreateSwitch(id, exitBB, (int)methodInstanceMap.size());
|
|
ValueScopeEnd(valueScopeStart);
|
|
}
|
|
|
|
BfDeferredCallEntry* prevHead = callEntries.mHead;
|
|
BfDeferredCallEntry* prevCallEntry = NULL;
|
|
|
|
HashSet<BfDeferredCallEntry*> handledSet;
|
|
|
|
deferredCallEntry = callEntries.mHead;
|
|
while (deferredCallEntry != NULL)
|
|
{
|
|
auto block = deferredCallEntry->mDeferredBlock;
|
|
if (block == NULL)
|
|
{
|
|
deferredCallEntry = deferredCallEntry->mNext;
|
|
continue;
|
|
}
|
|
int64 blockId = deferredCallEntry->mBlockId;
|
|
|
|
//auto itr = handledSet.insert(deferredCallEntry);
|
|
//if (!itr.second)
|
|
|
|
if (!handledSet.Add(deferredCallEntry))
|
|
{
|
|
// Already handled, can happen if we defer again within the block
|
|
deferredCallEntry = deferredCallEntry->mNext;
|
|
continue;
|
|
}
|
|
|
|
if (switchInst)
|
|
{
|
|
String caseName = StrFormat("deferCall.%s", BfTypeUtils::HashEncode64(blockId).c_str());
|
|
auto caseBB = mBfIRBuilder->CreateBlock(caseName, true);
|
|
if (moveBlocks)
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(caseBB);
|
|
mBfIRBuilder->AddSwitchCase(switchInst, GetConstValue64(blockId), caseBB);
|
|
mBfIRBuilder->SetInsertPoint(caseBB);
|
|
}
|
|
|
|
// Update .mDeferredAlloca to use the deferredCallTail
|
|
if (IsTargetingBeefBackend())
|
|
deferredCallTail = mBfIRBuilder->CreateLoad(callTail);
|
|
auto nextPtr = mBfIRBuilder->CreateInBoundsGEP(deferredCallTail, 0, 2); // mNext
|
|
auto next = mBfIRBuilder->CreateLoad(nextPtr);
|
|
mBfIRBuilder->CreateStore(next, callTail);
|
|
deferredCallEntry->mDeferredAlloca = mBfIRBuilder->CreateBitCast(deferredCallTail, mBfIRBuilder->GetType(deferredCallEntry->mDeferredAlloca));
|
|
|
|
int dataIdx = 3;
|
|
|
|
// Update .mCaptures to contain the stored values at the time the defer occurred
|
|
for (int captureIdx = 0; captureIdx < (int)deferredCallEntry->mCaptures.size(); captureIdx++)
|
|
{
|
|
auto& capture = deferredCallEntry->mCaptures[captureIdx];
|
|
if (!capture.mValue.mType->IsValuelessType())
|
|
{
|
|
auto gepInstance = mBfIRBuilder->CreateInBoundsGEP(deferredCallEntry->mDeferredAlloca, 0, dataIdx);
|
|
capture.mValue.mValue = mBfIRBuilder->CreateLoad(gepInstance);
|
|
dataIdx++;
|
|
}
|
|
}
|
|
|
|
auto prevHead = callEntries.mHead;
|
|
EmitDeferredCall(scopeData, *deferredCallEntry, moveBlocks);
|
|
ValueScopeEnd(valueScopeStart);
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
|
|
if (prevHead != callEntries.mHead)
|
|
{
|
|
// The list changed, start over and ignore anything we've already handled
|
|
deferredCallEntry = callEntries.mHead;
|
|
}
|
|
else
|
|
deferredCallEntry = deferredCallEntry->mNext;
|
|
}
|
|
|
|
// Blocks may have added new method types, so rebuild map
|
|
if (blockCount > 0)
|
|
{
|
|
deferredCallEntry = callEntries.mHead;
|
|
while (deferredCallEntry != NULL)
|
|
{
|
|
BfModuleMethodInstance moduleMethodInstance = deferredCallEntry->mModuleMethodInstance;
|
|
if (moduleMethodInstance.mMethodInstance != NULL)
|
|
{
|
|
auto deferredMethodCallData = mDeferredMethodCallData[moduleMethodInstance.mMethodInstance];
|
|
//methodInstanceMap.insert(MapType::value_type(deferredMethodCallData->mMethodId, moduleMethodInstance));
|
|
_CallInfo* callInfo = NULL;
|
|
if (methodInstanceMap.TryAdd(deferredMethodCallData->mMethodId, NULL, &callInfo))
|
|
{
|
|
callInfo->mModuleMethodInstance = moduleMethodInstance;
|
|
callInfo->mBypassVirtual = deferredCallEntry->mBypassVirtual;
|
|
}
|
|
}
|
|
deferredCallEntry = deferredCallEntry->mNext;
|
|
}
|
|
}
|
|
|
|
BfExprEvaluator exprEvaluator(this);
|
|
//for (auto itr = methodInstanceMap.begin(); itr != methodInstanceMap.end(); ++itr)
|
|
for (auto& callInfoKV : methodInstanceMap)
|
|
{
|
|
auto moduleMethodInstance = callInfoKV.mValue.mModuleMethodInstance;
|
|
bool bypassVirtual = callInfoKV.mValue.mBypassVirtual;
|
|
bool isAllocaFunc = callInfoKV.mValue.mIsAllocaFunc;
|
|
auto methodInstance = moduleMethodInstance.mMethodInstance;
|
|
auto methodDef = methodInstance->mMethodDef;
|
|
auto methodOwner = methodInstance->mMethodInstanceGroup->mOwner;
|
|
BfIRValue deferredCallInst = deferredCallTail;
|
|
|
|
auto deferredMethodCallData = mDeferredMethodCallData[methodInstance];
|
|
int64 methodId = deferredMethodCallData->mMethodId;
|
|
|
|
if (switchInst)
|
|
{
|
|
String caseName = StrFormat("deferCall.%s", BfTypeUtils::HashEncode64(methodId).c_str());
|
|
auto caseBB = mBfIRBuilder->CreateBlock(caseName, true);
|
|
if (moveBlocks)
|
|
mCurMethodState->mCurScope->mAtEndBlocks.push_back(caseBB);
|
|
mBfIRBuilder->AddSwitchCase(switchInst, GetConstValue64(methodId), caseBB);
|
|
mBfIRBuilder->SetInsertPoint(caseBB);
|
|
}
|
|
|
|
if (IsTargetingBeefBackend())
|
|
deferredCallTail = mBfIRBuilder->CreateLoad(callTail);
|
|
auto nextPtr = mBfIRBuilder->CreateInBoundsGEP(deferredCallTail, 0, 2); // mNext
|
|
auto next = mBfIRBuilder->CreateLoad(nextPtr);
|
|
mBfIRBuilder->CreateStore(next, callTail);
|
|
deferredCallInst = mBfIRBuilder->CreateBitCast(deferredCallTail, deferredMethodCallData->mDeferTypePtr);
|
|
|
|
int paramIdx = 0;
|
|
if (!methodDef->mIsStatic)
|
|
paramIdx = -1;
|
|
|
|
SizedArray<BfIRValue, 8> llvmArgs;
|
|
for (int argIdx = 0; paramIdx < methodInstance->GetParamCount(); argIdx++, paramIdx++)
|
|
{
|
|
auto argPtr = mBfIRBuilder->CreateInBoundsGEP(deferredCallInst, 0, argIdx + 2);
|
|
bool isStruct = false;
|
|
|
|
bool doSplat = methodInstance->GetParamIsSplat(paramIdx);;
|
|
BfTypedValue typedVal;
|
|
if (paramIdx == -1)
|
|
{
|
|
typedVal = BfTypedValue(argPtr, methodOwner, true);
|
|
}
|
|
else
|
|
{
|
|
auto paramType = methodInstance->GetParamType(paramIdx);
|
|
typedVal = BfTypedValue(argPtr, paramType, true);
|
|
}
|
|
|
|
if (doSplat)
|
|
{
|
|
exprEvaluator.SplatArgs(typedVal, llvmArgs);
|
|
continue;
|
|
}
|
|
|
|
if ((argIdx == 0) && (!methodDef->mIsStatic))
|
|
{
|
|
// 'this'
|
|
isStruct = methodOwner->IsValueType();
|
|
}
|
|
else
|
|
{
|
|
while (methodInstance->IsParamSkipped(paramIdx))
|
|
paramIdx++;
|
|
if (paramIdx >= methodInstance->GetParamCount())
|
|
break;
|
|
auto paramType = methodInstance->GetParamType(paramIdx);
|
|
isStruct = paramType->IsStruct();
|
|
}
|
|
|
|
if (isStruct)
|
|
{
|
|
llvmArgs.push_back(argPtr);
|
|
}
|
|
else
|
|
{
|
|
auto arg = mBfIRBuilder->CreateLoad(argPtr);
|
|
llvmArgs.push_back(arg);
|
|
}
|
|
}
|
|
|
|
BfDeferredBlockFlags flags = BfDeferredBlockFlag_None;
|
|
if (moveBlocks)
|
|
flags = (BfDeferredBlockFlags)(flags | BfDeferredBlockFlag_MoveNewBlocksToEnd);
|
|
if (nullCheckMethodSet.Contains(moduleMethodInstance.mMethodInstance))
|
|
flags = (BfDeferredBlockFlags)(flags | BfDeferredBlockFlag_DoNullChecks | BfDeferredBlockFlag_SkipObjectAccessCheck);
|
|
if (bypassVirtual)
|
|
flags = (BfDeferredBlockFlags)(flags | BfDeferredBlockFlag_BypassVirtual);
|
|
if (isAllocaFunc)
|
|
flags = (BfDeferredBlockFlags)(flags | BfDeferredBlockFlag_IsAllocaFunc);
|
|
EmitDeferredCall(moduleMethodInstance, llvmArgs, flags);
|
|
ValueScopeEnd(valueScopeStart);
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
}
|
|
|
|
if (endBB)
|
|
{
|
|
mBfIRBuilder->AddBlock(endBB);
|
|
mBfIRBuilder->SetInsertPoint(endBB);
|
|
}
|
|
}
|
|
|
|
void BfModule::TryInitVar(BfAstNode* checkNode, BfLocalVariable* localVar, BfTypedValue initValue, BfTypedValue& checkResult)
|
|
{
|
|
BF_ASSERT(!localVar->mAddr);
|
|
localVar->mAddr = AllocLocalVariable(localVar->mResolvedType, localVar->mName);
|
|
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
|
|
auto varType = localVar->mResolvedType;
|
|
|
|
AddDependency(varType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference);
|
|
|
|
if (!initValue)
|
|
{
|
|
AssertErrorState();
|
|
checkResult = BfTypedValue(mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), boolType);
|
|
return;
|
|
}
|
|
auto initType = initValue.mType;
|
|
|
|
bool isDynamicCast = false;
|
|
|
|
if (varType->IsGenericParam())
|
|
{
|
|
int pass = 0;
|
|
while (varType->IsGenericParam())
|
|
{
|
|
auto genericParamType = (BfGenericParamType*)varType;
|
|
|
|
auto genericParam = GetGenericParamInstance(genericParamType);
|
|
auto typeConstraint = genericParam->mTypeConstraint;
|
|
if ((typeConstraint == NULL) && (genericParam->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Interface)))
|
|
typeConstraint = mContext->mBfObjectType;
|
|
if (typeConstraint != NULL)
|
|
varType = typeConstraint;
|
|
else
|
|
break;
|
|
if (++pass >= 100) // Sanity - but we should have caught circular error before
|
|
break;
|
|
}
|
|
initValue = GetDefaultTypedValue(varType, false, BfDefaultValueKind_Undef);
|
|
}
|
|
|
|
BfTypeInstance* srcTypeInstance = initValue.mType->ToTypeInstance();
|
|
BfTypeInstance* varTypeInstance = varType->ToTypeInstance();
|
|
|
|
if (CanCast(initValue, varType))
|
|
{
|
|
if ((!varType->IsPointer()) && (!varType->IsObjectOrInterface()) && (!varType->IsVar()))
|
|
{
|
|
if (!IsInSpecializedSection())
|
|
{
|
|
if (initValue.mType != varType)
|
|
Warn(BfWarning_CS0472_ValueTypeNullCompare, StrFormat("Variable declaration is always 'true' because static cast cannot fail and a value of type '%s' can never be null",
|
|
TypeToString(varType).c_str()), checkNode);
|
|
else
|
|
Warn(BfWarning_CS0472_ValueTypeNullCompare, StrFormat("Variable declaration is always 'true' because a value of type '%s' can never be null",
|
|
TypeToString(varType).c_str()), checkNode);
|
|
}
|
|
}
|
|
}
|
|
// else if ((initType->IsInterface()) || (initType == mContext->mBfObjectType))
|
|
// {
|
|
// // Interface or System.Object -> *
|
|
// isDynamicCast = true;
|
|
// }
|
|
// else if ((srcTypeInstance != NULL) && (varTypeInstance != NULL) &&
|
|
// ((srcTypeInstance->IsObject()) && (TypeIsSubTypeOf(varTypeInstance, srcTypeInstance))))
|
|
// {
|
|
// // Class downcast
|
|
// isDynamicCast = true;
|
|
// }
|
|
// else if ((!CanCast(GetFakeTypedValue(varType), initType)) && (!initType->IsGenericParam()))
|
|
// {
|
|
// if (!IsInSpecializedSection())
|
|
// {
|
|
// Fail(StrFormat("Cannot convert type '%s' to '%s' via any conversion",
|
|
// TypeToString(initValue.mType).c_str(), TypeToString(varType).c_str()), checkNode);
|
|
// }
|
|
// }
|
|
|
|
if (!isDynamicCast)
|
|
{
|
|
//initValue = Cast(checkNode, initValue, varType, (BfCastFlags)(BfCastFlags_Explicit | BfCastFlags_SilentFail));
|
|
initValue = Cast(checkNode, initValue, varType);
|
|
|
|
if (!initValue)
|
|
{
|
|
checkResult = BfTypedValue(GetConstValue(0, boolType), boolType);
|
|
}
|
|
else
|
|
{
|
|
if (localVar->mAddr)
|
|
{
|
|
initValue = LoadValue(initValue);
|
|
if (!initValue.mType->IsVar())
|
|
mBfIRBuilder->CreateAlignedStore(initValue.mValue, localVar->mAddr, initValue.mType->mAlign);
|
|
}
|
|
|
|
if (varType->IsVar())
|
|
{
|
|
checkResult = GetDefaultTypedValue(boolType, false, BfDefaultValueKind_Undef);
|
|
}
|
|
else if ((varType->IsPointer()) || (varType->IsObjectOrInterface()))
|
|
{
|
|
checkResult = BfTypedValue(mBfIRBuilder->CreateIsNotNull(initValue.mValue), boolType);
|
|
}
|
|
else
|
|
{
|
|
checkResult = BfTypedValue(GetConstValue(1, boolType), boolType);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (mCompiler->IsAutocomplete())
|
|
{
|
|
auto allocaResult = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(boolType));
|
|
auto val = mBfIRBuilder->CreateLoad(allocaResult);
|
|
checkResult = BfTypedValue(val, boolType);
|
|
return;
|
|
}
|
|
|
|
int wantTypeId = 0;
|
|
if (!varType->IsGenericParam())
|
|
wantTypeId = varType->mTypeId;
|
|
|
|
auto objectType = mContext->mBfObjectType;
|
|
PopulateType(objectType, BfPopulateType_Full);
|
|
|
|
initValue = LoadValue(initValue);
|
|
|
|
auto prevBB = mBfIRBuilder->GetInsertBlock();
|
|
auto matchBB = mBfIRBuilder->CreateBlock("is.match");
|
|
auto endBB = mBfIRBuilder->CreateBlock("is.done");
|
|
|
|
BfIRValue boolResult = CreateAlloca(boolType);
|
|
mBfIRBuilder->CreateStore(mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), boolResult);
|
|
|
|
EmitDynamicCastCheck(initValue, varType, matchBB, endBB);
|
|
|
|
AddBasicBlock(matchBB);
|
|
mBfIRBuilder->CreateStore(mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 1), boolResult);
|
|
mBfIRBuilder->CreateBr(endBB);
|
|
|
|
AddBasicBlock(endBB);
|
|
checkResult = BfTypedValue(mBfIRBuilder->CreateLoad(boolResult), boolType);
|
|
}
|
|
|
|
BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varDecl, BfExprEvaluator* exprEvaluator)
|
|
{
|
|
if (mCurMethodState == NULL)
|
|
{
|
|
Fail("Invalid variable declaration", varDecl);
|
|
return NULL;
|
|
}
|
|
|
|
BfAutoComplete* bfAutocomplete = NULL;
|
|
|
|
// Just a check
|
|
mBfIRBuilder->GetInsertBlock();
|
|
|
|
if (mCompiler->mResolvePassData != NULL)
|
|
bfAutocomplete = mCompiler->mResolvePassData->mAutoComplete;
|
|
if (bfAutocomplete != NULL)
|
|
bfAutocomplete->CheckTypeRef(varDecl->mTypeRef, true, true);
|
|
|
|
bool isConst = (varDecl->mModSpecifier != NULL) && (varDecl->mModSpecifier->GetToken() == BfToken_Const);
|
|
bool isReadOnly = (varDecl->mModSpecifier != NULL) && (varDecl->mModSpecifier->GetToken() == BfToken_ReadOnly);
|
|
bool isStatic = (varDecl->mModSpecifier != NULL) && (varDecl->mModSpecifier->GetToken() == BfToken_Static);
|
|
|
|
BfLocalVariable* localDef = new BfLocalVariable();
|
|
if (varDecl->mNameNode != NULL)
|
|
{
|
|
varDecl->mNameNode->ToString(localDef->mName);
|
|
localDef->mNameNode = BfNodeDynCast<BfIdentifierNode>(varDecl->mNameNode);
|
|
}
|
|
else
|
|
{
|
|
localDef->mName = "val";
|
|
}
|
|
localDef->mIsStatic = isStatic;
|
|
|
|
bool handledExprBoolResult = false;
|
|
bool handledVarInit = false;
|
|
bool handledVarStore = false;
|
|
BfType* unresolvedType = NULL;
|
|
BfType* resolvedType = NULL;
|
|
BfTypedValue initValue;
|
|
bool hadVarType = false;
|
|
bool isLet = varDecl->mTypeRef->IsA<BfLetTypeReference>();
|
|
bool initHandled = false;
|
|
|
|
auto _DoConditionalInit = [&](BfType* expectedType)
|
|
{
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
|
|
auto _EmitCond = [&](BfIRValue condVal, BfTypedValue initValue)
|
|
{
|
|
initValue = Cast(varDecl->mInitializer, initValue, expectedType);
|
|
if (!initValue)
|
|
return;
|
|
|
|
initHandled = true;
|
|
|
|
if (localDef->mIsReadOnly)
|
|
{
|
|
if ((initValue.IsReadOnly()) ||
|
|
(initValue.mKind == BfTypedValueKind_TempAddr))
|
|
{
|
|
localDef->mAddr = initValue.mValue;
|
|
exprEvaluator->mResult = BfTypedValue(condVal, boolType);
|
|
return;
|
|
}
|
|
}
|
|
|
|
localDef->mAddr = AllocLocalVariable(resolvedType, localDef->mName);
|
|
|
|
auto doAssignBlock = mBfIRBuilder->CreateBlock("assign");
|
|
auto skipAssignBlock = mBfIRBuilder->CreateBlock("skipAssign");
|
|
|
|
auto insertBlock = mBfIRBuilder->GetInsertBlock();
|
|
mBfIRBuilder->CreateCondBr(condVal, doAssignBlock, skipAssignBlock);
|
|
|
|
mBfIRBuilder->AddBlock(doAssignBlock);
|
|
mBfIRBuilder->SetInsertPoint(doAssignBlock);
|
|
|
|
initValue = LoadValue(initValue);
|
|
mBfIRBuilder->CreateStore(initValue.mValue, localDef->mAddr);
|
|
mBfIRBuilder->CreateBr(skipAssignBlock);
|
|
|
|
mBfIRBuilder->AddBlock(skipAssignBlock);
|
|
mBfIRBuilder->SetInsertPoint(skipAssignBlock);
|
|
|
|
auto phiVal = mBfIRBuilder->CreatePhi(mBfIRBuilder->MapType(boolType), 2);
|
|
mBfIRBuilder->AddPhiIncoming(phiVal, mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), insertBlock);
|
|
mBfIRBuilder->AddPhiIncoming(phiVal, mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 1), doAssignBlock);
|
|
exprEvaluator->mResult = BfTypedValue(phiVal, boolType);
|
|
};
|
|
|
|
bool handled = false;
|
|
if ((initValue) && (initValue.mType->IsPayloadEnum()))
|
|
{
|
|
auto typeInst = initValue.mType->ToTypeInstance();
|
|
PopulateType(typeInst);
|
|
BfType* outType = NULL;
|
|
int tagId = -1;
|
|
if (typeInst->GetResultInfo(outType, tagId))
|
|
{
|
|
int dscDataIdx = -1;
|
|
auto dscType = typeInst->GetDiscriminatorType(&dscDataIdx);
|
|
|
|
BfIRValue dscVal = ExtractValue(initValue, dscDataIdx);
|
|
auto eqVal = mBfIRBuilder->CreateCmpEQ(dscVal, GetConstValue(tagId, dscType));
|
|
exprEvaluator->mResult = BfTypedValue(eqVal, boolType);
|
|
|
|
PopulateType(outType);
|
|
if (!outType->IsValuelessType())
|
|
{
|
|
auto outPtrType = CreatePointerType(outType);
|
|
|
|
initValue = MakeAddressable(initValue);
|
|
auto payloadVal = mBfIRBuilder->CreateBitCast(initValue.mValue, mBfIRBuilder->MapType(outPtrType));
|
|
auto payload = BfTypedValue(payloadVal, outType, true);
|
|
if ((initValue.mKind == BfTypedValueKind_ReadOnlyAddr) ||
|
|
(initValue.mKind == BfTypedValueKind_TempAddr) ||
|
|
(initValue.mKind == BfTypedValueKind_ReadOnlyTempAddr))
|
|
payload.mKind = initValue.mKind;
|
|
_EmitCond(eqVal, payload);
|
|
}
|
|
|
|
handled = true;
|
|
}
|
|
}
|
|
|
|
if (handled)
|
|
{
|
|
handledExprBoolResult = true;
|
|
handledVarInit = true;
|
|
handledVarStore = true;
|
|
}
|
|
else if ((initValue) && (initValue.mType->IsNullable()))
|
|
{
|
|
auto underlyingType = initValue.mType->GetUnderlyingType();
|
|
exprEvaluator->mResult = BfTypedValue(ExtractValue(initValue, 2), boolType);
|
|
handledExprBoolResult = true;
|
|
|
|
if (!resolvedType->IsNullable())
|
|
{
|
|
if (initValue.IsAddr())
|
|
initValue = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(initValue.mValue, 0, 1), initValue.mType->GetUnderlyingType(), true);
|
|
else
|
|
initValue = BfTypedValue(mBfIRBuilder->CreateExtractValue(initValue.mValue, 1), initValue.mType->GetUnderlyingType());
|
|
}
|
|
|
|
if ((initValue) && (!initValue.mType->IsValuelessType()))
|
|
{
|
|
_EmitCond(exprEvaluator->mResult.mValue, initValue);
|
|
handledVarStore = true;
|
|
}
|
|
|
|
handledVarInit = true;
|
|
}
|
|
else if (initValue)
|
|
{
|
|
BfAstNode* refNode = varDecl;
|
|
if (varDecl->mInitializer != NULL)
|
|
refNode = varDecl->mInitializer;
|
|
TryInitVar(refNode, localDef, initValue, exprEvaluator->mResult);
|
|
handledExprBoolResult = true;
|
|
handledVarInit = true;
|
|
handledVarStore = true;
|
|
}
|
|
};
|
|
|
|
if ((varDecl->mTypeRef->IsA<BfVarTypeReference>()) || (isLet))
|
|
{
|
|
hadVarType = true;
|
|
if (varDecl->mInitializer == NULL)
|
|
{
|
|
if (!isLet)
|
|
{
|
|
BfLocalVarEntry* shadowEntry;
|
|
if (mCurMethodState->mLocalVarSet.TryGet(BfLocalVarEntry(localDef), &shadowEntry))
|
|
{
|
|
auto prevLocal = shadowEntry->mLocalVar;
|
|
if (prevLocal->mLocalVarIdx >= mCurMethodState->GetLocalStartIdx())
|
|
{
|
|
BfExprEvaluator exprEvaluator(this);
|
|
initValue = exprEvaluator.LoadLocal(prevLocal);
|
|
|
|
resolvedType = initValue.mType;
|
|
unresolvedType = resolvedType;
|
|
localDef->mLocalVarId = prevLocal->mLocalVarId;
|
|
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
localDef->mIsShadow = true;
|
|
|
|
exprEvaluator.mResultLocalVarRefNode = varDecl->mNameNode;
|
|
exprEvaluator.mResultLocalVar = prevLocal;
|
|
exprEvaluator.CheckResultForReading(initValue);
|
|
|
|
if (bfAutocomplete != NULL)
|
|
bfAutocomplete->CheckVarResolution(varDecl->mTypeRef, resolvedType);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!initValue)
|
|
{
|
|
Fail("Implicitly-typed variables must be initialized", varDecl);
|
|
initValue = GetDefaultTypedValue(mContext->mBfObjectType);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isConst)
|
|
{
|
|
BfConstResolver constResolver(this);
|
|
initValue = constResolver.Resolve(varDecl->mInitializer);
|
|
}
|
|
else
|
|
{
|
|
BfExprEvaluator valExprEvaluator(this);
|
|
valExprEvaluator.mAllowReadOnlyReference = isLet;
|
|
initValue = CreateValueFromExpression(valExprEvaluator, varDecl->mInitializer, NULL, (BfEvalExprFlags)(BfEvalExprFlags_AllowSplat | BfEvalExprFlags_AllowRefExpr | BfEvalExprFlags_VariableDeclaration));
|
|
if ((exprEvaluator != NULL) && (initValue))
|
|
{
|
|
if (initValue.mType->IsNullable())
|
|
{
|
|
auto nullableElementType = initValue.mType->GetUnderlyingType();
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
initValue = LoadValue(initValue);
|
|
exprEvaluator->mResult = BfTypedValue(mBfIRBuilder->CreateExtractValue(initValue.mValue, nullableElementType->IsValuelessType() ? 1 : 2), boolType);
|
|
handledExprBoolResult = true;
|
|
if (!nullableElementType->IsValuelessType())
|
|
initValue = BfTypedValue(mBfIRBuilder->CreateExtractValue(initValue.mValue, 1), initValue.mType->GetUnderlyingType());
|
|
else
|
|
initValue = BfTypedValue(mBfIRBuilder->GetFakeVal(), nullableElementType);
|
|
}
|
|
else
|
|
{
|
|
auto typeInst = initValue.mType->ToTypeInstance();
|
|
if (typeInst != NULL)
|
|
{
|
|
PopulateType(typeInst);
|
|
BfType* outType = NULL;
|
|
int tagId = -1;
|
|
if (typeInst->GetResultInfo(outType, tagId))
|
|
{
|
|
handledExprBoolResult = true;
|
|
unresolvedType = outType;
|
|
resolvedType = outType;
|
|
isReadOnly = isLet;
|
|
localDef->mIsReadOnly = isLet;
|
|
_DoConditionalInit(outType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!initValue)
|
|
{
|
|
initValue = GetDefaultTypedValue(GetPrimitiveType(BfTypeCode_Var));
|
|
}
|
|
if (initValue.mType->IsNull())
|
|
{
|
|
Fail("Implicitly-typed variables cannot be initialized to 'null'", varDecl->mInitializer);
|
|
initValue = GetDefaultTypedValue(mContext->mBfObjectType);
|
|
}
|
|
if (unresolvedType == NULL)
|
|
unresolvedType = initValue.mType;
|
|
resolvedType = unresolvedType;
|
|
|
|
if ((initValue.IsTempAddr()) && (!localDef->mAddr) && (initValue.mType == resolvedType))
|
|
{
|
|
// Take over value
|
|
localDef->mAddr = initValue.mValue;
|
|
handledVarInit = true;
|
|
|
|
if (isLet)
|
|
{
|
|
localDef->mValue = mBfIRBuilder->CreateLoad(localDef->mAddr);
|
|
}
|
|
}
|
|
|
|
if (bfAutocomplete != NULL)
|
|
bfAutocomplete->CheckVarResolution(varDecl->mTypeRef, resolvedType);
|
|
}
|
|
else
|
|
{
|
|
BfTypeState typeState;
|
|
typeState.mCurVarInitializer = varDecl->mInitializer;
|
|
SetAndRestoreValue<BfTypeState*> prevTypeState(mContext->mCurTypeState, &typeState);
|
|
|
|
BfResolveTypeRefFlags flags = (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_AllowRef);
|
|
if (varDecl->mInitializer != NULL)
|
|
flags = (BfResolveTypeRefFlags)(flags | BfResolveTypeRefFlag_AllowInferredSizedArray);
|
|
|
|
unresolvedType = ResolveTypeRef(varDecl->mTypeRef, BfPopulateType_Data, flags);
|
|
if (unresolvedType == NULL)
|
|
unresolvedType = GetPrimitiveType(BfTypeCode_Var);
|
|
resolvedType = unresolvedType;
|
|
}
|
|
|
|
auto _CheckConst = [&]
|
|
{
|
|
if (initValue.IsAddr())
|
|
{
|
|
isConst = false;
|
|
}
|
|
else if (initValue.mValue.IsConst())
|
|
{
|
|
auto constant = mBfIRBuilder->GetConstant(initValue.mValue);
|
|
|
|
// NullPtr is stand-in for GlobalVar during autocomplete
|
|
if ((constant->mConstType == BfConstType_GlobalVar) ||
|
|
(constant->mTypeCode == BfTypeCode_NullPtr))
|
|
{
|
|
// Not really constant
|
|
// localNeedsAddr = false;
|
|
// isConst = false;
|
|
// initHandled = true;
|
|
// localDef->mValue = initValue.mValue;
|
|
if (GetStringPoolIdx(initValue.mValue, mBfIRBuilder) == -1)
|
|
isConst = false;
|
|
}
|
|
}
|
|
};
|
|
|
|
localDef->mResolvedType = resolvedType;
|
|
localDef->mIsReadOnly = isReadOnly;
|
|
|
|
if (!initHandled)
|
|
{
|
|
if (isLet)
|
|
{
|
|
localDef->mIsReadOnly = true;
|
|
if (initValue)
|
|
{
|
|
if ((initValue.mValue) && (initValue.mValue.IsConst()))
|
|
{
|
|
isConst = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_CheckConst();
|
|
|
|
bool localNeedsAddr = false;
|
|
bool allowValueAccess = true;
|
|
if (mHasFullDebugInfo)
|
|
{
|
|
//if (!IsTargetingBeefBackend())
|
|
|
|
if (!isConst)
|
|
localNeedsAddr = true;
|
|
|
|
/*if (mCurMethodInstance->mMethodDef->mName != "Boop2")
|
|
dbgNeedsAddr = true;*/
|
|
}
|
|
|
|
// This is required because of lifetime and LLVM domination rules for certain instances of variable declarations in binary conditionals.
|
|
// IE: if ((something) && (let a = somethingElse))
|
|
if ((exprEvaluator != NULL) && (!isConst))
|
|
{
|
|
localNeedsAddr = true;
|
|
allowValueAccess = false;
|
|
}
|
|
|
|
if ((varDecl->mEqualsNode != NULL) && (mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL) && (!initHandled))
|
|
{
|
|
mCompiler->mResolvePassData->mAutoComplete->CheckEmptyStart(varDecl->mEqualsNode, resolvedType);
|
|
}
|
|
|
|
BfIRInitType initType = BfIRInitType_NotSet;
|
|
|
|
if (varDecl->mInitializer != NULL)
|
|
{
|
|
initType = BfIRInitType_NotNeeded_AliveOnDecl;
|
|
|
|
if ((!initValue) && (!initHandled))
|
|
{
|
|
if ((isConst) || (isStatic))
|
|
{
|
|
BfConstResolver constResolver(this);
|
|
initValue = constResolver.Resolve(varDecl->mInitializer, resolvedType, BfConstResolveFlag_ActualizeValues);
|
|
if (!initValue)
|
|
initValue = GetDefaultTypedValue(resolvedType);
|
|
}
|
|
else if (varDecl->mInitializer->IsA<BfUninitializedExpression>())
|
|
{
|
|
// Fake 'is assigned'
|
|
}
|
|
else
|
|
{ //
|
|
auto expectedType = resolvedType;
|
|
if (expectedType->IsRef())
|
|
expectedType = expectedType->GetUnderlyingType();
|
|
|
|
BfExprEvaluator valExprEvaluator(this);
|
|
valExprEvaluator.mAllowReadOnlyReference = isReadOnly;
|
|
initValue = CreateValueFromExpression(valExprEvaluator, varDecl->mInitializer, expectedType, (BfEvalExprFlags)(BfEvalExprFlags_NoCast | BfEvalExprFlags_AllowRefExpr | BfEvalExprFlags_VariableDeclaration));
|
|
|
|
if ((initValue) && (resolvedType->IsUndefSizedArray()))
|
|
{
|
|
int stringId = GetStringPoolIdx(initValue.mValue, mBfIRBuilder);
|
|
if (stringId >= 0)
|
|
{
|
|
BfStringPoolEntry* entry = NULL;
|
|
if (mContext->mStringObjectIdMap.TryGetValue(stringId, &entry))
|
|
resolvedType = CreateSizedArrayType(((BfSizedArrayType*)resolvedType)->mElementType, entry->mString.mLength);
|
|
}
|
|
}
|
|
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
|
|
if (exprEvaluator != NULL)
|
|
_DoConditionalInit(expectedType);
|
|
|
|
if ((!handledVarInit) && (initValue))
|
|
initValue = Cast(varDecl->mInitializer, initValue, resolvedType, BfCastFlags_PreferAddr);
|
|
}
|
|
|
|
if ((initValue) && (resolvedType->IsUndefSizedArray()))
|
|
{
|
|
resolvedType = initValue.mType;
|
|
unresolvedType = resolvedType;
|
|
}
|
|
|
|
if (auto autoComplete = mCompiler->GetAutoComplete())
|
|
{
|
|
autoComplete->CheckResult(varDecl->mInitializer, initValue);
|
|
}
|
|
}
|
|
if ((!handledVarInit) && (!isConst))
|
|
{
|
|
if (initValue)
|
|
{
|
|
// Handled later
|
|
}
|
|
else if (varDecl->mInitializer->IsA<BfUninitializedExpression>())
|
|
{
|
|
// Fake 'is assigned'
|
|
initType = BfIRInitType_Uninitialized;
|
|
}
|
|
else
|
|
{
|
|
AssertErrorState();
|
|
}
|
|
}
|
|
|
|
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
}
|
|
else
|
|
{
|
|
if (isConst)
|
|
{
|
|
Fail("Const locals must be initialized", varDecl->mModSpecifier);
|
|
initValue = GetDefaultTypedValue(resolvedType, true);
|
|
}
|
|
else if (isReadOnly)
|
|
{
|
|
Fail("Readonly locals must be initialized", varDecl->mModSpecifier);
|
|
initValue = GetDefaultTypedValue(resolvedType, true);
|
|
}
|
|
else if (auto refTypeRef = BfNodeDynCast<BfRefTypeRef>(varDecl->mTypeRef))
|
|
{
|
|
Fail("Ref locals must be initialized", refTypeRef->mRefToken);
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(!resolvedType->IsRef());
|
|
}
|
|
}
|
|
|
|
PopulateType(resolvedType);
|
|
AddDependency(resolvedType, mCurTypeInstance, BfDependencyMap::DependencyFlag_LocalUsage);
|
|
|
|
localDef->mResolvedType = resolvedType;
|
|
|
|
_CheckConst();
|
|
|
|
if ((initValue.mKind == BfTypedValueKind_TempAddr) && (!initHandled))
|
|
{
|
|
BF_ASSERT(initValue.IsAddr());
|
|
BF_ASSERT(initValue.mType->IsComposite());
|
|
|
|
handledVarInit = true;
|
|
handledVarStore = true;
|
|
|
|
if (!localDef->mAddr)
|
|
{
|
|
localDef->mAddr = initValue.mValue;
|
|
if (localDef->mIsReadOnly)
|
|
localDef->mValue = mBfIRBuilder->CreateLoad(localDef->mAddr);
|
|
}
|
|
if (WantsLifetimes())
|
|
mCurMethodState->mCurScope->mDeferredLifetimeEnds.push_back(localDef->mAddr);
|
|
}
|
|
|
|
if ((!localDef->mAddr) && (!isConst) && (!isStatic) && ((!localDef->mIsReadOnly) || (localNeedsAddr)))
|
|
{
|
|
if ((exprEvaluator != NULL) && (exprEvaluator->mResultIsTempComposite))
|
|
{
|
|
//TODO: Can we remove this one?
|
|
BF_ASSERT(initValue.IsAddr());
|
|
BF_ASSERT(initValue.mType->IsComposite());
|
|
localDef->mAddr = initValue.mValue;
|
|
}
|
|
else
|
|
localDef->mAddr = AllocLocalVariable(resolvedType, localDef->mName);
|
|
}
|
|
|
|
if (isStatic)
|
|
{
|
|
String name = mModuleName + "_" + mCurMethodInstance->mMethodDef->mName + "_" + localDef->mName;
|
|
|
|
HashContext closureHashCtx;
|
|
closureHashCtx.Mixin(varDecl->mSrcStart);
|
|
uint64 closureHash = closureHashCtx.Finish64();
|
|
name += "$";
|
|
name += BfTypeUtils::HashEncode64(closureHash);
|
|
|
|
initValue = LoadValue(initValue);
|
|
if ((initValue) && (!initValue.mValue.IsConst()))
|
|
{
|
|
Fail("Static local variables can only be initialized with a const value", varDecl->mInitializer);
|
|
initValue = BfTypedValue();
|
|
}
|
|
|
|
if (!initValue)
|
|
initValue = GetDefaultTypedValue(localDef->mResolvedType);
|
|
|
|
if (!localDef->mResolvedType->IsValuelessType())
|
|
localDef->mAddr = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapType(localDef->mResolvedType), false, BfIRLinkageType_Internal, initValue.mValue, name);;
|
|
initHandled = true;
|
|
}
|
|
|
|
bool wantsStore = false;
|
|
if ((initValue) && (!handledVarStore) && (!isConst) && (!initHandled))
|
|
{
|
|
initValue = LoadValue(initValue);
|
|
if (initValue.IsSplat())
|
|
{
|
|
BF_ASSERT(!mIsComptimeModule);
|
|
if (!localDef->mAddr)
|
|
localDef->mAddr = AllocLocalVariable(resolvedType, localDef->mName);
|
|
AggregateSplatIntoAddr(initValue, localDef->mAddr);
|
|
initHandled = true;
|
|
}
|
|
else
|
|
{
|
|
initValue = AggregateSplat(initValue);
|
|
localDef->mValue = initValue.mValue;
|
|
if ((localDef->mAddr) && (!localDef->mResolvedType->IsValuelessType()))
|
|
{
|
|
if ((!initValue.mType->IsVar()) && (!initValue.mType->IsValuelessType()))
|
|
wantsStore = true;
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(isReadOnly || isLet || initValue.mType->IsValuelessType() || (mBfIRBuilder->mIgnoreWrites));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((localDef->mIsReadOnly) && (!isConst) && (!localDef->mValue) && (!initHandled))
|
|
{
|
|
if (!resolvedType->IsValuelessType())
|
|
{
|
|
AssertErrorState();
|
|
initValue = GetDefaultTypedValue(resolvedType, true, BfDefaultValueKind_Undef);
|
|
localDef->mValue = initValue.mValue;
|
|
}
|
|
}
|
|
|
|
if ((!localDef->mAddr) && (!isConst) && ((!localDef->mIsReadOnly) || (localNeedsAddr)))
|
|
{
|
|
localDef->mAddr = AllocLocalVariable(resolvedType, localDef->mName);
|
|
}
|
|
|
|
if ((exprEvaluator != NULL) && (!handledExprBoolResult))
|
|
{
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
if (!initValue)
|
|
{
|
|
AssertErrorState();
|
|
exprEvaluator->mResult = BfTypedValue(mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), boolType);
|
|
}
|
|
else if ((resolvedType->IsPointer()) || (resolvedType->IsObjectOrInterface()))
|
|
{
|
|
exprEvaluator->mResult = BfTypedValue(mBfIRBuilder->CreateIsNotNull(initValue.mValue), boolType);
|
|
}
|
|
else if (resolvedType->IsVar())
|
|
{
|
|
exprEvaluator->mResult = GetDefaultTypedValue(GetPrimitiveType(BfTypeCode_Boolean), false, BfDefaultValueKind_Undef);
|
|
}
|
|
else
|
|
{
|
|
// Always true
|
|
if ((!IsInSpecializedSection()) && (!resolvedType->IsGenericParam()))
|
|
Warn(BfWarning_CS0472_ValueTypeNullCompare, StrFormat("Variable declaration is always 'true' since a value of type '%s' can never be null",
|
|
TypeToString(initValue.mType).c_str()), varDecl);
|
|
exprEvaluator->mResult = BfTypedValue(mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 1), boolType);
|
|
}
|
|
}
|
|
|
|
if ((unresolvedType->IsGenericParam()) && (resolvedType->IsValuelessType()) && (mHasFullDebugInfo))
|
|
{
|
|
// We do this in order to be able to bind to lines that contain valueless variable declarations in some generics
|
|
// We don't need to do this in non-generics because the breakpoint will just move to the next line that actually has instructions
|
|
EmitEnsureInstructionAt();
|
|
}
|
|
|
|
if ((resolvedType->IsVoid()) && (!IsInSpecializedSection()))
|
|
{
|
|
Warn(0, StrFormat("Variable '%s' is declared as 'void'", localDef->mName.c_str()), varDecl->mTypeRef);
|
|
}
|
|
|
|
if ((!handledVarInit) && (isConst))
|
|
localDef->mConstValue = initValue.mValue;
|
|
|
|
if (!allowValueAccess)
|
|
localDef->mValue = BfIRValue();
|
|
|
|
if (!localDef->mIsShadow)
|
|
CheckVariableDef(localDef);
|
|
|
|
ValidateAllocation(localDef->mResolvedType, varDecl->mTypeRef);
|
|
|
|
if ((exprEvaluator == NULL) && (varDecl->GetSourceData() != NULL))
|
|
UpdateSrcPos(varDecl);
|
|
localDef->Init();
|
|
|
|
if (localDef->mConstValue)
|
|
initType = BfIRInitType_NotNeeded;
|
|
|
|
BfLocalVariable* localVar = AddLocalVariableDef(localDef, true, false, BfIRValue(), initType);
|
|
if (wantsStore)
|
|
mBfIRBuilder->CreateAlignedStore(initValue.mValue, localVar->mAddr, localVar->mResolvedType->mAlign);
|
|
|
|
if ((mCurMethodState->mConstResolveState != NULL) && (mCurMethodState->mConstResolveState->mInCalcAppend))
|
|
{
|
|
if (localDef->mValue.IsConst())
|
|
localDef->mConstValue = localDef->mValue;
|
|
}
|
|
|
|
return localVar;
|
|
}
|
|
|
|
BfLocalVariable* BfModule::HandleVariableDeclaration(BfType* type, BfAstNode* nameNode, BfTypedValue val, bool updateSrcLoc, bool forceAddr)
|
|
{
|
|
BfLocalVariable* localDef = new BfLocalVariable();
|
|
nameNode->ToString(localDef->mName);
|
|
localDef->mNameNode = BfNodeDynCast<BfIdentifierNode>(nameNode);
|
|
localDef->mResolvedType = type;
|
|
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
localDef->mValue = val.mValue;
|
|
|
|
if ((!localDef->mIsReadOnly) || (mHasFullDebugInfo))
|
|
{
|
|
localDef->mAddr = AllocLocalVariable(localDef->mResolvedType, localDef->mName);
|
|
if ((val.mValue) && (!localDef->mResolvedType->IsValuelessType()) && (!localDef->mResolvedType->IsVar()))
|
|
{
|
|
if (localDef->mResolvedType->IsRef())
|
|
val = MakeAddressable(val, true, true);
|
|
|
|
if (val.IsSplat())
|
|
{
|
|
AggregateSplatIntoAddr(val, localDef->mAddr);
|
|
}
|
|
else
|
|
mBfIRBuilder->CreateAlignedStore(val.mValue, localDef->mAddr, localDef->mResolvedType->mAlign);
|
|
}
|
|
}
|
|
|
|
CheckVariableDef(localDef);
|
|
|
|
if (nameNode->GetSourceData() != NULL)
|
|
UpdateSrcPos(nameNode);
|
|
localDef->Init();
|
|
return AddLocalVariableDef(localDef, true);
|
|
}
|
|
|
|
BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varDecl, BfTypedValue val, bool updateSrcLoc, bool forceAddr)
|
|
{
|
|
if (varDecl->mEqualsNode != NULL)
|
|
Fail("Unexpected initialization", varDecl->mEqualsNode);
|
|
if (varDecl->mInitializer != NULL)
|
|
CreateValueFromExpression(varDecl->mInitializer);
|
|
|
|
auto isLet = varDecl->mTypeRef->IsA<BfLetTypeReference>();
|
|
auto isVar = varDecl->mTypeRef->IsA<BfVarTypeReference>();
|
|
bool isRef = false;
|
|
|
|
if (auto varRefTypeReference = BfNodeDynCast<BfVarRefTypeReference>(varDecl->mTypeRef))
|
|
{
|
|
isRef = true;
|
|
isLet = varRefTypeReference->mVarToken->GetToken() == BfToken_Let;
|
|
isVar = varRefTypeReference->mVarToken->GetToken() == BfToken_Var;
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(!val.IsAddr());
|
|
}
|
|
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
if ((autoComplete != NULL) && ((isLet) || (isVar)))
|
|
autoComplete->CheckVarResolution(varDecl->mTypeRef, val.mType);
|
|
|
|
// BfType* type = val.mType;
|
|
// if (type == NULL)
|
|
// {
|
|
// type = ResolveTypeRef(varDecl->mTypeRef);
|
|
// if (type == NULL)
|
|
// type = mContext->mBfObjectType;
|
|
// }
|
|
|
|
BfType* type = NULL;
|
|
if ((isLet) || (isVar))
|
|
{
|
|
type = val.mType;
|
|
}
|
|
else
|
|
{
|
|
type = ResolveTypeRef(varDecl->mTypeRef);
|
|
}
|
|
if (type == NULL)
|
|
{
|
|
type = GetPrimitiveType(BfTypeCode_Var);
|
|
val = GetDefaultTypedValue(type);
|
|
}
|
|
|
|
if ((type->IsVar()) || (type->IsLet()))
|
|
{
|
|
}
|
|
|
|
if (isRef)
|
|
{
|
|
type = CreateRefType(type);
|
|
}
|
|
|
|
BfLocalVariable* localDef = new BfLocalVariable();
|
|
if (varDecl->mNameNode != NULL)
|
|
varDecl->mNameNode->ToString(localDef->mName);
|
|
localDef->mNameNode = BfNodeDynCast<BfIdentifierNode>(varDecl->mNameNode);
|
|
localDef->mResolvedType = type;
|
|
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
localDef->mValue = val.mValue;
|
|
if (isLet)
|
|
{
|
|
localDef->mIsReadOnly = true;
|
|
}
|
|
|
|
if ((!localDef->mIsReadOnly) || (mHasFullDebugInfo) || (forceAddr))
|
|
{
|
|
localDef->mAddr = AllocLocalVariable(localDef->mResolvedType, localDef->mName);
|
|
if ((val.mValue) && (!localDef->mResolvedType->IsValuelessType()) && (!localDef->mResolvedType->IsVar()))
|
|
{
|
|
if (localDef->mResolvedType->IsRef())
|
|
val = MakeAddressable(val, true, true);
|
|
|
|
if (val.IsSplat())
|
|
{
|
|
AggregateSplatIntoAddr(val, localDef->mAddr);
|
|
}
|
|
else
|
|
mBfIRBuilder->CreateAlignedStore(val.mValue, localDef->mAddr, localDef->mResolvedType->mAlign);
|
|
}
|
|
if (forceAddr)
|
|
localDef->mValue = BfIRValue();
|
|
}
|
|
|
|
CheckVariableDef(localDef);
|
|
|
|
if ((updateSrcLoc) && (varDecl->GetSourceData() != NULL))
|
|
UpdateSrcPos(varDecl);
|
|
localDef->Init();
|
|
return AddLocalVariableDef(localDef, true);
|
|
}
|
|
|
|
void BfModule::CheckTupleVariableDeclaration(BfTupleExpression* tupleExpr, BfType* initType)
|
|
{
|
|
if (initType == NULL)
|
|
return;
|
|
|
|
BfTypeInstance* initTupleType = NULL;
|
|
if ((initType != NULL) && (initType->IsTuple()))
|
|
initTupleType = (BfTypeInstance*)initType;
|
|
|
|
if (initTupleType != NULL)
|
|
{
|
|
mBfIRBuilder->PopulateType(initTupleType);
|
|
AddDependency(initTupleType, mCurTypeInstance, BfDependencyMap::DependencyFlag_LocalUsage);
|
|
|
|
int paramCountDiff = (int)tupleExpr->mValues.size() - (int)initTupleType->mFieldInstances.size();
|
|
if (paramCountDiff > 0)
|
|
{
|
|
Fail(StrFormat("Too many variable names, expected %d fewer.", paramCountDiff), tupleExpr->mValues[(int)initTupleType->mFieldInstances.size()]);
|
|
}
|
|
else if (paramCountDiff < 0)
|
|
{
|
|
BfAstNode* refNode = tupleExpr->mCloseParen;
|
|
if (refNode == NULL)
|
|
refNode = tupleExpr;
|
|
Fail(StrFormat("Too few variable names, expected %d more.", -paramCountDiff), refNode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Fail(StrFormat("Value result type '%s' must be a tuple type to be applicable for tuple decomposition", TypeToString(initType).c_str()), tupleExpr);
|
|
}
|
|
}
|
|
|
|
void BfModule::HandleTupleVariableDeclaration(BfVariableDeclaration* varDecl, BfTupleExpression* tupleExpr, BfTypedValue initTupleValue, bool isReadOnly, bool isConst, bool forceAddr, BfIRBlock* declBlock)
|
|
{
|
|
BfTypeInstance* initTupleType = NULL;
|
|
if ((initTupleValue) && (initTupleValue.mType->IsTuple()))
|
|
initTupleType = (BfTypeInstance*)initTupleValue.mType;
|
|
|
|
CheckTupleVariableDeclaration(tupleExpr, initTupleValue.mType);
|
|
mBfIRBuilder->PopulateType(initTupleValue.mType);
|
|
|
|
for (int varIdx = 0; varIdx < (int)tupleExpr->mValues.size(); varIdx++)
|
|
{
|
|
BfType* resolvedType = NULL;
|
|
BfTypedValue initValue;
|
|
if ((initTupleType != NULL) && (varIdx < (int)initTupleType->mFieldInstances.size()))
|
|
{
|
|
auto fieldInstance = &initTupleType->mFieldInstances[varIdx];
|
|
auto fieldDef = fieldInstance->GetFieldDef();
|
|
resolvedType = fieldInstance->GetResolvedType();
|
|
if (fieldInstance->mDataIdx != -1)
|
|
{
|
|
if (initTupleValue.IsAddr())
|
|
{
|
|
initValue = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(initTupleValue.mValue, 0, fieldInstance->mDataIdx), resolvedType, true);
|
|
initValue = LoadValue(initValue);
|
|
}
|
|
else
|
|
initValue = ExtractValue(initTupleValue, fieldInstance, fieldInstance->mDataIdx);
|
|
}
|
|
|
|
BfTupleNameNode* tupleNameNode = NULL;
|
|
if (varIdx < (int)tupleExpr->mNames.size())
|
|
tupleNameNode = tupleExpr->mNames[varIdx];
|
|
|
|
if (!fieldDef->IsUnnamedTupleField())
|
|
{
|
|
if (tupleNameNode != NULL)
|
|
{
|
|
if (fieldDef->mName != tupleNameNode->mNameNode->ToString())
|
|
{
|
|
Fail(StrFormat("Mismatched tuple field name, expected '%s'", fieldDef->mName.c_str()), tupleNameNode->mNameNode);
|
|
}
|
|
}
|
|
}
|
|
else if ((tupleNameNode != NULL) && (tupleNameNode->mNameNode != NULL))
|
|
{
|
|
Fail(StrFormat("Unexpected tuple field name, expected unnamed tuple field", fieldDef->mName.c_str()), tupleNameNode->mNameNode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
resolvedType = mContext->mBfObjectType;
|
|
initValue = GetDefaultTypedValue(resolvedType);
|
|
}
|
|
|
|
BfExpression* varNameNode = tupleExpr->mValues[varIdx];
|
|
if (!varNameNode->IsExact<BfIdentifierNode>())
|
|
{
|
|
if (BfTupleExpression* innerTupleExpr = BfNodeDynCast<BfTupleExpression>(varNameNode))
|
|
{
|
|
HandleTupleVariableDeclaration(varDecl, innerTupleExpr, initValue, isReadOnly, isConst, false, declBlock);
|
|
}
|
|
else if (!varNameNode->IsExact<BfUninitializedExpression>())
|
|
Fail("Variable name expected", varNameNode);
|
|
continue;
|
|
}
|
|
|
|
bool initHandled = false;
|
|
|
|
BfLocalVariable* localDef = new BfLocalVariable();
|
|
varNameNode->ToString(localDef->mName);
|
|
localDef->mNameNode = BfNodeDynCast<BfIdentifierNode>(varNameNode);
|
|
localDef->mResolvedType = resolvedType;
|
|
localDef->mReadFromId = 0; // Don't give usage errors for binds
|
|
if (isReadOnly)
|
|
{
|
|
localDef->mIsReadOnly = true;
|
|
if ((initValue) && (initValue.mValue.IsConst()))
|
|
{
|
|
isConst = true;
|
|
}
|
|
}
|
|
CheckVariableDef(localDef);
|
|
|
|
if ((!isConst) && ((forceAddr) || (!localDef->mIsReadOnly) || (mHasFullDebugInfo)))
|
|
{
|
|
localDef->mAddr = AllocLocalVariable(resolvedType, localDef->mName);
|
|
}
|
|
|
|
if ((varDecl != NULL) && (varDecl->mEqualsNode != NULL) && (mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL) && (!initHandled))
|
|
{
|
|
mCompiler->mResolvePassData->mAutoComplete->CheckEmptyStart(varDecl->mEqualsNode, resolvedType);
|
|
}
|
|
|
|
if ((varDecl == NULL) || (varDecl->mInitializer != NULL))
|
|
{
|
|
if ((!isConst) && (!initHandled))
|
|
{
|
|
if (initValue)
|
|
{
|
|
if (!forceAddr)
|
|
localDef->mValue = initValue.mValue;
|
|
if (localDef->mAddr)
|
|
{
|
|
mBfIRBuilder->CreateStore(initValue.mValue, localDef->mAddr);
|
|
}
|
|
}
|
|
else if ((varDecl == NULL) || (varDecl->mInitializer->IsA<BfUninitializedExpression>()))
|
|
{
|
|
// Fake 'is assigned'
|
|
}
|
|
else
|
|
{
|
|
AssertErrorState();
|
|
}
|
|
}
|
|
|
|
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
}
|
|
else if ((varDecl != NULL) && (varDecl->mInitializer == NULL))
|
|
{
|
|
if (auto refTypeRef = BfNodeDynCast<BfRefTypeRef>(varDecl->mTypeRef))
|
|
{
|
|
Fail("Ref variables must be initialized", refTypeRef->mRefToken);
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(!localDef->mResolvedType->IsRef());
|
|
}
|
|
}
|
|
|
|
if (isConst)
|
|
localDef->mConstValue = initValue.mValue;
|
|
|
|
CheckVariableDef(localDef);
|
|
|
|
if ((varDecl != NULL) && (varDecl->GetSourceData() != NULL))
|
|
UpdateSrcPos(varDecl);
|
|
localDef->Init();
|
|
|
|
auto defBlock = mBfIRBuilder->GetInsertBlock();
|
|
if (declBlock != NULL)
|
|
mBfIRBuilder->SetInsertPoint(*declBlock);
|
|
AddLocalVariableDef(localDef, true, false, BfIRValue(), BfIRInitType_NotNeeded);
|
|
if (declBlock != NULL)
|
|
mBfIRBuilder->SetInsertPoint(defBlock);
|
|
}
|
|
}
|
|
|
|
void BfModule::HandleTupleVariableDeclaration(BfVariableDeclaration* varDecl)
|
|
{
|
|
BfAutoComplete* bfAutocomplete = mCompiler->GetAutoComplete();
|
|
if (bfAutocomplete != NULL)
|
|
bfAutocomplete->CheckTypeRef(varDecl->mTypeRef, true);
|
|
|
|
BfTupleExpression* tupleExpr = BfNodeDynCast<BfTupleExpression>(varDecl->mNameNode);
|
|
|
|
bool isConst = (varDecl->mModSpecifier != NULL) && (varDecl->mModSpecifier->GetToken() == BfToken_Const);
|
|
bool isReadOnly = (varDecl->mModSpecifier != NULL) && (varDecl->mModSpecifier->GetToken() == BfToken_ReadOnly);
|
|
|
|
BfTypedValue initTupleValue;
|
|
bool hadVarType = false;
|
|
bool isLet = varDecl->mTypeRef->IsA<BfLetTypeReference>();
|
|
bool isVar = varDecl->mTypeRef->IsA<BfVarTypeReference>();
|
|
bool wasVarOrLet = isVar || isLet;
|
|
|
|
if ((!isLet) && (!isVar))
|
|
{
|
|
ResolveTypeRef(varDecl->mTypeRef);
|
|
Fail("'var' or 'let' expected", varDecl->mTypeRef);
|
|
isVar = true;
|
|
}
|
|
|
|
if ((isVar) || (isLet))
|
|
{
|
|
hadVarType = true;
|
|
if (varDecl->mInitializer == NULL)
|
|
{
|
|
Fail("Implicitly-typed variables must be initialized", varDecl);
|
|
initTupleValue = GetDefaultTypedValue(mContext->mBfObjectType);
|
|
}
|
|
else
|
|
{
|
|
if (isConst)
|
|
{
|
|
BfConstResolver constResolver(this);
|
|
initTupleValue = constResolver.Resolve(varDecl->mInitializer);
|
|
}
|
|
else
|
|
{
|
|
initTupleValue = CreateValueFromExpression(varDecl->mInitializer, NULL);
|
|
}
|
|
}
|
|
|
|
initTupleValue = LoadValue(initTupleValue);
|
|
|
|
if ((bfAutocomplete != NULL) && (wasVarOrLet))
|
|
bfAutocomplete->CheckVarResolution(varDecl->mTypeRef, initTupleValue.mType);
|
|
}
|
|
|
|
bool isCompatible = false;
|
|
if (initTupleValue)
|
|
HandleTupleVariableDeclaration(varDecl, tupleExpr, initTupleValue, isLet || isReadOnly, isConst, false);
|
|
else
|
|
AssertErrorState();
|
|
}
|
|
|
|
void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArray<BfExpression*>& arguments, BfAstNode* tooFewRef, BfIRValue phiVal, BfIRBlock& matchedBlockStart, BfIRBlock& matchedBlockEnd, BfIRBlock& falseBlockStart, BfIRBlock& falseBlockEnd, bool& hadConditional, bool clearOutOnMismatch, bool prevHadFallthrough)
|
|
{
|
|
SetAndRestoreValue<bool> prevInCondBlock(mCurMethodState->mInConditionalBlock);
|
|
|
|
auto tupleType = tupleVal.mType->ToTypeInstance();
|
|
|
|
struct DeferredAssign
|
|
{
|
|
BfExpression* mExpr;
|
|
BfTypedValue mArgValue;
|
|
BfTypedValue mTupleElement;
|
|
int mFieldIdx;
|
|
bool mIsOut;
|
|
};
|
|
Array<DeferredAssign> deferredAssigns;
|
|
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
|
|
for (int tupleFieldIdx = 0; tupleFieldIdx < (int)tupleType->mFieldInstances.size(); tupleFieldIdx++)
|
|
{
|
|
auto tupleFieldInstance = &tupleType->mFieldInstances[tupleFieldIdx];
|
|
|
|
if (tupleFieldIdx >= arguments.size())
|
|
{
|
|
BfError* error = Fail(StrFormat("Not enough parameters specified, expected %d more.", tupleType->mFieldInstances.size() - (int)arguments.size()), tooFewRef);
|
|
break;
|
|
}
|
|
|
|
BfTypedValue tupleElement;
|
|
if (tupleFieldInstance->mDataIdx >= 0)
|
|
{
|
|
tupleElement = ExtractValue(tupleVal, tupleFieldInstance, tupleFieldInstance->mDataIdx);
|
|
}
|
|
else
|
|
tupleElement = GetDefaultTypedValue(tupleFieldInstance->GetResolvedType());
|
|
|
|
auto expr = BfNodeDynCast<BfExpression>(arguments[tupleFieldIdx]);
|
|
if (auto varDecl = BfNodeDynCast<BfVariableDeclaration>(expr))
|
|
{
|
|
bool isVarOrLet = (varDecl->mTypeRef->IsExact<BfLetTypeReference>()) || (varDecl->mTypeRef->IsExact<BfVarTypeReference>());
|
|
bool isRef = false;
|
|
if (varDecl->mTypeRef->IsExact<BfVarRefTypeReference>())
|
|
{
|
|
isVarOrLet = true;
|
|
isRef = true;
|
|
}
|
|
|
|
if (!isVarOrLet)
|
|
{
|
|
auto wantType = ResolveTypeRef(varDecl->mTypeRef);
|
|
if (wantType == NULL)
|
|
wantType = mContext->mBfObjectType;
|
|
if (wantType != NULL)
|
|
tupleElement = Cast(varDecl->mTypeRef, tupleElement, wantType);
|
|
if (!tupleElement)
|
|
tupleElement = GetDefaultTypedValue(wantType);
|
|
}
|
|
|
|
PopulateType(tupleElement.mType);
|
|
if (!isRef)
|
|
tupleElement = LoadValue(tupleElement);
|
|
|
|
if (prevHadFallthrough)
|
|
Fail("Destructuring cannot be used when the previous case contains a fallthrough", expr);
|
|
|
|
CreateOutVariable(expr, varDecl, NULL, tupleElement.mType, tupleElement);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (auto binOpExpr = BfNodeDynCast<BfBinaryOperatorExpression>(expr))
|
|
{
|
|
if (binOpExpr->mOp == BfBinaryOp_Multiply)
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreError(mIgnoreErrors, true);
|
|
auto resolvedType = ResolveTypeRef(binOpExpr->mLeft, NULL);
|
|
prevIgnoreError.Restore();
|
|
if (resolvedType != NULL)
|
|
{
|
|
resolvedType = CreatePointerType(resolvedType);
|
|
|
|
PopulateType(tupleElement.mType);
|
|
tupleElement = LoadValue(tupleElement);
|
|
tupleElement = Cast(binOpExpr->mLeft, tupleElement, resolvedType);
|
|
|
|
if (prevHadFallthrough)
|
|
Fail("Destructuring cannot be used when the previous case contains a fallthrough", expr);
|
|
|
|
auto localVar = HandleVariableDeclaration(resolvedType, binOpExpr->mRight, tupleElement, false, true);
|
|
localVar->mReadFromId = 0; // Don't give usage errors for binds
|
|
auto curScope = mCurMethodState->mCurScope;
|
|
if (curScope->mScopeKind == BfScopeKind_StatementTarget)
|
|
MoveLocalToParentScope(localVar);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto uninitExpr = BfNodeDynCast<BfUninitializedExpression>(expr))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (tupleFieldInstance->mDataIdx >= 0)
|
|
{
|
|
if (auto tupleExpr = BfNodeDynCast<BfTupleExpression>(expr))
|
|
{
|
|
if (tupleElement.mType->IsTuple())
|
|
{
|
|
BfAstNode* tooFewRef = tupleExpr->mCloseParen;
|
|
if (tupleExpr->mValues.size() > 0)
|
|
tooFewRef = tupleExpr->mValues[tupleExpr->mValues.size() - 1];
|
|
if (tooFewRef == NULL)
|
|
tooFewRef = tupleExpr->mOpenParen;
|
|
HandleCaseEnumMatch_Tuple(tupleElement, tupleExpr->mValues, tooFewRef, phiVal, matchedBlockStart, matchedBlockEnd, falseBlockStart, falseBlockEnd, hadConditional, clearOutOnMismatch, prevHadFallthrough);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (expr == NULL)
|
|
{
|
|
// Error would have occured in the parser
|
|
//AssertErrorState();
|
|
}
|
|
else
|
|
{
|
|
mCurMethodState->mInConditionalBlock = true;
|
|
auto tupleElementAddr = tupleElement;
|
|
|
|
BfTypedValue exprResult;
|
|
|
|
if (auto invocationExpr = BfNodeDynCast<BfInvocationExpression>(expr))
|
|
{
|
|
if (auto memberRefExpr = BfNodeDynCast<BfMemberReferenceExpression>(invocationExpr->mTarget))
|
|
{
|
|
if (memberRefExpr->mTarget == NULL)
|
|
{
|
|
if (tupleElement.mType->IsPayloadEnum())
|
|
{
|
|
auto intType = GetPrimitiveType(BfTypeCode_Int32);
|
|
BfTypedValue enumTagVal;
|
|
if (tupleElement.IsAddr())
|
|
{
|
|
enumTagVal = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(tupleElement.mValue, 0, 2), intType, true);
|
|
enumTagVal = LoadValue(enumTagVal);
|
|
}
|
|
else
|
|
enumTagVal = BfTypedValue(mBfIRBuilder->CreateExtractValue(tupleElement.mValue, 2), intType, false);
|
|
|
|
int uncondTagId = -1;
|
|
bool hadConditional = false;
|
|
exprResult = TryCaseEnumMatch(tupleElementAddr, enumTagVal, expr, NULL, NULL, NULL, uncondTagId, hadConditional, clearOutOnMismatch, prevHadFallthrough);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!exprResult)
|
|
{
|
|
tupleElement = LoadValue(tupleElement);
|
|
|
|
bool isMatchedBlockEnd = matchedBlockEnd == mBfIRBuilder->GetInsertBlock();
|
|
bool isFalseBlockEnd = falseBlockEnd == mBfIRBuilder->GetInsertBlock();
|
|
|
|
BfExprEvaluator exprEvaluator(this);
|
|
exprEvaluator.mExpectingType = tupleFieldInstance->GetResolvedType();
|
|
exprEvaluator.mBfEvalExprFlags = (BfEvalExprFlags)(BfEvalExprFlags_AllowOutExpr | BfEvalExprFlags_AllowRefExpr);
|
|
if (mCurMethodState->mDeferredLocalAssignData != NULL)
|
|
{
|
|
SetAndRestoreValue<bool> prevIsIfCondition(mCurMethodState->mDeferredLocalAssignData->mIsIfCondition, true);
|
|
SetAndRestoreValue<bool> prevIfMayBeSkipped(mCurMethodState->mDeferredLocalAssignData->mIfMayBeSkipped, true);
|
|
exprEvaluator.Evaluate(expr);
|
|
}
|
|
else
|
|
{
|
|
exprEvaluator.Evaluate(expr);
|
|
}
|
|
if (isMatchedBlockEnd)
|
|
matchedBlockEnd = mBfIRBuilder->GetInsertBlock();
|
|
if (isFalseBlockEnd)
|
|
falseBlockEnd = mBfIRBuilder->GetInsertBlock();
|
|
auto argValue = exprEvaluator.mResult;
|
|
if (!argValue)
|
|
continue;
|
|
|
|
if (argValue.mType->IsRef())
|
|
{
|
|
auto refType = (BfRefType*)argValue.mType;
|
|
bool isOut = refType->mRefKind == BfRefType::RefKind_Out;
|
|
if ((refType->mRefKind != BfRefType::RefKind_Out) && (refType->mRefKind != BfRefType::RefKind_Ref))
|
|
{
|
|
BfAstNode* refNode = expr;
|
|
if (auto unaryOperatorExpr = BfNodeDynCast<BfUnaryOperatorExpression>(expr))
|
|
refNode = unaryOperatorExpr->mOpToken;
|
|
Fail("Invalid ref type", refNode);
|
|
}
|
|
|
|
DeferredAssign deferredAssign = { expr, argValue, tupleElement, tupleFieldIdx, isOut };
|
|
deferredAssigns.push_back(deferredAssign);
|
|
|
|
if (isOut)
|
|
{
|
|
if (mCurMethodState->mDeferredLocalAssignData != NULL)
|
|
{
|
|
SetAndRestoreValue<bool> prevIsIfCondition(mCurMethodState->mDeferredLocalAssignData->mIsIfCondition, true);
|
|
SetAndRestoreValue<bool> prevIfMayBeSkipped(mCurMethodState->mDeferredLocalAssignData->mIfMayBeSkipped, true);
|
|
exprEvaluator.MarkResultAssigned();
|
|
}
|
|
else
|
|
{
|
|
exprEvaluator.MarkResultAssigned();
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!argValue.mType->IsValueType())
|
|
argValue = LoadValue(argValue);
|
|
argValue = Cast(expr, argValue, tupleFieldInstance->GetResolvedType());
|
|
if (!argValue)
|
|
continue;
|
|
|
|
exprEvaluator.PerformBinaryOperation(expr, expr, BfBinaryOp_Equality, expr, BfBinOpFlag_NoClassify, tupleElement, argValue);
|
|
exprResult = exprEvaluator.mResult;
|
|
}
|
|
|
|
if (exprResult)
|
|
{
|
|
hadConditional = true;
|
|
if (phiVal)
|
|
{
|
|
auto insertBlock = mBfIRBuilder->GetInsertBlock();
|
|
mBfIRBuilder->AddPhiIncoming(phiVal, mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), insertBlock);
|
|
}
|
|
|
|
matchedBlockStart = matchedBlockEnd = mBfIRBuilder->CreateBlock("match", false);
|
|
|
|
mBfIRBuilder->CreateCondBr(exprResult.mValue, matchedBlockStart, falseBlockStart);
|
|
mBfIRBuilder->AddBlock(matchedBlockStart);
|
|
mBfIRBuilder->SetInsertPoint(matchedBlockStart);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!deferredAssigns.empty())
|
|
mBfIRBuilder->SetInsertPoint(matchedBlockEnd);
|
|
|
|
// We assign these only after the value checks succeed
|
|
for (auto& deferredAssign : deferredAssigns)
|
|
{
|
|
auto argValue = RemoveRef(deferredAssign.mArgValue);
|
|
auto tupleElement = Cast(deferredAssign.mExpr, deferredAssign.mTupleElement, argValue.mType);
|
|
if (!tupleElement)
|
|
continue;
|
|
tupleElement = LoadOrAggregateValue(tupleElement);
|
|
if (!tupleElement.mType->IsValuelessType())
|
|
mBfIRBuilder->CreateStore(tupleElement.mValue, argValue.mValue);
|
|
}
|
|
|
|
if ((clearOutOnMismatch) && (!deferredAssigns.IsEmpty()))
|
|
{
|
|
auto curInsertPoint = mBfIRBuilder->GetInsertBlock();
|
|
mBfIRBuilder->SetInsertPoint(falseBlockEnd);
|
|
for (auto& deferredAssign : deferredAssigns)
|
|
{
|
|
if (!deferredAssign.mIsOut)
|
|
continue;
|
|
|
|
auto tupleFieldInstance = &tupleType->mFieldInstances[deferredAssign.mFieldIdx];
|
|
// We have to re-process the expr because we haven't done it in this branch, and then clear the result out
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, mHadBuildError); // Don't fail twice
|
|
BfExprEvaluator exprEvaluator(this);
|
|
exprEvaluator.mExpectingType = tupleFieldInstance->GetResolvedType();
|
|
exprEvaluator.mBfEvalExprFlags = BfEvalExprFlags_AllowOutExpr;
|
|
exprEvaluator.Evaluate(deferredAssign.mExpr);
|
|
auto argValue = exprEvaluator.mResult;
|
|
if (!argValue)
|
|
continue;
|
|
mBfIRBuilder->CreateMemSet(argValue.mValue, GetConstValue8(0), GetConstValue(argValue.mType->mSize), GetConstValue(argValue.mType->mAlign));
|
|
}
|
|
falseBlockEnd = mBfIRBuilder->GetInsertBlock();
|
|
mBfIRBuilder->SetInsertPoint(curInsertPoint);
|
|
}
|
|
|
|
if (arguments.size() > tupleType->mFieldInstances.size())
|
|
{
|
|
for (int i = (int)tupleType->mFieldInstances.size(); i < (int)arguments.size(); i++)
|
|
{
|
|
// For autocomplete and such
|
|
auto expr = arguments[i];
|
|
if (expr != NULL)
|
|
CreateValueFromExpression(expr);
|
|
}
|
|
|
|
BfAstNode* errorRef = arguments[(int)tupleType->mFieldInstances.size()];
|
|
BfError* error = Fail(StrFormat("Too many arguments, expected %d fewer.", arguments.size() - tupleType->mFieldInstances.size()), errorRef);
|
|
}
|
|
}
|
|
|
|
BfTypedValue BfModule::TryCaseTupleMatch(BfTypedValue tupleVal, BfTupleExpression* tupleExpr, BfIRBlock* eqBlock, BfIRBlock* notEqBlock, BfIRBlock* matchBlock, bool& hadConditional, bool clearOutOnMismatch, bool prevHadFallthrough)
|
|
{
|
|
if (!tupleVal.mType->IsTuple())
|
|
return BfTypedValue();
|
|
|
|
auto tupleType = (BfTypeInstance*)tupleVal.mType;
|
|
|
|
BfAstNode* tooFewRef = tupleExpr->mCloseParen;
|
|
if ((tooFewRef == NULL) && (!tupleExpr->mCommas.IsEmpty()))
|
|
tooFewRef = tupleExpr->mCommas[tupleExpr->mCommas.size() - 1];
|
|
else if (tooFewRef == NULL)
|
|
tooFewRef = tupleExpr->mOpenParen;
|
|
|
|
///
|
|
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
|
|
bool wasCapturingMethodInfo = false;
|
|
if (autoComplete != NULL)
|
|
{
|
|
wasCapturingMethodInfo = autoComplete->mIsCapturingMethodMatchInfo;
|
|
autoComplete->CheckInvocation(tupleExpr, tupleExpr->mOpenParen, tupleExpr->mCloseParen, tupleExpr->mCommas);
|
|
|
|
if (autoComplete->mIsCapturingMethodMatchInfo)
|
|
{
|
|
autoComplete->mMethodMatchInfo->mInstanceList.Clear();
|
|
|
|
auto methodMatchInfo = autoComplete->mMethodMatchInfo;
|
|
|
|
// auto methodDef = tupleType->mTypeDef->mMethods[0];
|
|
//
|
|
// BfAutoComplete::MethodMatchEntry methodMatchEntry;
|
|
// methodMatchEntry.mMethodDef = methodDef;
|
|
// methodMatchEntry.mTypeInstance = tupleType;
|
|
// methodMatchEntry.mCurMethodInstance = mCurMethodInstance;
|
|
// //methodMatchEntry.mPayloadEnumField = fieldInstance;
|
|
//autoComplete->mMethodMatchInfo->mInstanceList.push_back(methodMatchEntry);
|
|
|
|
methodMatchInfo->mBestIdx = 0;
|
|
methodMatchInfo->mMostParamsMatched = 0;
|
|
|
|
int cursorIdx = tupleExpr->GetParser()->mCursorIdx;
|
|
if ((tupleExpr->mCloseParen == NULL) || (cursorIdx <= tupleExpr->mCloseParen->GetSrcStart()))
|
|
{
|
|
int paramIdx = 0;
|
|
for (int commaIdx = 0; commaIdx < (int)tupleExpr->mCommas.size(); commaIdx++)
|
|
{
|
|
auto commaNode = tupleExpr->mCommas[commaIdx];
|
|
if ((commaNode != NULL) && (cursorIdx >= commaNode->GetSrcStart()))
|
|
paramIdx = commaIdx + 1;
|
|
}
|
|
|
|
bool isEmpty = true;
|
|
|
|
if (paramIdx < (int)tupleExpr->mValues.size())
|
|
{
|
|
auto paramNode = tupleExpr->mValues[paramIdx];
|
|
if (paramNode != NULL)
|
|
isEmpty = false;
|
|
}
|
|
|
|
if (isEmpty)
|
|
{
|
|
if (paramIdx < (int)tupleType->mFieldInstances.size())
|
|
{
|
|
auto fieldDef = tupleType->mFieldInstances[paramIdx].GetFieldDef();
|
|
String insertStr;
|
|
if (fieldDef->IsUnnamedTupleField())
|
|
insertStr = "p";
|
|
insertStr += fieldDef->mName;
|
|
|
|
insertStr.Insert(0, "let ");
|
|
autoComplete->mEntriesSet.Clear();
|
|
autoComplete->AddEntry(AutoCompleteEntry("paramName", insertStr));
|
|
autoComplete->mInsertStartIdx = cursorIdx;
|
|
autoComplete->mInsertEndIdx = cursorIdx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
defer
|
|
(
|
|
if (autoComplete != NULL)
|
|
autoComplete->mIsCapturingMethodMatchInfo = (wasCapturingMethodInfo) && (!autoComplete->mIsCapturingMethodMatchInfo);
|
|
);
|
|
|
|
///
|
|
|
|
//BfIRValue phiVal;
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
//phiVal = mBfIRBuilder->CreatePhi(mBfIRBuilder->MapType(boolType), 2);
|
|
|
|
auto startBlock = mBfIRBuilder->GetInsertBlock();
|
|
//auto dscrType = enumType->GetDiscriminatorType();
|
|
//BfIRValue eqResult = mBfIRBuilder->CreateCmpEQ(tagVal.mValue, mBfIRBuilder->CreateConst(dscrType->mTypeDef->mTypeCode, tagId));
|
|
|
|
BfIRBlock falseBlockStart;
|
|
BfIRBlock falseBlockEnd;
|
|
BfIRBlock doneBlockStart;
|
|
if (notEqBlock != NULL)
|
|
doneBlockStart = *notEqBlock;
|
|
else
|
|
doneBlockStart = mBfIRBuilder->CreateBlock("caseDone", false);
|
|
BfIRBlock doneBlockEnd = doneBlockStart;
|
|
|
|
if (clearOutOnMismatch)
|
|
{
|
|
falseBlockStart = falseBlockEnd = mBfIRBuilder->CreateBlock("caseNotEq", false);
|
|
mBfIRBuilder->AddBlock(falseBlockStart);
|
|
}
|
|
|
|
BfIRBlock matchedBlockStart = mBfIRBuilder->CreateBlock("caseMatch", false);
|
|
if (matchBlock != NULL)
|
|
*matchBlock = matchedBlockStart;
|
|
mBfIRBuilder->CreateBr(matchedBlockStart);
|
|
mBfIRBuilder->AddBlock(matchedBlockStart);
|
|
|
|
mBfIRBuilder->SetInsertPoint(doneBlockEnd);
|
|
BfIRValue phiVal;
|
|
if (eqBlock == NULL)
|
|
phiVal = mBfIRBuilder->CreatePhi(mBfIRBuilder->MapType(boolType), 2);
|
|
|
|
mBfIRBuilder->SetInsertPoint(matchedBlockStart);
|
|
BfIRBlock matchedBlockEnd = matchedBlockStart;
|
|
HandleCaseEnumMatch_Tuple(tupleVal, tupleExpr->mValues, tooFewRef, falseBlockStart ? BfIRValue() : phiVal, matchedBlockStart, matchedBlockEnd,
|
|
falseBlockStart ? falseBlockStart : doneBlockStart, falseBlockEnd ? falseBlockEnd : doneBlockEnd, hadConditional, clearOutOnMismatch, prevHadFallthrough);
|
|
|
|
if (phiVal)
|
|
{
|
|
auto falseVal = mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0);
|
|
if (falseBlockEnd)
|
|
mBfIRBuilder->AddPhiIncoming(phiVal, falseVal, falseBlockEnd);
|
|
|
|
auto trueVal = mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 1);
|
|
mBfIRBuilder->AddPhiIncoming(phiVal, trueVal, matchedBlockEnd);
|
|
}
|
|
|
|
if (eqBlock != NULL)
|
|
mBfIRBuilder->CreateBr(*eqBlock);
|
|
else
|
|
mBfIRBuilder->CreateBr(doneBlockStart);
|
|
|
|
if (falseBlockEnd)
|
|
{
|
|
mBfIRBuilder->SetInsertPoint(falseBlockEnd);
|
|
mBfIRBuilder->CreateBr(doneBlockStart);
|
|
//mBfIRBuilder->AddPhiIncoming(phiVal, mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), falseBlock);
|
|
}
|
|
|
|
mBfIRBuilder->AddBlock(doneBlockStart);
|
|
mBfIRBuilder->SetInsertPoint(doneBlockEnd);
|
|
|
|
if (phiVal)
|
|
return BfTypedValue(phiVal, boolType);
|
|
else
|
|
return GetDefaultTypedValue(boolType);
|
|
}
|
|
|
|
BfTypedValue BfModule::TryCaseEnumMatch(BfTypedValue enumVal, BfTypedValue tagVal, BfExpression* expr, BfIRBlock* eqBlock, BfIRBlock* notEqBlock, BfIRBlock* matchBlock, int& tagId, bool& hadConditional, bool clearOutOnMismatch, bool prevHadFallthrough)
|
|
{
|
|
auto invocationExpr = BfNodeDynCast<BfInvocationExpression>(expr);
|
|
if (invocationExpr == NULL)
|
|
return BfTypedValue();
|
|
|
|
auto activeTypeDef = GetActiveTypeDef();
|
|
|
|
BfType* targetType = NULL;
|
|
BfIdentifierNode* nameNode = NULL;
|
|
BfTokenNode* dotNode = NULL;
|
|
if (auto memberRefExpr = BfNodeDynCast<BfMemberReferenceExpression>(invocationExpr->mTarget))
|
|
{
|
|
if (memberRefExpr->mTarget == NULL)
|
|
{
|
|
targetType = enumVal.mType;
|
|
}
|
|
else if (auto typeRef = BfNodeDynCast<BfTypeReference>(memberRefExpr->mTarget))
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
|
|
targetType = ResolveTypeRef(typeRef);
|
|
}
|
|
else if (auto identifier = BfNodeDynCast<BfIdentifierNode>(memberRefExpr->mTarget))
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
|
|
targetType = ResolveTypeRef(identifier, NULL);
|
|
}
|
|
if (auto nameIdentifier = BfNodeDynCast<BfIdentifierNode>(memberRefExpr->mMemberName))
|
|
{
|
|
dotNode = memberRefExpr->mDotToken;
|
|
nameNode = nameIdentifier;
|
|
}
|
|
else
|
|
return BfTypedValue();
|
|
}
|
|
else if (auto qualifiedNameNode = BfNodeDynCast<BfQualifiedNameNode>(invocationExpr->mTarget))
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
|
|
targetType = ResolveTypeRef(qualifiedNameNode->mLeft, NULL);
|
|
nameNode = qualifiedNameNode->mRight;
|
|
}
|
|
else if (auto identiferNode = BfNodeDynCast<BfIdentifierNode>(invocationExpr->mTarget))
|
|
{
|
|
targetType = mCurTypeInstance;
|
|
nameNode = identiferNode;
|
|
}
|
|
else
|
|
return BfTypedValue();
|
|
|
|
// These may have been colorized as methods, so change that
|
|
SetElementType(nameNode, BfSourceElementType_Normal);
|
|
|
|
if ((targetType == NULL) || (!targetType->IsPayloadEnum()))
|
|
return BfTypedValue();
|
|
|
|
auto enumType = targetType->ToTypeInstance();
|
|
PopulateType(enumType);
|
|
|
|
StringT<128> enumCaseName;
|
|
if (nameNode != NULL)
|
|
nameNode->ToString(enumCaseName);
|
|
|
|
auto tagType = GetPrimitiveType(BfTypeCode_Int32);
|
|
if (enumVal.mType != enumType)
|
|
{
|
|
Fail(StrFormat("Cannot match enum type '%s' with type '%s'",
|
|
TypeToString(enumVal.mType).c_str(), TypeToString(enumType).c_str()));
|
|
enumVal = GetDefaultTypedValue(enumType);
|
|
tagVal = GetDefaultTypedValue(tagType);
|
|
}
|
|
|
|
BfIRValue eqResult;
|
|
for (int fieldIdx = 0; fieldIdx < (int)enumType->mFieldInstances.size(); fieldIdx++)
|
|
{
|
|
auto fieldInstance = &enumType->mFieldInstances[fieldIdx];
|
|
auto fieldDef = fieldInstance->GetFieldDef();
|
|
if (fieldDef == NULL)
|
|
continue;
|
|
if ((fieldInstance->mIsEnumPayloadCase) && (fieldDef->mName == enumCaseName))
|
|
{
|
|
if (!IsInSpecializedSection())
|
|
{
|
|
if ((!enumType->IsTypeMemberIncluded(fieldDef->mDeclaringType, activeTypeDef, this)) ||
|
|
(!enumType->IsTypeMemberAccessible(fieldDef->mDeclaringType, activeTypeDef)))
|
|
continue;
|
|
}
|
|
|
|
auto resolvePassData = mCompiler->mResolvePassData;
|
|
if (resolvePassData != NULL)
|
|
{
|
|
if (resolvePassData->mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Field)
|
|
resolvePassData->HandleFieldReference(nameNode, enumType->mTypeDef, fieldDef);
|
|
String filter;
|
|
auto autoComplete = resolvePassData->mAutoComplete;
|
|
if ((autoComplete != NULL) && (autoComplete->InitAutocomplete(dotNode, nameNode, filter)))
|
|
autoComplete->AddEnumTypeMembers(enumType, enumCaseName, false, enumType == mCurTypeInstance);
|
|
}
|
|
|
|
BF_ASSERT(fieldInstance->mResolvedType->IsTuple());
|
|
auto tupleType = (BfTypeInstance*)fieldInstance->mResolvedType;
|
|
PopulateType(tupleType);
|
|
mBfIRBuilder->PopulateType(tupleType);
|
|
|
|
if (tupleType->IsDeleting())
|
|
{
|
|
mCompiler->RequestExtraCompile();
|
|
InternalError("TryCaseEnumMatch using deleted type", expr);
|
|
return BfTypedValue();
|
|
}
|
|
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
tagId = -fieldInstance->mDataIdx - 1;
|
|
|
|
auto startBlock = mBfIRBuilder->GetInsertBlock();
|
|
auto dscrType = enumType->GetDiscriminatorType();
|
|
BfIRValue eqResult = mBfIRBuilder->CreateCmpEQ(tagVal.mValue, mBfIRBuilder->CreateConst(dscrType->mTypeDef->mTypeCode, tagId));
|
|
|
|
bool isConstMatch = false;
|
|
bool isConstIgnore = false;
|
|
if (auto constant = mBfIRBuilder->GetConstant(eqResult))
|
|
{
|
|
isConstMatch = constant->mBool;
|
|
isConstIgnore = !constant->mBool;
|
|
}
|
|
|
|
SetAndRestoreValue<bool> prevIgnoreWrite(mBfIRBuilder->mIgnoreWrites);
|
|
if (isConstIgnore)
|
|
mBfIRBuilder->mIgnoreWrites = true;
|
|
|
|
BfIRBlock falseBlockStart;
|
|
BfIRBlock falseBlockEnd;
|
|
BfIRBlock doneBlockStart;
|
|
BfIRBlock doneBlockEnd;
|
|
if (notEqBlock != NULL)
|
|
doneBlockStart = doneBlockEnd = *notEqBlock;
|
|
else
|
|
doneBlockStart = doneBlockEnd = mBfIRBuilder->CreateBlock("caseDone", false);
|
|
|
|
if (clearOutOnMismatch)
|
|
{
|
|
falseBlockStart = falseBlockEnd = mBfIRBuilder->CreateBlock("caseNotEq", false);
|
|
mBfIRBuilder->AddBlock(falseBlockStart);
|
|
}
|
|
|
|
BfIRBlock matchedBlockStart = mBfIRBuilder->CreateBlock("caseMatch", false);
|
|
BfIRBlock matchedBlockEnd = matchedBlockStart;
|
|
if (matchBlock != NULL)
|
|
*matchBlock = matchedBlockStart;
|
|
|
|
if (isConstMatch)
|
|
mBfIRBuilder->CreateBr(matchedBlockStart);
|
|
else
|
|
mBfIRBuilder->CreateCondBr(eqResult, matchedBlockStart, falseBlockStart ? falseBlockStart : doneBlockStart);
|
|
|
|
mBfIRBuilder->AddBlock(matchedBlockStart);
|
|
|
|
mBfIRBuilder->SetInsertPoint(doneBlockEnd);
|
|
BfIRValue phiVal;
|
|
if ((eqBlock == NULL) && (!isConstIgnore) && (!isConstMatch))
|
|
phiVal = mBfIRBuilder->CreatePhi(mBfIRBuilder->MapType(boolType), 1 + (int)tupleType->mFieldInstances.size());
|
|
|
|
mBfIRBuilder->SetInsertPoint(matchedBlockEnd);
|
|
|
|
BfTypedValue tupleVal;
|
|
if (!enumVal.IsAddr())
|
|
{
|
|
auto unionInnerType = enumType->GetUnionInnerType();
|
|
if (unionInnerType == tupleType)
|
|
{
|
|
tupleVal = ExtractValue(enumVal, NULL, 1);
|
|
}
|
|
}
|
|
|
|
if (!tupleVal)
|
|
{
|
|
if (!tupleType->IsValuelessType())
|
|
{
|
|
tupleVal = ExtractValue(enumVal, NULL, 1);
|
|
tupleVal = Cast(NULL, tupleVal, tupleType, BfCastFlags_Force);
|
|
}
|
|
else
|
|
tupleVal = GetDefaultTypedValue(tupleType);
|
|
}
|
|
|
|
////
|
|
|
|
BfAstNode* tooFewRef = invocationExpr->mCloseParen;
|
|
if ((tooFewRef == NULL) && (!invocationExpr->mCommas.IsEmpty()))
|
|
tooFewRef = invocationExpr->mCommas[invocationExpr->mCommas.size() - 1];
|
|
else if (tooFewRef == NULL)
|
|
tooFewRef = invocationExpr->mOpenParen;
|
|
|
|
///
|
|
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
|
|
bool wasCapturingMethodInfo = false;
|
|
if (autoComplete != NULL)
|
|
{
|
|
wasCapturingMethodInfo = autoComplete->mIsCapturingMethodMatchInfo;
|
|
autoComplete->CheckInvocation(invocationExpr, invocationExpr->mOpenParen, invocationExpr->mCloseParen, invocationExpr->mCommas);
|
|
|
|
if (autoComplete->mIsCapturingMethodMatchInfo)
|
|
{
|
|
autoComplete->mMethodMatchInfo->mInstanceList.Clear();
|
|
|
|
auto methodMatchInfo = autoComplete->mMethodMatchInfo;
|
|
|
|
BfAutoComplete::MethodMatchEntry methodMatchEntry;
|
|
methodMatchEntry.mTypeInstance = enumType;
|
|
methodMatchEntry.mCurMethodInstance = mCurMethodInstance;
|
|
methodMatchEntry.mPayloadEnumField = fieldInstance;
|
|
autoComplete->mMethodMatchInfo->mInstanceList.push_back(methodMatchEntry);
|
|
|
|
methodMatchInfo->mBestIdx = 0;
|
|
methodMatchInfo->mMostParamsMatched = 0;
|
|
|
|
int cursorIdx = invocationExpr->GetParser()->mCursorIdx;
|
|
if ((invocationExpr->mCloseParen == NULL) || (cursorIdx <= invocationExpr->mCloseParen->GetSrcStart()))
|
|
{
|
|
int paramIdx = 0;
|
|
for (int commaIdx = 0; commaIdx < (int)invocationExpr->mCommas.size(); commaIdx++)
|
|
{
|
|
auto commaNode = invocationExpr->mCommas[commaIdx];
|
|
if ((commaNode != NULL) && (cursorIdx >= commaNode->GetSrcStart()))
|
|
paramIdx = commaIdx + 1;
|
|
}
|
|
|
|
bool isEmpty = true;
|
|
|
|
if (paramIdx < (int)invocationExpr->mArguments.size())
|
|
{
|
|
auto paramNode = invocationExpr->mArguments[paramIdx];
|
|
if (paramNode != NULL)
|
|
isEmpty = false;
|
|
}
|
|
|
|
if (isEmpty)
|
|
{
|
|
if (paramIdx < (int)tupleType->mFieldInstances.size())
|
|
{
|
|
auto fieldDef = tupleType->mFieldInstances[paramIdx].GetFieldDef();
|
|
String insertStr;
|
|
if (fieldDef->IsUnnamedTupleField())
|
|
insertStr = "p";
|
|
insertStr += fieldDef->mName;
|
|
|
|
insertStr.Insert(0, "let ");
|
|
autoComplete->mEntriesSet.Clear();
|
|
autoComplete->AddEntry(AutoCompleteEntry("paramName", insertStr));
|
|
autoComplete->mInsertStartIdx = cursorIdx;
|
|
autoComplete->mInsertEndIdx = cursorIdx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
defer
|
|
(
|
|
if (autoComplete != NULL)
|
|
autoComplete->mIsCapturingMethodMatchInfo = (wasCapturingMethodInfo) && (!autoComplete->mIsCapturingMethodMatchInfo);
|
|
);
|
|
|
|
///
|
|
|
|
HandleCaseEnumMatch_Tuple(tupleVal, invocationExpr->mArguments, tooFewRef, falseBlockStart ? BfIRValue() : phiVal, matchedBlockStart, matchedBlockEnd,
|
|
falseBlockStart ? falseBlockStart : doneBlockStart, falseBlockEnd ? falseBlockEnd : doneBlockEnd,
|
|
hadConditional, clearOutOnMismatch, prevHadFallthrough);
|
|
|
|
///////
|
|
|
|
if (phiVal)
|
|
{
|
|
auto falseVal = mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0);
|
|
if (falseBlockEnd)
|
|
mBfIRBuilder->AddPhiIncoming(phiVal, falseVal, falseBlockEnd);
|
|
else
|
|
mBfIRBuilder->AddPhiIncoming(phiVal, falseVal, startBlock);
|
|
auto trueVal = mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 1);
|
|
mBfIRBuilder->AddPhiIncoming(phiVal, trueVal, matchedBlockEnd);
|
|
}
|
|
|
|
if (eqBlock != NULL)
|
|
mBfIRBuilder->CreateBr(*eqBlock);
|
|
else
|
|
mBfIRBuilder->CreateBr(doneBlockStart);
|
|
|
|
if (falseBlockEnd)
|
|
{
|
|
mBfIRBuilder->SetInsertPoint(falseBlockEnd);
|
|
mBfIRBuilder->CreateBr(doneBlockStart);
|
|
//mBfIRBuilder->AddPhiIncoming(phiVal, mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0), falseBlock);
|
|
}
|
|
|
|
mBfIRBuilder->AddBlock(doneBlockStart);
|
|
mBfIRBuilder->SetInsertPoint(doneBlockEnd);
|
|
|
|
if (phiVal)
|
|
return BfTypedValue(phiVal, boolType);
|
|
else if (eqResult)
|
|
return BfTypedValue(eqResult, boolType);
|
|
else
|
|
return GetDefaultTypedValue(boolType);
|
|
}
|
|
}
|
|
|
|
return BfTypedValue();
|
|
}
|
|
|
|
BfTypedValue BfModule::HandleCaseBind(BfTypedValue enumVal, const BfTypedValue& tagVal, BfEnumCaseBindExpression* bindExpr, BfIRBlock* eqBlock, BfIRBlock* notEqBlock, BfIRBlock* matchBlock, int* outEnumIdx)
|
|
{
|
|
BfTypeInstance* tupleType = NULL;
|
|
|
|
auto activeTypeDef = GetActiveTypeDef();
|
|
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
BfIRValue eqResult;
|
|
if (bindExpr->mEnumMemberExpr != NULL)
|
|
{
|
|
int enumIdx = -1;
|
|
String findName;
|
|
|
|
BfType* type = NULL;
|
|
BfAstNode* targetNode = NULL;
|
|
BfAstNode* nameNode = NULL;
|
|
BfAstNode* dotNode = NULL;
|
|
|
|
if (auto memberExpr = BfNodeDynCast<BfMemberReferenceExpression>(bindExpr->mEnumMemberExpr))
|
|
{
|
|
dotNode = memberExpr->mDotToken;
|
|
if (memberExpr->mMemberName != NULL)
|
|
{
|
|
nameNode = memberExpr->mMemberName;
|
|
findName = memberExpr->mMemberName->ToString();
|
|
|
|
if (memberExpr->mTarget == NULL)
|
|
{
|
|
type = enumVal.mType;
|
|
}
|
|
else if (auto typeRef = BfNodeDynCast<BfTypeReference>(memberExpr->mTarget))
|
|
{
|
|
type = ResolveTypeRef(typeRef);
|
|
}
|
|
}
|
|
|
|
targetNode = memberExpr->mTarget;
|
|
}
|
|
else if (auto identiferNode = BfNodeDynCast<BfIdentifierNode>(bindExpr->mEnumMemberExpr))
|
|
{
|
|
if (mCurTypeInstance->IsPayloadEnum())
|
|
{
|
|
nameNode = identiferNode;
|
|
findName = nameNode->ToString();
|
|
targetNode = identiferNode;
|
|
type = mCurTypeInstance;
|
|
}
|
|
else
|
|
{
|
|
Fail("Expected a qualified enum case name. Consider prefixing name with a dot to infer enum type name.", bindExpr->mEnumMemberExpr);
|
|
}
|
|
}
|
|
|
|
if (!findName.empty())
|
|
{
|
|
if (type != NULL)
|
|
{
|
|
if (type != enumVal.mType)
|
|
{
|
|
Fail(StrFormat("Enum case type '%s' does not match compared value of type '%s'",
|
|
TypeToString(enumVal.mType).c_str(), TypeToString(type).c_str()), targetNode);
|
|
}
|
|
|
|
if (type->IsEnum())
|
|
{
|
|
auto enumType = (BfTypeInstance*)type;
|
|
for (auto& fieldInstance : enumType->mFieldInstances)
|
|
{
|
|
auto fieldDef = fieldInstance.GetFieldDef();
|
|
if ((fieldDef != NULL) && (fieldDef->IsEnumCaseEntry()) && (fieldDef->mName == findName))
|
|
{
|
|
if ((!enumType->IsTypeMemberIncluded(fieldDef->mDeclaringType, activeTypeDef, this)) ||
|
|
(!enumType->IsTypeMemberAccessible(fieldDef->mDeclaringType, activeTypeDef)))
|
|
continue;
|
|
|
|
auto resolvePassData = mCompiler->mResolvePassData;
|
|
if (resolvePassData != NULL)
|
|
{
|
|
if (resolvePassData->mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Field)
|
|
resolvePassData->HandleFieldReference(nameNode, enumType->mTypeDef, fieldDef);
|
|
String filter;
|
|
auto autoComplete = resolvePassData->mAutoComplete;
|
|
if ((autoComplete != NULL) && (autoComplete->InitAutocomplete(dotNode, nameNode, filter)))
|
|
autoComplete->AddEnumTypeMembers(enumType, findName, false, enumType == mCurTypeInstance);
|
|
}
|
|
|
|
enumIdx = -fieldInstance.mDataIdx - 1;
|
|
if (outEnumIdx != NULL)
|
|
*outEnumIdx = enumIdx;
|
|
if (fieldInstance.mIsEnumPayloadCase)
|
|
tupleType = fieldInstance.mResolvedType->ToTypeInstance();
|
|
}
|
|
}
|
|
|
|
if (enumIdx == -1)
|
|
{
|
|
Fail("Enum case not found", nameNode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Fail(StrFormat("Type '%s' is not an enum type", TypeToString(type).c_str()), targetNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
BF_ASSERT(tagVal.mType->IsPrimitiveType());
|
|
eqResult = mBfIRBuilder->CreateCmpEQ(tagVal.mValue, mBfIRBuilder->CreateConst(((BfPrimitiveType*)tagVal.mType)->mTypeDef->mTypeCode, enumIdx));
|
|
}
|
|
else
|
|
{
|
|
eqResult = mBfIRBuilder->CreateConst(BfTypeCode_Boolean, 0);
|
|
}
|
|
|
|
BfIRBlock falseBlock;
|
|
if (notEqBlock != NULL)
|
|
{
|
|
falseBlock = *notEqBlock;
|
|
}
|
|
else
|
|
{
|
|
falseBlock = mBfIRBuilder->CreateBlock("notEqBlock", false);
|
|
}
|
|
|
|
auto mainBlock = mBfIRBuilder->GetInsertBlock();
|
|
|
|
BfIRBlock trueBlock = mBfIRBuilder->CreateBlock("eqBlock", false);
|
|
if (matchBlock != NULL)
|
|
*matchBlock = trueBlock;
|
|
mBfIRBuilder->AddBlock(trueBlock);
|
|
mBfIRBuilder->SetInsertPoint(trueBlock);
|
|
|
|
if ((tupleType != NULL) && (bindExpr->mBindNames != NULL))
|
|
{
|
|
BfIRValue valueScopeStart;
|
|
if (IsTargetingBeefBackend())
|
|
valueScopeStart = mBfIRBuilder->CreateValueScopeStart();
|
|
|
|
bool isVar = bindExpr->mBindToken->GetToken() == BfToken_Var;
|
|
bool isLet = bindExpr->mBindToken->GetToken() == BfToken_Let;
|
|
|
|
BfTypedValue tupleVal;
|
|
if (enumVal.IsAddr())
|
|
{
|
|
auto ptrVal = mBfIRBuilder->CreateInBoundsGEP(enumVal.mValue, 0, 1);
|
|
tupleVal = BfTypedValue(mBfIRBuilder->CreateBitCast(ptrVal, mBfIRBuilder->MapTypeInstPtr(tupleType)), tupleType, true);
|
|
}
|
|
else
|
|
{
|
|
auto unionInnerType = enumVal.mType->ToTypeInstance()->GetUnionInnerType();
|
|
tupleVal = ExtractValue(enumVal, NULL, 1);
|
|
if (unionInnerType != tupleType)
|
|
{
|
|
tupleVal = MakeAddressable(tupleVal);
|
|
tupleVal = BfTypedValue(mBfIRBuilder->CreateBitCast(tupleVal.mValue, mBfIRBuilder->MapTypeInstPtr(tupleType)), tupleType, true);
|
|
}
|
|
}
|
|
HandleTupleVariableDeclaration(NULL, bindExpr->mBindNames, tupleVal, isLet, false, true /*, &mainBlock*/);
|
|
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
if ((autoComplete != NULL) && ((isVar || isLet)))
|
|
autoComplete->CheckVarResolution(bindExpr->mBindToken, tupleType);
|
|
|
|
if (valueScopeStart)
|
|
mBfIRBuilder->CreateValueScopeSoftEnd(valueScopeStart);
|
|
}
|
|
if (eqBlock != NULL)
|
|
mBfIRBuilder->CreateBr(*eqBlock);
|
|
else
|
|
mBfIRBuilder->CreateBr(falseBlock);
|
|
|
|
// Don't create the condBr until now, so HandleTupleVariableDeclaration can create the variable declarations in mainBlock--
|
|
// we need them there since the code that uses the new variables is created outside the eqBlock
|
|
mBfIRBuilder->SetInsertPoint(mainBlock);
|
|
mBfIRBuilder->CreateCondBr(eqResult, trueBlock, falseBlock);
|
|
|
|
mBfIRBuilder->AddBlock(falseBlock);
|
|
mBfIRBuilder->SetInsertPoint(falseBlock);
|
|
|
|
return BfTypedValue(eqResult, boolType);
|
|
}
|
|
|
|
void BfModule::AddBasicBlock(BfIRBlock bb, bool activate)
|
|
{
|
|
mBfIRBuilder->AddBlock(bb);
|
|
|
|
if (activate)
|
|
mBfIRBuilder->SetInsertPoint(bb);
|
|
}
|
|
|
|
void BfModule::VisitEmbeddedStatement(BfAstNode* stmt, BfExprEvaluator* exprEvaluator, BfEmbeddedStatementFlags flags)
|
|
{
|
|
if ((flags & BfEmbeddedStatementFlags_CheckStack) != 0)
|
|
{
|
|
BP_ZONE("BfModule.VisitEmbeddedStatement");
|
|
|
|
StackHelper stackHelper;
|
|
if (!stackHelper.CanStackExpand(64 * 1024))
|
|
{
|
|
|
|
if (!stackHelper.Execute([&]()
|
|
{
|
|
VisitEmbeddedStatement(stmt, exprEvaluator, flags);
|
|
}))
|
|
{
|
|
Fail("Statement too complex to parse", stmt);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto block = BfNodeDynCast<BfBlock>(stmt);
|
|
BfLabelNode* labelNode = NULL;
|
|
if (block == NULL)
|
|
{
|
|
auto labeledBlock = BfNodeDynCast<BfLabeledBlock>(stmt);
|
|
if (labeledBlock != NULL)
|
|
{
|
|
block = labeledBlock->mBlock;
|
|
labelNode = labeledBlock->mLabelNode;
|
|
}
|
|
}
|
|
|
|
BfAstNode* openBrace = NULL;
|
|
BfAstNode* closeBrace = NULL;
|
|
|
|
if (block != NULL)
|
|
{
|
|
openBrace = block->mOpenBrace;
|
|
closeBrace = block->mCloseBrace;
|
|
|
|
if (openBrace == NULL)
|
|
{
|
|
auto checkScope = mCurMethodState->mCurScope;
|
|
while ((checkScope != NULL) && (closeBrace == NULL))
|
|
{
|
|
closeBrace = checkScope->mCloseNode;
|
|
checkScope = checkScope->mPrevScope;
|
|
}
|
|
BF_ASSERT(closeBrace != NULL);
|
|
}
|
|
}
|
|
|
|
if ((block != NULL) && (openBrace != NULL))
|
|
UpdateSrcPos(openBrace);
|
|
|
|
if ((flags & BfEmbeddedStatementFlags_Unscoped) != 0)
|
|
{
|
|
SetAndRestoreValue<BfExprEvaluator*> prevExprEvaluator(mCurMethodState->mCurScope->mExprEvaluator, exprEvaluator);
|
|
SetAndRestoreValue<bool> prevAllowReturn(mCurMethodState->mDisableReturns, true);
|
|
VisitCodeBlock(block);
|
|
}
|
|
else if (mCurMethodState != NULL)
|
|
{
|
|
bool isIgnore = mBfIRBuilder->mIgnoreWrites;
|
|
|
|
mCurMethodState->mInHeadScope = false;
|
|
|
|
BfScopeData scopeData;
|
|
if (IsTargetingBeefBackend())
|
|
scopeData.mValueScopeStart = mBfIRBuilder->CreateValueScopeStart();
|
|
|
|
mCurMethodState->AddScope(&scopeData);
|
|
if (block != NULL)
|
|
{
|
|
mCurMethodState->mCurScope->mAstBlock = block;
|
|
mCurMethodState->mCurScope->mCloseNode = closeBrace;
|
|
}
|
|
if (labelNode != NULL)
|
|
scopeData.mLabelNode = labelNode->mLabel;
|
|
NewScopeState(block != NULL);
|
|
mCurMethodState->mCurScope->mOuterIsConditional = (flags & BfEmbeddedStatementFlags_IsConditional) != 0;
|
|
mCurMethodState->mCurScope->mIsDeferredBlock = (flags & BfEmbeddedStatementFlags_IsDeferredBlock) != 0;
|
|
mCurMethodState->mCurScope->mExprEvaluator = exprEvaluator;
|
|
|
|
//
|
|
{
|
|
SetAndRestoreValue<bool> inDeferredBlock(mCurMethodState->mInDeferredBlock, mCurMethodState->mInDeferredBlock || mCurMethodState->mCurScope->mIsDeferredBlock);
|
|
if (block != NULL)
|
|
{
|
|
if (labelNode != NULL)
|
|
VisitCodeBlock(block, BfIRBlock(), BfIRBlock(), BfIRBlock(), false, NULL, labelNode);
|
|
else
|
|
VisitCodeBlock(block);
|
|
}
|
|
else
|
|
{
|
|
if (auto varDecl = BfNodeDynCast<BfVariableDeclaration>(stmt))
|
|
{
|
|
Fail("Variable declarations must be wrapped in a block statement", varDecl);
|
|
}
|
|
|
|
VisitChild(stmt);
|
|
}
|
|
}
|
|
|
|
if ((block != NULL) && (closeBrace != NULL))
|
|
{
|
|
UpdateSrcPos(closeBrace);
|
|
if (!mCurMethodState->mLeftBlockUncond)
|
|
EmitEnsureInstructionAt();
|
|
}
|
|
|
|
if (block != NULL)
|
|
{
|
|
BfAutoParentNodeEntry autoParentNodeEntry(this, block);
|
|
RestoreScopeState();
|
|
}
|
|
else
|
|
RestoreScopeState();
|
|
|
|
BF_ASSERT(isIgnore == mBfIRBuilder->mIgnoreWrites);
|
|
}
|
|
else
|
|
{
|
|
if (block != NULL)
|
|
VisitCodeBlock(block);
|
|
else
|
|
VisitChild(stmt);
|
|
}
|
|
}
|
|
|
|
void BfModule::VisitCodeBlock(BfBlock* block, BfIRBlock continueBlock, BfIRBlock breakBlock, BfIRBlock fallthroughBlock, bool defaultBreak, bool* hadReturn, BfLabelNode* labelNode, bool closeScope, BfEmbeddedStatementFlags flags)
|
|
{
|
|
BfBreakData breakData;
|
|
breakData.mIRContinueBlock = continueBlock;
|
|
breakData.mIRBreakBlock = breakBlock;
|
|
breakData.mIRFallthroughBlock = fallthroughBlock;
|
|
breakData.mScope = mCurMethodState->mCurScope;
|
|
breakData.mPrevBreakData = mCurMethodState->mBreakData;
|
|
|
|
SetAndRestoreValue<BfBreakData*> prevBreakData(mCurMethodState->mBreakData, &breakData);
|
|
VisitEmbeddedStatement(block, NULL, flags);
|
|
|
|
if (closeScope)
|
|
RestoreScopeState();
|
|
|
|
if ((!mCurMethodState->mLeftBlockUncond) && (defaultBreak))
|
|
{
|
|
mBfIRBuilder->CreateBr(breakBlock);
|
|
}
|
|
if (hadReturn != NULL)
|
|
{
|
|
*hadReturn = mCurMethodState->mHadReturn;
|
|
mCurMethodState->SetHadReturn(false);
|
|
mCurMethodState->mLeftBlockUncond = false;
|
|
}
|
|
}
|
|
|
|
void BfModule::VisitCodeBlock(BfBlock* block)
|
|
{
|
|
//BP_ZONE("BfModule::VisitCodeBlock");
|
|
|
|
BfAutoParentNodeEntry autoParentNodeEntry(this, block);
|
|
|
|
BfIRBlock prevInsertBlock;
|
|
bool hadReturn = false;
|
|
|
|
int startLocalMethod = 0; // was -1
|
|
auto rootMethodState = mCurMethodState->GetRootMethodState();
|
|
|
|
BfIRBlock startInsertBlock = mBfIRBuilder->GetInsertBlock();
|
|
|
|
bool allowLocalMethods = mCurMethodInstance != NULL;
|
|
//int startDeferredLocalIdx = (int)rootMethodState->mDeferredLocalMethods.size();
|
|
|
|
int curLocalMethodIdx = -1;
|
|
|
|
// Scan for any local method declarations
|
|
if (allowLocalMethods)
|
|
{
|
|
startLocalMethod = (int)mCurMethodState->mLocalMethods.size();
|
|
curLocalMethodIdx = startLocalMethod;
|
|
auto itr = block->begin();
|
|
while (itr != block->end())
|
|
{
|
|
BfAstNode* child = *itr;
|
|
if (auto localMethodDecl = BfNodeDynCastExact<BfLocalMethodDeclaration>(child))
|
|
{
|
|
BfLocalMethod* localMethod;
|
|
auto rootMethodState = mCurMethodState->GetRootMethodState();
|
|
|
|
String methodName;
|
|
if (localMethodDecl->mMethodDeclaration->mNameNode != NULL)
|
|
{
|
|
methodName = GetLocalMethodName(localMethodDecl->mMethodDeclaration->mNameNode->ToString(), localMethodDecl->mMethodDeclaration->mOpenParen, mCurMethodState, mCurMethodState->mMixinState);
|
|
|
|
BfLocalMethod** localMethodPtr = NULL;
|
|
if (rootMethodState->mLocalMethodCache.TryGetValue(methodName, &localMethodPtr))
|
|
{
|
|
localMethod = *localMethodPtr;
|
|
}
|
|
else
|
|
{
|
|
localMethod = new BfLocalMethod();
|
|
localMethod->mSystem = mSystem;
|
|
localMethod->mModule = this;
|
|
localMethod->mMethodDeclaration = localMethodDecl->mMethodDeclaration;
|
|
localMethod->mSource = mCurTypeInstance->mTypeDef->mSource;
|
|
localMethod->mSource->mRefCount++;
|
|
|
|
if (mCurMethodState->mClosureState != NULL)
|
|
localMethod->mOuterLocalMethod = mCurMethodState->mClosureState->mLocalMethod;
|
|
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
if ((autoComplete != NULL) && (autoComplete->mResolveType == BfResolveType_Autocomplete))
|
|
{
|
|
auto autoComplete = mCompiler->mResolvePassData->mAutoComplete;
|
|
if (!autoComplete->IsAutocompleteNode(localMethod->mMethodDeclaration))
|
|
localMethod->mDeclOnly = true;
|
|
}
|
|
|
|
if (localMethod->mMethodDeclaration->mNameNode != NULL)
|
|
localMethod->mMethodName = localMethod->mMethodDeclaration->mNameNode->ToString();
|
|
localMethod->mExpectedFullName = methodName;
|
|
rootMethodState->mLocalMethodCache[methodName] = localMethod;
|
|
mContext->mLocalMethodGraveyard.push_back(localMethod);
|
|
}
|
|
BF_ASSERT(mCurMethodState->mCurScope != NULL);
|
|
localMethod->mDeclDIScope = mCurMethodState->mCurScope->mDIScope;
|
|
localMethod->mDeclMethodState = mCurMethodState;
|
|
localMethod->mDeclMixinState = mCurMethodState->mMixinState;
|
|
if (localMethod->mDeclMixinState != NULL)
|
|
localMethod->mDeclMixinState->mHasDeferredUsage = true;
|
|
mCurMethodState->mLocalMethods.push_back(localMethod);
|
|
|
|
String* namePtr;
|
|
if (!mCurMethodState->mLocalMethodMap.TryAdd(localMethod->mMethodName, &namePtr, &localMethodPtr))
|
|
{
|
|
BF_ASSERT(localMethod != *localMethodPtr);
|
|
localMethod->mNextWithSameName = *localMethodPtr;
|
|
}
|
|
*localMethodPtr = localMethod;
|
|
}
|
|
}
|
|
++itr;
|
|
}
|
|
}
|
|
|
|
bool wantsAllLocalMethods = true;
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
if (autoComplete != NULL)
|
|
{
|
|
// If we only need reasoning "at the cursor" then we don't need all local methods
|
|
if ((!autoComplete->mIsAutoComplete) ||
|
|
(autoComplete->mResolveType == BfResolveType_GetCurrentLocation) ||
|
|
(autoComplete->mResolveType == BfResolveType_GetFixits) ||
|
|
(autoComplete->mResolveType == BfResolveType_GetResultString) ||
|
|
(autoComplete->mResolveType == BfResolveType_GetSymbolInfo) ||
|
|
(autoComplete->mResolveType == BfResolveType_ShowFileSymbolReferences))
|
|
wantsAllLocalMethods = false;
|
|
}
|
|
|
|
/*if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mResolveType == BfResolveType_ShowFileSymbolReferences))
|
|
{
|
|
if (mCompiler->mResolvePassData->mSymbolReferenceLocalIdx != -1)
|
|
{
|
|
// We need to reproduce the behavior when we found the symbol - only process autocomplete nodes, otherwise local method ids will be wrong
|
|
// when we have local methods that were skipped the first time but not the second
|
|
wantsAllLocalMethods = false;
|
|
}
|
|
}*/
|
|
|
|
SetAndRestoreValue<bool> prevIgnoreWrite(mBfIRBuilder->mIgnoreWrites);
|
|
bool hadUnreachableCode = false;
|
|
|
|
// Handle statements
|
|
auto itr = block->begin();
|
|
while (itr != block->end())
|
|
{
|
|
BfAstNode* child = *itr;
|
|
|
|
if (auto localMethodDecl = BfNodeDynCastExact<BfLocalMethodDeclaration>(child))
|
|
{
|
|
/*if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_Mixin))
|
|
Fail("Mixins cannot contain local methods", child);*/
|
|
|
|
if (!allowLocalMethods)
|
|
{
|
|
Fail("Invalid use of local methods", child);
|
|
}
|
|
else if (localMethodDecl->mMethodDeclaration->mNameNode != NULL)
|
|
{
|
|
BfLocalMethod* localMethod = mCurMethodState->mLocalMethods[curLocalMethodIdx];
|
|
BF_ASSERT(localMethod->mMethodDeclaration == localMethodDecl->mMethodDeclaration);
|
|
|
|
bool wantsLocalMethod = (wantsAllLocalMethods) || (autoComplete->IsAutocompleteNode(localMethod->mMethodDeclaration));
|
|
|
|
if ((!wantsLocalMethod) && (mCurMethodInstance->mMethodDef->mIsLocalMethod))
|
|
wantsLocalMethod = true;
|
|
|
|
if (wantsLocalMethod)
|
|
{
|
|
if (!mCurMethodInstance->IsSpecializedGenericMethodOrType())
|
|
GetLocalMethodInstance(localMethod, BfTypeVector(), NULL, true); // Only necessary on unspecialized pass
|
|
}
|
|
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
|
|
mCompiler->mResolvePassData->mAutoComplete->CheckMethod(localMethod->mMethodDeclaration, true);
|
|
|
|
curLocalMethodIdx++;
|
|
}
|
|
|
|
++itr;
|
|
continue;
|
|
}
|
|
|
|
if ((mCurMethodState != NULL) && (mCurMethodState->mLeftBlockUncond)) // mLeftBlock is cleared after conditional block is completed
|
|
{
|
|
if (mCurMethodState->mHadReturn)
|
|
hadReturn = true;
|
|
|
|
if ((!hadUnreachableCode) && (!mCurMethodState->mInPostReturn))
|
|
{
|
|
if ((mCurMethodState->mCurScope == NULL) || (!mCurMethodState->mCurScope->mSupressNextUnreachable))
|
|
Warn(BfWarning_CS0162_UnreachableCode, "Unreachable code", child);
|
|
|
|
hadUnreachableCode = true;
|
|
prevInsertBlock = mBfIRBuilder->GetInsertBlock();
|
|
|
|
mCurMethodState->mInPostReturn = true;
|
|
mBfIRBuilder->mIgnoreWrites = true;
|
|
}
|
|
}
|
|
if ((mCurMethodState != NULL) && (mCurMethodState->mCurScope != NULL))
|
|
mCurMethodState->mCurScope->mSupressNextUnreachable = false;
|
|
|
|
if (itr.IsLast())
|
|
{
|
|
if (auto expr = BfNodeDynCast<BfExpression>(child))
|
|
{
|
|
if (expr->IsExpression())
|
|
{
|
|
if (mCurMethodState != NULL)
|
|
{
|
|
if (mCurMethodState->mCurScope->mExprEvaluator != NULL)
|
|
{
|
|
if ((mAttributeState != NULL) &&
|
|
((mAttributeState->mFlags & (BfAttributeState::Flag_StopOnError | BfAttributeState::Flag_HadError)) == (BfAttributeState::Flag_StopOnError | BfAttributeState::Flag_HadError)))
|
|
{
|
|
// Resolve as just 'false'
|
|
mCurMethodState->mCurScope->mExprEvaluator->mResult = GetDefaultTypedValue(GetPrimitiveType(BfTypeCode_Boolean));
|
|
}
|
|
else
|
|
{
|
|
auto exprEvaluator = mCurMethodState->mCurScope->mExprEvaluator;
|
|
|
|
// Evaluate last child as an expression
|
|
exprEvaluator->VisitChild(expr);
|
|
exprEvaluator->FinishExpressionResult();
|
|
|
|
if ((exprEvaluator->mResult) && (!exprEvaluator->mResult.mType->IsValuelessType()) && (!exprEvaluator->mResult.mValue.IsConst()) &&
|
|
(!exprEvaluator->mResult.IsAddr()) && (exprEvaluator->mResult.mValue) && (!exprEvaluator->mResult.mValue.IsFake()))
|
|
{
|
|
if ((mCurMethodState->mCurScope != NULL) && (mCurMethodState->mCurScope->mPrevScope != NULL))
|
|
{
|
|
// We need to make sure we don't retain any values through the scope's ValueScopeHardEnd - and extend alloca through previous scope
|
|
|
|
bool wasReadOnly = exprEvaluator->mResult.IsReadOnly();
|
|
FixIntUnknown(exprEvaluator->mResult, exprEvaluator->mExpectingType);
|
|
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
|
|
auto tempVar = CreateAlloca(exprEvaluator->mResult.mType, false, "blockExpr");
|
|
mBfIRBuilder->SetInsertPointAtStart(startInsertBlock);
|
|
auto lifetimeStart = mBfIRBuilder->CreateLifetimeStart(tempVar);
|
|
mBfIRBuilder->ClearDebugLocation(lifetimeStart);
|
|
|
|
if ((!mBfIRBuilder->mIgnoreWrites) && (IsTargetingBeefBackend()))
|
|
mCurMethodState->mCurScope->mPrevScope->mDeferredLifetimeEnds.push_back(tempVar);
|
|
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
|
|
if (exprEvaluator->mResult.IsSplat())
|
|
AggregateSplatIntoAddr(exprEvaluator->mResult, tempVar);
|
|
else if (!exprEvaluator->mResult.mType->IsValuelessType())
|
|
mBfIRBuilder->CreateAlignedStore(exprEvaluator->mResult.mValue, tempVar, exprEvaluator->mResult.mType->mAlign);
|
|
exprEvaluator->mResult = BfTypedValue(tempVar, exprEvaluator->mResult.mType,
|
|
exprEvaluator->mResult.IsThis() ?
|
|
(wasReadOnly ? BfTypedValueKind_ReadOnlyThisAddr : BfTypedValueKind_ThisAddr) :
|
|
(wasReadOnly ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr));
|
|
}
|
|
}
|
|
|
|
if (exprEvaluator->mResult.IsAddr())
|
|
{
|
|
if (mCurMethodState->mCurScope->ExtendLifetime(exprEvaluator->mResult.mValue))
|
|
mBfIRBuilder->CreateLifetimeSoftEnd(exprEvaluator->mResult.mValue);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
else if (mCurMethodState->InMainMixinScope())
|
|
{
|
|
mCurMethodState->mMixinState->mResultExpr = expr;
|
|
break;
|
|
}
|
|
else if ((mCurMethodInstance != NULL) && (mCurMethodInstance->IsMixin()) && (mCurMethodState->mCurScope == &mCurMethodState->mHeadScope))
|
|
{
|
|
// Only in mixin definition - result ignored
|
|
CreateValueFromExpression(expr, NULL, BfEvalExprFlags_AllowRefExpr);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
FailAfter("Expression block cannot be used here. Consider adding semicolon if a statement was intended.", expr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateSrcPos(child);
|
|
BfAutoParentNodeEntry autoParentNode(this, child);
|
|
|
|
if ((mAttributeState != NULL) &&
|
|
((mAttributeState->mFlags & (BfAttributeState::Flag_StopOnError | BfAttributeState::Flag_HadError)) == (BfAttributeState::Flag_StopOnError | BfAttributeState::Flag_HadError)))
|
|
{
|
|
// Ignore child
|
|
}
|
|
else
|
|
child->Accept(this);
|
|
|
|
mContext->CheckLockYield();
|
|
|
|
++itr;
|
|
}
|
|
|
|
if (mCurMethodState != NULL)
|
|
{
|
|
// Any local method that hasn't been called needs to be processed now
|
|
for (int localMethodIdx = startLocalMethod; localMethodIdx < (int)mCurMethodState->mLocalMethods.size(); localMethodIdx++)
|
|
{
|
|
auto localMethod = mCurMethodState->mLocalMethods[localMethodIdx];
|
|
if ((wantsAllLocalMethods) || (autoComplete->IsAutocompleteNode(localMethod->mMethodDeclaration)))
|
|
{
|
|
//??
|
|
auto moduleMethodInstance = GetLocalMethodInstance(localMethod, BfTypeVector(), NULL, true);
|
|
}
|
|
mContext->CheckLockYield();
|
|
}
|
|
|
|
while ((int)mCurMethodState->mLocalMethods.size() > startLocalMethod)
|
|
{
|
|
auto localMethod = mCurMethodState->mLocalMethods.back();
|
|
|
|
#if _DEBUG
|
|
BfLocalMethod** localMethodPtr = NULL;
|
|
mCurMethodState->mLocalMethodMap.TryGetValue(localMethod->mMethodName, &localMethodPtr);
|
|
BF_ASSERT(*localMethodPtr == localMethod);
|
|
#endif
|
|
if (localMethod->mNextWithSameName == NULL)
|
|
mCurMethodState->mLocalMethodMap.Remove(localMethod->mMethodName);
|
|
else
|
|
{
|
|
mCurMethodState->mLocalMethodMap[localMethod->mMethodName] = localMethod->mNextWithSameName;
|
|
localMethod->mNextWithSameName = NULL;
|
|
}
|
|
|
|
mCurMethodState->mLocalMethods.pop_back();
|
|
}
|
|
|
|
if (hadUnreachableCode)
|
|
{
|
|
if (hadReturn)
|
|
mCurMethodState->SetHadReturn(true);
|
|
mCurMethodState->mLeftBlockUncond = true;
|
|
mCurMethodState->mInPostReturn = false;
|
|
|
|
if (prevInsertBlock)
|
|
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BfModule::Visit(BfAstNode* astNode)
|
|
{
|
|
AssertErrorState();
|
|
}
|
|
|
|
void BfModule::Visit(BfIdentifierNode* identifierNode)
|
|
{
|
|
Visit((BfExpression*)identifierNode);
|
|
}
|
|
|
|
void BfModule::Visit(BfTypeReference* typeRef)
|
|
{
|
|
Visit((BfAstNode*)typeRef);
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
|
|
mCompiler->mResolvePassData->mAutoComplete->CheckTypeRef(typeRef, true);
|
|
}
|
|
|
|
void BfModule::Visit(BfEmptyStatement* astNode)
|
|
{
|
|
}
|
|
|
|
void BfModule::Visit(BfTryStatement* tryStmt)
|
|
{
|
|
Fail("Exceptions not supported", tryStmt->mTryToken);
|
|
VisitChild(tryStmt->mStatement);
|
|
}
|
|
|
|
void BfModule::Visit(BfCatchStatement* catchStmt)
|
|
{
|
|
Fail("Exceptions not supported", catchStmt->mCatchToken);
|
|
}
|
|
|
|
void BfModule::Visit(BfFinallyStatement* finallyStmt)
|
|
{
|
|
Fail("Exceptions not supported", finallyStmt->mFinallyToken);
|
|
VisitChild(finallyStmt->mStatement);
|
|
}
|
|
|
|
void BfModule::Visit(BfCheckedStatement* checkedStmt)
|
|
{
|
|
Fail("'checked' not supported", checkedStmt->mCheckedToken);
|
|
VisitChild(checkedStmt->mStatement);
|
|
}
|
|
|
|
void BfModule::Visit(BfUncheckedStatement* uncheckedStmt)
|
|
{
|
|
VisitChild(uncheckedStmt->mStatement);
|
|
}
|
|
|
|
void BfModule::DoIfStatement(BfIfStatement* ifStmt, bool includeTrueStmt, bool includeFalseStmt)
|
|
{
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
if (autoComplete != NULL)
|
|
autoComplete->CheckIdentifier(ifStmt->mIfToken, true);
|
|
|
|
if (ifStmt->mCondition == NULL)
|
|
{
|
|
AssertErrorState();
|
|
return;
|
|
}
|
|
|
|
//TODO: Only conditionally create the scopeData here if we create a variable inside the condition statement
|
|
|
|
UpdateSrcPos(ifStmt);
|
|
|
|
BfScopeData newScope;
|
|
newScope.mOuterIsConditional = true;
|
|
newScope.mScopeKind = BfScopeKind_StatementTarget;
|
|
if (ifStmt->mLabelNode != NULL)
|
|
newScope.mLabelNode = ifStmt->mLabelNode->mLabel;
|
|
mCurMethodState->AddScope(&newScope);
|
|
NewScopeState();
|
|
|
|
BfBreakData breakData;
|
|
breakData.mScope = &newScope;
|
|
breakData.mPrevBreakData = mCurMethodState->mBreakData;
|
|
SetAndRestoreValue<BfBreakData*> prevBreakData(mCurMethodState->mBreakData, &breakData);
|
|
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
|
|
BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope);
|
|
deferredLocalAssignData.mIsIfCondition = true;
|
|
deferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData, true);
|
|
deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
|
|
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &deferredLocalAssignData);
|
|
|
|
BfAutoParentNodeEntry autoParentNodeEntry(this, ifStmt);
|
|
BfTypedValue condValue = CreateValueFromExpression(ifStmt->mCondition, boolType);
|
|
|
|
newScope.mScopeKind = BfScopeKind_Normal;
|
|
|
|
deferredLocalAssignData.mIsIfCondition = false;
|
|
|
|
// The "extend chain" is only valid for the conditional -- since that expression may contain unconditionally executed and
|
|
// conditionally executed code (in the case of "(GetVal(out a) && GetVal(out b))" for example
|
|
mCurMethodState->mDeferredLocalAssignData->BreakExtendChain();
|
|
|
|
if (!condValue)
|
|
{
|
|
AssertErrorState();
|
|
condValue = BfTypedValue(GetDefaultValue(boolType), boolType);
|
|
}
|
|
|
|
BfIRBlock trueBB;
|
|
BfIRBlock falseBB;
|
|
bool isConstBranch = false;
|
|
bool constResult = false;
|
|
if (condValue.mValue.IsConst())
|
|
{
|
|
auto constant = mBfIRBuilder->GetConstant(condValue.mValue);
|
|
if ((constant != NULL) && (constant->mTypeCode == BfTypeCode_Boolean))
|
|
{
|
|
isConstBranch = true;
|
|
constResult = constant->mBool;
|
|
}
|
|
}
|
|
|
|
if (!isConstBranch)
|
|
{
|
|
trueBB = mBfIRBuilder->CreateBlock("if.then", true);
|
|
falseBB = (ifStmt->mFalseStatement == NULL) ? BfIRBlock() : mBfIRBuilder->CreateBlock("if.else");
|
|
}
|
|
else
|
|
EmitEnsureInstructionAt();
|
|
|
|
auto contBB = mBfIRBuilder->CreateBlock("if.end");
|
|
|
|
if (!isConstBranch)
|
|
{
|
|
mBfIRBuilder->CreateCondBr(condValue.mValue, trueBB, (falseBB) ? falseBB : contBB);
|
|
}
|
|
|
|
// TRUE statement
|
|
bool ignoredLastBlock = true;
|
|
|
|
if (includeTrueStmt)
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites);
|
|
SetAndRestoreValue<bool> prevInConstIgnore(mCurMethodState->mCurScope->mInConstIgnore);
|
|
|
|
if (trueBB)
|
|
mBfIRBuilder->SetInsertPoint(trueBB);
|
|
if ((isConstBranch) && (constResult != true))
|
|
{
|
|
mBfIRBuilder->mIgnoreWrites = true;
|
|
mCurMethodState->mCurScope->mInConstIgnore = true;
|
|
}
|
|
else
|
|
ignoredLastBlock = false;
|
|
VisitEmbeddedStatement(ifStmt->mTrueStatement);
|
|
}
|
|
prevDLA.Restore();
|
|
|
|
if (mCurMethodState->mDeferredLocalAssignData != NULL)
|
|
mCurMethodState->mDeferredLocalAssignData->mHadBreak |= deferredLocalAssignData.mHadBreak;
|
|
|
|
bool trueHadReturn = mCurMethodState->mHadReturn;
|
|
|
|
// We restore the scopeData before the False block because we don't want variables created in the if condition to
|
|
// be visible in the false section
|
|
//RestoreScopeState();
|
|
RestoreScoreState_LocalVariables(mCurMethodState->mCurScope->mLocalVarStart);
|
|
|
|
if ((!mCurMethodState->mLeftBlockUncond) && (!ignoredLastBlock))
|
|
mBfIRBuilder->CreateBr_NoCollapse(contBB);
|
|
|
|
if (mCurMethodState->mLeftBlockUncond)
|
|
{
|
|
deferredLocalAssignData.mLeftBlockUncond = true;
|
|
mCurMethodState->mLeftBlockCond = true;
|
|
}
|
|
|
|
mCurMethodState->mLeftBlockUncond = false;
|
|
mCurMethodState->SetHadReturn(false);
|
|
|
|
bool falseHadReturn = false;
|
|
if (ifStmt->mFalseStatement != NULL)
|
|
{
|
|
BfDeferredLocalAssignData falseDeferredLocalAssignData(&newScope);
|
|
falseDeferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
|
|
if (falseBB)
|
|
{
|
|
mBfIRBuilder->AddBlock(falseBB);
|
|
mBfIRBuilder->SetInsertPoint(falseBB);
|
|
}
|
|
|
|
ignoredLastBlock = true;
|
|
//
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites);
|
|
SetAndRestoreValue<bool> prevInConstIgnore(mCurMethodState->mCurScope->mInConstIgnore);
|
|
|
|
if ((isConstBranch) && (constResult != false))
|
|
{
|
|
mBfIRBuilder->mIgnoreWrites = true;
|
|
mCurMethodState->mCurScope->mInConstIgnore = true;
|
|
}
|
|
else
|
|
ignoredLastBlock = false;
|
|
falseDeferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData);
|
|
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &falseDeferredLocalAssignData);
|
|
if (includeFalseStmt)
|
|
VisitEmbeddedStatement(ifStmt->mFalseStatement, NULL, (BfEmbeddedStatementFlags)(BfEmbeddedStatementFlags_IsConditional | BfEmbeddedStatementFlags_CheckStack));
|
|
}
|
|
if ((!mCurMethodState->mLeftBlockUncond) && (!ignoredLastBlock))
|
|
{
|
|
if (IsTargetingBeefBackend())
|
|
{
|
|
// If we don't do this, then with:
|
|
// if (a) { } else if (b) { }
|
|
// Then we hit the closing second brace even if 'b' is false
|
|
//SetIllegalSrcPos();
|
|
//BfIRBuilder->ClearDebugLocation();
|
|
}
|
|
auto br = mBfIRBuilder->CreateBr_NoCollapse(contBB);
|
|
//mBfIRBuilder->ClearDebugLocation(br);
|
|
}
|
|
falseHadReturn = mCurMethodState->mHadReturn;
|
|
if (mCurMethodState->mLeftBlockUncond)
|
|
{
|
|
falseDeferredLocalAssignData.mLeftBlockUncond = true;
|
|
mCurMethodState->mLeftBlockCond = true;
|
|
}
|
|
mCurMethodState->mLeftBlockUncond = false;
|
|
mCurMethodState->SetHadReturn(false);
|
|
|
|
deferredLocalAssignData.SetIntersection(falseDeferredLocalAssignData);
|
|
mCurMethodState->ApplyDeferredLocalAssignData(deferredLocalAssignData);
|
|
}
|
|
else
|
|
{
|
|
// If we had a const-ignored if statement with no else
|
|
if (ignoredLastBlock)
|
|
{
|
|
if (!mCurMethodState->mLeftBlockUncond)
|
|
mBfIRBuilder->CreateBr_NoCollapse(contBB);
|
|
}
|
|
}
|
|
|
|
mBfIRBuilder->AddBlock(contBB);
|
|
mBfIRBuilder->SetInsertPoint(contBB);
|
|
|
|
if (isConstBranch)
|
|
mCurMethodState->SetHadReturn(constResult ? trueHadReturn : falseHadReturn);
|
|
else
|
|
mCurMethodState->SetHadReturn(trueHadReturn && falseHadReturn);
|
|
mCurMethodState->mLeftBlockUncond = mCurMethodState->mHadReturn;
|
|
|
|
if (mCurMethodState->mHadReturn)
|
|
{
|
|
mBfIRBuilder->EraseFromParent(contBB);
|
|
}
|
|
else
|
|
{
|
|
mBfIRBuilder->SetInsertPoint(contBB);
|
|
}
|
|
|
|
RestoreScopeState();
|
|
}
|
|
|
|
void BfModule::Visit(BfIfStatement* ifStmt)
|
|
{
|
|
DoIfStatement(ifStmt, true, true);
|
|
}
|
|
|
|
void BfModule::Visit(BfVariableDeclaration* varDecl)
|
|
{
|
|
//BP_ZONE("BfModule::Visit(BfVariableDeclaration)");
|
|
|
|
UpdateSrcPos(varDecl);
|
|
|
|
BfTupleExpression* tupleVariableDeclaration = BfNodeDynCast<BfTupleExpression>(varDecl->mNameNode);
|
|
if (tupleVariableDeclaration != NULL)
|
|
{
|
|
HandleTupleVariableDeclaration(varDecl);
|
|
}
|
|
else
|
|
HandleVariableDeclaration(varDecl);
|
|
}
|
|
|
|
void BfModule::Visit(BfLocalMethodDeclaration* methodDecl)
|
|
{
|
|
Fail("Local method declarations must be wrapped in a block statement", methodDecl->mMethodDeclaration->mNameNode);
|
|
}
|
|
|
|
void BfModule::Visit(BfAttributedStatement* attribStmt)
|
|
{
|
|
BfAttributeState attributeState;
|
|
attributeState.mSrc = attribStmt->mAttributes;
|
|
attributeState.mTarget = (BfAttributeTargets)(BfAttributeTargets_Invocation | BfAttributeTargets_MemberAccess);
|
|
if (auto block = BfNodeDynCast<BfBlock>(attribStmt->mStatement))
|
|
attributeState.mTarget = BfAttributeTargets_Block;
|
|
|
|
attributeState.mCustomAttributes = GetCustomAttributes(attribStmt->mAttributes, attributeState.mTarget);
|
|
|
|
SetAndRestoreValue<BfAttributeState*> prevAttributeState(mAttributeState, &attributeState);
|
|
|
|
if (auto ignoreErrorsAttrib = attributeState.mCustomAttributes->Get(mCompiler->mIgnoreErrorsAttributeTypeDef))
|
|
{
|
|
SetAndRestoreValue<bool> ignoreErrors(mIgnoreErrors, true);
|
|
if (!ignoreErrorsAttrib->mCtorArgs.IsEmpty())
|
|
{
|
|
auto constant = mCurTypeInstance->mConstHolder->GetConstant(ignoreErrorsAttrib->mCtorArgs[0]);
|
|
if (constant->mBool)
|
|
attributeState.mFlags = BfAttributeState::Flag_StopOnError;
|
|
}
|
|
VisitChild(attribStmt->mStatement);
|
|
attributeState.mUsed = true;
|
|
}
|
|
else if (attributeState.mCustomAttributes->Contains(mCompiler->mConstSkipAttributeTypeDef))
|
|
{
|
|
if ((mCurMethodState == NULL) || (mCurMethodState->mCurScope == NULL) || (!mCurMethodState->mCurScope->mInConstIgnore))
|
|
{
|
|
VisitChild(attribStmt->mStatement);
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(mBfIRBuilder->mIgnoreWrites);
|
|
}
|
|
attributeState.mUsed = true;
|
|
}
|
|
else
|
|
{
|
|
VisitChild(attribStmt->mStatement);
|
|
}
|
|
|
|
FinishAttributeState(&attributeState);
|
|
}
|
|
|
|
void BfModule::Visit(BfExpression* expression)
|
|
{
|
|
UpdateSrcPos(expression);
|
|
BfExprEvaluator exprEvaluator(this);
|
|
exprEvaluator.mUsedAsStatement = true;
|
|
exprEvaluator.Evaluate(expression);
|
|
}
|
|
|
|
void BfModule::Visit(BfExpressionStatement* expressionStmt)
|
|
{
|
|
expressionStmt->mExpression->Accept(this);
|
|
}
|
|
|
|
void BfModule::Visit(BfThrowStatement* throwStmt)
|
|
{
|
|
if (throwStmt->mExpression == NULL)
|
|
{
|
|
AssertErrorState();
|
|
return;
|
|
}
|
|
|
|
UpdateSrcPos(throwStmt->mThrowToken);
|
|
auto throwValue = CreateValueFromExpression(throwStmt->mExpression);
|
|
Fail("Exceptions are not supported", throwStmt->mThrowToken);
|
|
|
|
if (mCurMethodInstance->mReturnType->IsVoid())
|
|
EmitReturn(BfTypedValue());
|
|
else
|
|
EmitReturn(GetDefaultTypedValue(mCurMethodInstance->mReturnType));
|
|
}
|
|
|
|
void BfModule::Visit(BfDeleteStatement* deleteStmt)
|
|
{
|
|
UpdateSrcPos(deleteStmt);
|
|
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
if (autoComplete != NULL)
|
|
autoComplete->CheckIdentifier(deleteStmt->mDeleteToken, true);
|
|
|
|
bool isAppendDelete = false;
|
|
BfTypedValue customAllocator;
|
|
if (deleteStmt->mAllocExpr != NULL)
|
|
{
|
|
if (auto expr = BfNodeDynCast<BfExpression>(deleteStmt->mAllocExpr))
|
|
customAllocator = CreateValueFromExpression(expr);
|
|
else if (auto tokenNode = BfNodeDynCast<BfTokenNode>(deleteStmt->mAllocExpr))
|
|
{
|
|
if (tokenNode->mToken == BfToken_Append)
|
|
isAppendDelete = true;
|
|
}
|
|
}
|
|
|
|
BfAttributeState attributeState;
|
|
attributeState.mTarget = BfAttributeTargets_Delete;
|
|
SetAndRestoreValue<BfAttributeState*> prevAttributeState(mAttributeState, &attributeState);
|
|
attributeState.mCustomAttributes = GetCustomAttributes(deleteStmt->mAttributes, attributeState.mTarget);
|
|
|
|
if (deleteStmt->mExpression == NULL)
|
|
{
|
|
AssertErrorState();
|
|
return;
|
|
}
|
|
|
|
auto val = CreateValueFromExpression(deleteStmt->mExpression);
|
|
if (!val)
|
|
return;
|
|
|
|
auto checkType = val.mType;
|
|
for (int pass = 0; pass < 2; pass++)
|
|
{
|
|
BfGenericParamType* genericType = NULL;
|
|
if (checkType->IsGenericParam())
|
|
genericType = (BfGenericParamType*)checkType;
|
|
if ((checkType->IsPointer()) && (checkType->GetUnderlyingType()->IsGenericParam()))
|
|
genericType = (BfGenericParamType*)checkType->GetUnderlyingType();
|
|
if ((genericType != NULL) || (checkType->IsUnspecializedType()))
|
|
{
|
|
BfGenericParamFlags genericParamFlags = BfGenericParamFlag_None;
|
|
BfType* typeConstraint = NULL;
|
|
BfGenericParamInstance* genericParam = NULL;
|
|
if (genericType != NULL)
|
|
genericParam = GetMergedGenericParamData(genericType, genericParamFlags, typeConstraint);
|
|
if (genericParam == NULL)
|
|
GetMergedGenericParamData(checkType, genericParamFlags, typeConstraint);
|
|
|
|
if (typeConstraint != NULL)
|
|
checkType = typeConstraint;
|
|
bool canAlwaysDelete = checkType->IsDelegate() || checkType->IsFunction() || checkType->IsArray();
|
|
if (auto checkTypeInst = checkType->ToTypeInstance())
|
|
{
|
|
if ((checkTypeInst->IsInstanceOf(mCompiler->mDelegateTypeDef)) ||
|
|
(checkTypeInst->IsInstanceOf(mCompiler->mFunctionTypeDef)))
|
|
canAlwaysDelete = true;
|
|
}
|
|
|
|
if (!canAlwaysDelete)
|
|
{
|
|
bool success = false;
|
|
if (genericParamFlags & (BfGenericParamFlag_Delete | BfGenericParamFlag_Var))
|
|
success = true;
|
|
else if (genericParamFlags & BfGenericParamFlag_StructPtr)
|
|
success = true;
|
|
else if ((genericParamFlags & BfGenericParamFlag_Struct) && (checkType->IsPointer()))
|
|
success = true;
|
|
|
|
if (success)
|
|
{
|
|
if ((pass == 1) && (genericType != NULL))
|
|
{
|
|
auto genericParamInst = GetGenericParamInstance(genericType);
|
|
Warn(0, StrFormat("Must add 'where alloctype(%s) : delete' constraint to generic parameter to delete generic type '%s'",
|
|
genericParamInst->GetGenericParamDef()->mName.c_str(), TypeToString(val.mType).c_str()), deleteStmt->mExpression);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (genericType != NULL)
|
|
{
|
|
auto genericParamInst = GetGenericParamInstance(genericType);
|
|
Fail(StrFormat("Must add 'where %s : delete' constraint to generic parameter to delete generic type '%s'",
|
|
genericParamInst->GetGenericParamDef()->mName.c_str(), TypeToString(val.mType).c_str()), deleteStmt->mExpression);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (pass == 0)
|
|
{
|
|
if (checkType->IsAllocType())
|
|
checkType = checkType->GetUnderlyingType();
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (checkType->IsVar())
|
|
{
|
|
// Mixin or unconstrained generic
|
|
return;
|
|
}
|
|
|
|
if ((!checkType->IsPointer()) && (!checkType->IsObjectOrInterface()))
|
|
{
|
|
Fail(StrFormat("Cannot delete a value of type '%s'", TypeToString(val.mType).c_str()), deleteStmt->mExpression);
|
|
return;
|
|
}
|
|
|
|
if (val.mType->IsGenericParam())
|
|
return;
|
|
|
|
auto bodyBB = mBfIRBuilder->CreateBlock("delete.body");
|
|
auto endBB = mBfIRBuilder->CreateBlock("delete.end");
|
|
|
|
bool mayBeSentinel = false;
|
|
|
|
if (checkType->IsPointer())
|
|
{
|
|
auto innerType = checkType->GetUnderlyingType();
|
|
PopulateType(innerType);
|
|
if (innerType->IsValuelessType())
|
|
mayBeSentinel = true;
|
|
}
|
|
|
|
BfIRValue isNotNull;
|
|
if (mayBeSentinel)
|
|
{
|
|
auto intVal = mBfIRBuilder->CreatePtrToInt(val.mValue, BfTypeCode_IntPtr);
|
|
isNotNull = mBfIRBuilder->CreateCmpGT(intVal, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 1), false);
|
|
}
|
|
else
|
|
{
|
|
isNotNull = mBfIRBuilder->CreateIsNotNull(val.mValue);
|
|
}
|
|
mBfIRBuilder->CreateCondBr(isNotNull, bodyBB, endBB);
|
|
|
|
mBfIRBuilder->AddBlock(bodyBB);
|
|
mBfIRBuilder->SetInsertPoint(bodyBB);
|
|
|
|
if (val.mType->IsObjectOrInterface())
|
|
{
|
|
EmitObjectAccessCheck(val);
|
|
}
|
|
|
|
SizedArray<BfIRValue, 4> llvmArgs;
|
|
auto bitAddr = mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->GetPrimitiveType(BfTypeCode_NullPtr));
|
|
llvmArgs.push_back(bitAddr);
|
|
if (val.mType->IsObjectOrInterface())
|
|
{
|
|
auto objectType = mContext->mBfObjectType;
|
|
BfTypeInstance* checkTypeInst = val.mType->ToTypeInstance();
|
|
|
|
bool allowPrivate = checkTypeInst == mCurTypeInstance;
|
|
bool allowProtected = allowPrivate || TypeIsSubTypeOf(mCurTypeInstance, checkTypeInst);
|
|
while (checkTypeInst != NULL)
|
|
{
|
|
auto dtorMethodDef = checkTypeInst->mTypeDef->GetMethodByName("~this");
|
|
if (dtorMethodDef)
|
|
{
|
|
if (!CheckProtection(dtorMethodDef->mProtection, checkTypeInst->mTypeDef, allowProtected, allowPrivate))
|
|
{
|
|
auto error = Fail(StrFormat("'%s.~this()' is inaccessible due to its protection level", TypeToString(checkTypeInst).c_str()), deleteStmt->mExpression); // CS0122
|
|
}
|
|
}
|
|
checkTypeInst = checkTypeInst->mBaseType;
|
|
allowPrivate = false;
|
|
}
|
|
|
|
if ((mCompiler->mOptions.mObjectHasDebugFlags) && (!mIsComptimeModule))
|
|
{
|
|
auto preDelete = GetInternalMethod((deleteStmt->mTargetTypeToken != NULL) ? "Dbg_ObjectPreCustomDelete" : "Dbg_ObjectPreDelete");
|
|
SizedArray<BfIRValue, 4> llvmArgs;
|
|
llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(objectType)));
|
|
mBfIRBuilder->CreateCall(preDelete.mFunc, llvmArgs);
|
|
}
|
|
|
|
// call dtor
|
|
BfExprEvaluator expressionEvaluator(this);
|
|
PopulateType(val.mType);
|
|
PopulateType(objectType, BfPopulateType_DataAndMethods);
|
|
|
|
if (objectType->mVirtualMethodTable.size() == 0)
|
|
{
|
|
if (!mCompiler->IsAutocomplete())
|
|
AssertErrorState();
|
|
}
|
|
else if (!IsSkippingExtraResolveChecks())
|
|
{
|
|
BfMethodInstance* methodInstance = objectType->mVirtualMethodTable[mCompiler->GetVTableMethodOffset() + 0].mImplementingMethod;
|
|
BF_ASSERT(methodInstance->mMethodDef->mName == "~this");
|
|
SizedArray<BfIRValue, 4> llvmArgs;
|
|
llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(objectType)));
|
|
expressionEvaluator.CreateCall(deleteStmt->mDeleteToken, methodInstance, mBfIRBuilder->GetFakeVal(), false, llvmArgs);
|
|
}
|
|
|
|
if ((deleteStmt->mTargetTypeToken != NULL) && (!isAppendDelete))
|
|
{
|
|
if (deleteStmt->mAllocExpr != NULL)
|
|
{
|
|
if (customAllocator)
|
|
{
|
|
auto customAllocTypeInst = customAllocator.mType->ToTypeInstance();
|
|
if (customAllocTypeInst != NULL)
|
|
{
|
|
if ((customAllocTypeInst != NULL) && (customAllocTypeInst->mTypeDef->GetMethodByName("FreeObject") != NULL))
|
|
{
|
|
BfTypedValueExpression typedValueExpr;
|
|
typedValueExpr.Init(val);
|
|
typedValueExpr.mRefNode = deleteStmt->mAllocExpr;
|
|
BfExprEvaluator exprEvaluator(this);
|
|
SizedArray<BfExpression*, 2> argExprs;
|
|
argExprs.push_back(&typedValueExpr);
|
|
BfSizedArray<BfExpression*> sizedArgExprs(argExprs);
|
|
BfResolvedArgs argValues(&sizedArgExprs);
|
|
exprEvaluator.ResolveArgValues(argValues);
|
|
exprEvaluator.mNoBind = true;
|
|
exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, true, "FreeObject", argValues, BfMethodGenericArguments());
|
|
customAllocator = BfTypedValue();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((mCompiler->mOptions.mEnableRealtimeLeakCheck) && (!mIsComptimeModule))
|
|
{
|
|
SizedArray<BfIRValue, 4> llvmArgs;
|
|
llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(objectType)));
|
|
auto moduleMethodInstance = GetInternalMethod("Dbg_MarkObjectDeleted");
|
|
mBfIRBuilder->CreateCall(moduleMethodInstance.mFunc, llvmArgs);
|
|
}
|
|
else if (!isAppendDelete)
|
|
{
|
|
mBfIRBuilder->CreateCall(GetBuiltInFunc(BfBuiltInFuncType_Free), llvmArgs);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((isAppendDelete) || (customAllocator))
|
|
{
|
|
// Do nothing
|
|
}
|
|
else
|
|
{
|
|
auto func = GetBuiltInFunc(BfBuiltInFuncType_Free);
|
|
if (!func)
|
|
{
|
|
BF_ASSERT(mCompiler->mIsResolveOnly);
|
|
}
|
|
else
|
|
mBfIRBuilder->CreateCall(func, llvmArgs);
|
|
}
|
|
}
|
|
|
|
if (customAllocator.mType == GetPrimitiveType(BfTypeCode_NullPtr))
|
|
{
|
|
if (!checkType->IsObjectOrInterface())
|
|
Warn(0, "Type '%' has no destructor, so delete:null has no effect", deleteStmt->mExpression);
|
|
}
|
|
else if (customAllocator)
|
|
{
|
|
auto voidPtrType = GetPrimitiveType(BfTypeCode_NullPtr);
|
|
auto ptrValue = BfTypedValue(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(voidPtrType)), voidPtrType);
|
|
BfTypedValueExpression typedValueExpr;
|
|
typedValueExpr.Init(ptrValue);
|
|
BfExprEvaluator exprEvaluator(this);
|
|
SizedArray<BfExpression*, 2> argExprs;
|
|
argExprs.push_back(&typedValueExpr);
|
|
BfSizedArray<BfExpression*> sizedArgExprs(argExprs);
|
|
BfResolvedArgs argValues(&sizedArgExprs);
|
|
exprEvaluator.ResolveArgValues(argValues);
|
|
exprEvaluator.mNoBind = true;
|
|
exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, false, "Free", argValues, BfMethodGenericArguments());
|
|
}
|
|
|
|
mBfIRBuilder->CreateBr(endBB);
|
|
|
|
mBfIRBuilder->AddBlock(endBB);
|
|
mBfIRBuilder->SetInsertPoint(endBB);
|
|
}
|
|
|
|
void BfModule::Visit(BfSwitchStatement* switchStmt)
|
|
{
|
|
BfScopeData outerScope;
|
|
outerScope.mInnerIsConditional = false;
|
|
outerScope.mCloseNode = switchStmt;
|
|
if (switchStmt->mCloseBrace != NULL)
|
|
outerScope.mCloseNode = switchStmt->mCloseBrace;
|
|
mCurMethodState->AddScope(&outerScope);
|
|
NewScopeState();
|
|
|
|
auto valueScopeStartOuter = ValueScopeStart();
|
|
|
|
BfTypedValue switchValue;
|
|
if (switchStmt->mSwitchValue == NULL)
|
|
{
|
|
AssertErrorState();
|
|
UpdateSrcPos(switchStmt->mSwitchToken);
|
|
}
|
|
else
|
|
{
|
|
UpdateExprSrcPos(switchStmt->mSwitchValue);
|
|
|
|
BfEvalExprFlags flags = BfEvalExprFlags_None;
|
|
flags = BfEvalExprFlags_AllowSplat;
|
|
switchValue = CreateValueFromExpression(switchStmt->mSwitchValue, NULL, flags);
|
|
}
|
|
EmitEnsureInstructionAt();
|
|
|
|
if (!switchValue)
|
|
{
|
|
AssertErrorState();
|
|
switchValue = GetDefaultTypedValue(mContext->mBfObjectType);
|
|
}
|
|
|
|
if (switchValue.mType->IsPointer())
|
|
{
|
|
auto underlyingType = switchValue.mType->GetUnderlyingType();
|
|
if (underlyingType->IsEnum())
|
|
{
|
|
switchValue = LoadValue(switchValue);
|
|
switchValue = BfTypedValue(switchValue.mValue, underlyingType, true);
|
|
}
|
|
}
|
|
|
|
// We make the switch value conditional, but all other uses of this scope is conditional since it's conditional on cases
|
|
BfScopeData newScope;
|
|
newScope.mInnerIsConditional = true;
|
|
newScope.mCloseNode = switchStmt;
|
|
if (switchStmt->mCloseBrace != NULL)
|
|
newScope.mCloseNode = switchStmt->mCloseBrace;
|
|
if (switchStmt->mLabelNode != NULL)
|
|
newScope.mLabelNode = switchStmt->mLabelNode->mLabel;
|
|
mCurMethodState->AddScope(&newScope);
|
|
NewScopeState();
|
|
|
|
BfTypedValue switchValueAddr = switchValue;
|
|
|
|
BfLocalVariable* localDef = new BfLocalVariable();
|
|
localDef->mName = "_";
|
|
localDef->mResolvedType = switchValueAddr.mType;
|
|
localDef->mIsReadOnly = true;
|
|
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
if (switchValue.IsAddr())
|
|
{
|
|
localDef->mAddr = switchValue.mValue;
|
|
}
|
|
else
|
|
{
|
|
localDef->mValue = switchValue.mValue;
|
|
localDef->mIsSplat = switchValue.IsSplat();
|
|
}
|
|
|
|
bool wantsDebugInfo = mHasFullDebugInfo && !mBfIRBuilder->mIgnoreWrites;
|
|
|
|
bool tryExtendValue = false;
|
|
bool addDebugInfo = true;
|
|
if ((wantsDebugInfo) && (!switchValue.mType->IsValuelessType()) && (!switchValue.mType->IsVar()))
|
|
{
|
|
if (IsTargetingBeefBackend())
|
|
{
|
|
// We don't need to make a copy
|
|
if (switchValue.IsSplat())
|
|
{
|
|
localDef->mIsSplat = true;
|
|
|
|
if (WantsDebugInfo())
|
|
{
|
|
bool found = false;
|
|
|
|
String varName = "_";
|
|
for (auto dbgVar : mCurMethodState->mLocals)
|
|
{
|
|
if (dbgVar->mAddr == switchValue.mValue)
|
|
{
|
|
varName += "$a$" + dbgVar->mName;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
auto fakeVal = CreateAlloca(GetPrimitiveType(BfTypeCode_Int32), true, "_fake");
|
|
addDebugInfo = false;
|
|
auto diVariable = mBfIRBuilder->DbgCreateAutoVariable(mCurMethodState->mCurScope->mDIScope, varName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, mBfIRBuilder->DbgGetType(localDef->mResolvedType), BfIRInitType_NotNeeded_AliveOnDecl);
|
|
mBfIRBuilder->DbgInsertDeclare(fakeVal, diVariable);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if (!localDef->mAddr)
|
|
// {
|
|
// BfIRValue value = localDef->mValue;
|
|
// if (newLocalVar->mConstValue)
|
|
// value = localDef->mConstValue;
|
|
// auto aliasValue = mBfIRBuilder->CreateAliasValue(value);
|
|
// mBfIRBuilder->DbgInsertValueIntrinsic(aliasValue, diVariable);
|
|
// scopeData.mDeferredLifetimeEnds.push_back(aliasValue);
|
|
// }
|
|
|
|
tryExtendValue = true;
|
|
}
|
|
}
|
|
else if ((switchValue.mType->IsComposite()) && (switchValue.IsAddr()))
|
|
{
|
|
auto refType = CreateRefType(switchValue.mType);
|
|
auto allocaVal = CreateAlloca(refType);
|
|
mBfIRBuilder->CreateStore(switchValue.mValue, allocaVal);
|
|
|
|
auto diType = mBfIRBuilder->DbgGetType(refType);
|
|
auto diVariable = mBfIRBuilder->DbgCreateAutoVariable(mCurMethodState->mCurScope->mDIScope,
|
|
localDef->mName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, diType);
|
|
mBfIRBuilder->DbgInsertDeclare(allocaVal, diVariable);
|
|
addDebugInfo = false;
|
|
}
|
|
else
|
|
{
|
|
if (switchValueAddr.IsSplat())
|
|
{
|
|
auto addr = CreateAlloca(switchValue.mType);
|
|
if (switchValue.IsSplat())
|
|
AggregateSplatIntoAddr(switchValue, addr);
|
|
else
|
|
mBfIRBuilder->CreateStore(switchValue.mValue, addr);
|
|
localDef->mAddr = addr;
|
|
localDef->mValue = BfIRValue();
|
|
localDef->mIsSplat = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!localDef->mResolvedType->IsVar())
|
|
AddLocalVariableDef(localDef, addDebugInfo, true);
|
|
|
|
BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope);
|
|
deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
|
|
int numExpressions = 0;
|
|
|
|
SizedArray<BfIRBlock, 8> blocks;
|
|
SizedArray<BfWhenExpression*, 8> whenExprs;
|
|
SizedArray<BfIRBlock, 8> whenFailBlocks;
|
|
BfIRBlock defaultBlock;
|
|
auto endBlock = mBfIRBuilder->CreateBlock("switch.end");
|
|
|
|
for (BfSwitchCase* switchCase : switchStmt->mSwitchCases)
|
|
{
|
|
auto caseBlock = mBfIRBuilder->CreateBlock(StrFormat("switch.%d", blocks.size()));
|
|
blocks.push_back(caseBlock);
|
|
numExpressions += (int)switchCase->mCaseExpressions.size();
|
|
}
|
|
|
|
defaultBlock = mBfIRBuilder->CreateBlock("default");
|
|
bool hasDefaultCase = switchStmt->mDefaultCase != NULL;
|
|
if (hasDefaultCase)
|
|
blocks.push_back(defaultBlock);
|
|
|
|
SizedArray<BfDeferredLocalAssignData, 8> deferredLocalAssignDataVec;
|
|
deferredLocalAssignDataVec.resize(blocks.size());
|
|
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
|
|
BfTypedValue enumTagVal;
|
|
|
|
// Declare cases
|
|
int blockIdx = 0;
|
|
bool hadConstIntVals = false;
|
|
bool hadWhen = false;
|
|
BfIRValue switchStatement;
|
|
auto switchBlock = mBfIRBuilder->GetInsertBlock();
|
|
BfIRBlock noSwitchBlock = mBfIRBuilder->CreateBlock("noSwitch", true);
|
|
|
|
BfPrimitiveType* intCoercibleType = GetIntCoercibleType(switchValue.mType);
|
|
|
|
bool isConstSwitch = false;
|
|
if ((mBfIRBuilder->IsConstValue(switchValue.mValue)) || (switchValue.mType->IsValuelessType()))
|
|
{
|
|
isConstSwitch = true;
|
|
}
|
|
|
|
if (switchValue.mValue)
|
|
{
|
|
mBfIRBuilder->PopulateType(switchValue.mType);
|
|
|
|
if (intCoercibleType != NULL)
|
|
{
|
|
auto intValue = GetIntCoercible(switchValue);
|
|
switchStatement = mBfIRBuilder->CreateSwitch(intValue.mValue, noSwitchBlock, numExpressions);
|
|
}
|
|
else if (switchValue.mType->IsPayloadEnum())
|
|
{
|
|
enumTagVal = ExtractValue(switchValue, NULL, 2);
|
|
enumTagVal = LoadValue(enumTagVal);
|
|
switchStatement = mBfIRBuilder->CreateSwitch(enumTagVal.mValue, noSwitchBlock, numExpressions);
|
|
}
|
|
else if ((!isConstSwitch) && (!switchValue.mType->IsVar()))
|
|
switchStatement = mBfIRBuilder->CreateSwitch(switchValue.mValue, noSwitchBlock, numExpressions);
|
|
}
|
|
|
|
auto valueScopeStartInner = ValueScopeStart();
|
|
|
|
mBfIRBuilder->SetInsertPoint(noSwitchBlock);
|
|
|
|
bool isPayloadEnum = switchValue.mType->IsPayloadEnum();
|
|
bool isTuple = switchValue.mType->IsTuple();
|
|
bool isIntegralSwitch = switchValue.mType->IsIntegral() || (intCoercibleType != NULL) || ((switchValue.mType->IsEnum()) && (!isPayloadEnum));
|
|
|
|
auto _ShowCaseError = [&] (int64 id, BfAstNode* errNode)
|
|
{
|
|
if (isPayloadEnum)
|
|
{
|
|
auto enumType = switchValue.mType->ToTypeInstance();
|
|
for (auto& fieldInstance : enumType->mFieldInstances)
|
|
{
|
|
auto fieldDef = fieldInstance.GetFieldDef();
|
|
if (fieldDef->IsEnumCaseEntry())
|
|
{
|
|
int enumIdx = -fieldInstance.mDataIdx - 1;
|
|
if (enumIdx == id)
|
|
{
|
|
Fail(StrFormat("The switch statement already contains a case for the the value '%s'", fieldDef->mName.c_str()), errNode);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Fail(StrFormat("The switch statement already contains a case for the the value '%lld'", id), errNode);
|
|
};
|
|
|
|
int caseCount = 0;
|
|
bool allHadReturns = true;
|
|
bool hadCondCase = false;
|
|
BfIRBlock lastDefaultBlock;
|
|
|
|
struct _CaseState
|
|
{
|
|
BfIRBlock mCondBlock;
|
|
BfIRBlock mUncondBlock;
|
|
};
|
|
|
|
bool hadConstMatch = false;
|
|
|
|
auto startingLocalVarId = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
|
|
|
|
bool prevHadFallthrough = false;
|
|
|
|
Dictionary<int64, _CaseState> handledCases;
|
|
HashSet<int64> condCases;
|
|
for (BfSwitchCase* switchCase : switchStmt->mSwitchCases)
|
|
{
|
|
deferredLocalAssignDataVec[blockIdx].mScopeData = mCurMethodState->mCurScope;
|
|
deferredLocalAssignDataVec[blockIdx].ExtendFrom(mCurMethodState->mDeferredLocalAssignData);
|
|
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &deferredLocalAssignDataVec[blockIdx]);
|
|
mCurMethodState->mDeferredLocalAssignData->mVarIdBarrier = startingLocalVarId;
|
|
|
|
SetIllegalSrcPos();
|
|
auto caseBlock = blocks[blockIdx];
|
|
|
|
BfScopeData caseScopeData;
|
|
bool openedScope = false;
|
|
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
|
|
{
|
|
// This does the enum autocomplete popup
|
|
BfAstNode* checkNode = NULL;
|
|
int caseExprIdx = (int)switchCase->mCaseExpressions.size();
|
|
if (caseExprIdx == 0)
|
|
checkNode = switchCase->mCaseToken;
|
|
else if (caseExprIdx - 1 < (int)switchCase->mCaseCommas.size())
|
|
checkNode = switchCase->mCaseCommas[caseExprIdx - 1];
|
|
if (checkNode != NULL)
|
|
mCompiler->mResolvePassData->mAutoComplete->CheckEmptyStart(checkNode, switchValue.mType);
|
|
}
|
|
|
|
bool mayHaveMatch = false;
|
|
|
|
BfWhenExpression* whenExpr = NULL;
|
|
|
|
for (BfExpression* caseExpr : switchCase->mCaseExpressions)
|
|
{
|
|
if (auto checkWhenExpr = BfNodeDynCast<BfWhenExpression>(caseExpr))
|
|
{
|
|
hadWhen = true;
|
|
whenExpr = checkWhenExpr;
|
|
}
|
|
}
|
|
|
|
bool wantsOpenedScope = isPayloadEnum || isTuple;
|
|
|
|
BfIRBlock lastNotEqBlock;
|
|
for (BfExpression* caseExpr : switchCase->mCaseExpressions)
|
|
{
|
|
BfConstant* constantInt = NULL;
|
|
|
|
if (auto checkWhenExpr = BfNodeDynCast<BfWhenExpression>(caseExpr))
|
|
continue;
|
|
|
|
if ((!openedScope) && (wantsOpenedScope))
|
|
{
|
|
openedScope = true;
|
|
|
|
caseScopeData.mOuterIsConditional = true;
|
|
caseScopeData.mIsSharedTempBlock = true;
|
|
mCurMethodState->AddScope(&caseScopeData);
|
|
NewScopeState();
|
|
UpdateSrcPos(caseExpr);
|
|
SetIllegalSrcPos();
|
|
}
|
|
|
|
BfIRValue eqResult;
|
|
BfIRBlock notEqBB;
|
|
bool handled = false;
|
|
BfTypedValue caseValue;
|
|
BfIRBlock doBlock = caseBlock;
|
|
bool hadConditional = false;
|
|
bool isEnumDescValue = isPayloadEnum;
|
|
if (isPayloadEnum)
|
|
{
|
|
auto dscrType = switchValue.mType->ToTypeInstance()->GetDiscriminatorType();
|
|
if (!enumTagVal)
|
|
{
|
|
enumTagVal = ExtractValue(switchValue, NULL, 2);
|
|
enumTagVal = LoadValue(enumTagVal);
|
|
}
|
|
|
|
notEqBB = mBfIRBuilder->CreateBlock(StrFormat("switch.notEq.%d", blockIdx), false);
|
|
int tagId = -1;
|
|
BfIRBlock matchBlock;
|
|
BfTypedValue eqTypedResult;
|
|
if (auto bindExpr = BfNodeDynCast<BfEnumCaseBindExpression>(caseExpr))
|
|
{
|
|
eqTypedResult = HandleCaseBind(switchValueAddr, enumTagVal, bindExpr, &caseBlock, ¬EqBB, &matchBlock, &tagId);
|
|
}
|
|
else
|
|
{
|
|
eqTypedResult = TryCaseEnumMatch(switchValueAddr, enumTagVal, caseExpr, &caseBlock, ¬EqBB, &matchBlock, tagId, hadConditional, false, prevHadFallthrough);
|
|
if (auto constant = mBfIRBuilder->GetConstant(eqTypedResult.mValue))
|
|
{
|
|
if (constant->mBool)
|
|
mayHaveMatch = true;
|
|
}
|
|
if (hadConditional)
|
|
hadCondCase = true;
|
|
}
|
|
|
|
if (tagId != -1)
|
|
{
|
|
doBlock = matchBlock; // Jump to binds rather than just the code
|
|
caseValue = BfTypedValue(GetConstValue(tagId, GetPrimitiveType(dscrType->mTypeDef->mTypeCode)), dscrType);
|
|
}
|
|
else
|
|
hadCondCase = true;
|
|
|
|
if (eqTypedResult)
|
|
{
|
|
handled = true;
|
|
eqResult = eqTypedResult.mValue;
|
|
}
|
|
}
|
|
else if (auto tupleExpr = BfNodeDynCast<BfTupleExpression>(caseExpr))
|
|
{
|
|
notEqBB = mBfIRBuilder->CreateBlock(StrFormat("switch.notEq.%d", blockIdx), false);
|
|
|
|
BfIRBlock matchBlock;
|
|
BfTypedValue eqTypedResult = TryCaseTupleMatch(switchValue, tupleExpr, &caseBlock, ¬EqBB, &matchBlock, hadConditional, false, prevHadFallthrough);
|
|
if (hadConditional)
|
|
hadCondCase = true;
|
|
if (eqTypedResult)
|
|
{
|
|
mayHaveMatch = true;
|
|
handled = true;
|
|
eqResult = eqTypedResult.mValue;
|
|
}
|
|
}
|
|
|
|
if (!eqResult)
|
|
{
|
|
caseValue = CreateValueFromExpression(caseExpr, switchValue.mType, (BfEvalExprFlags)(BfEvalExprFlags_AllowEnumId | BfEvalExprFlags_NoCast));
|
|
if (!caseValue)
|
|
continue;
|
|
isEnumDescValue = false;
|
|
}
|
|
|
|
BfTypedValue caseIntVal = caseValue;
|
|
if ((isIntegralSwitch) || (isPayloadEnum))
|
|
{
|
|
if ((intCoercibleType != NULL) &&
|
|
(caseValue.mType == switchValue.mType) &&
|
|
(caseValue.mValue.IsConst()))
|
|
{
|
|
caseIntVal = GetIntCoercible(caseValue);
|
|
constantInt = mBfIRBuilder->GetConstant(caseIntVal.mValue);
|
|
}
|
|
else
|
|
{
|
|
// For a non-const case, allow for conversion operators, otherwise cast now
|
|
if ((isIntegralSwitch) && (caseValue.mValue.IsConst()))
|
|
{
|
|
if (caseValue.mType != switchValue.mType)
|
|
{
|
|
caseValue = Cast(caseExpr, caseValue, switchValue.mType);
|
|
if (!caseValue)
|
|
continue;
|
|
caseIntVal = caseValue;
|
|
}
|
|
}
|
|
|
|
if ((caseValue.mType == switchValue.mType) || (eqResult))
|
|
{
|
|
constantInt = mBfIRBuilder->GetConstant(caseValue.mValue);
|
|
if ((constantInt != NULL) && (!mBfIRBuilder->IsInt(constantInt->mTypeCode)))
|
|
constantInt = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((!switchStatement) && (!isConstSwitch))
|
|
{
|
|
// Do nothing
|
|
mayHaveMatch = true;
|
|
}
|
|
else if ((constantInt != NULL) && (!hadWhen) && (!isConstSwitch))
|
|
{
|
|
if (hadConditional)
|
|
{
|
|
condCases.Add(constantInt->mInt64);
|
|
}
|
|
else
|
|
{
|
|
_CaseState* caseState = NULL;
|
|
handledCases.TryAdd(constantInt->mInt64, NULL, &caseState);
|
|
|
|
if (condCases.Contains(constantInt->mInt64))
|
|
{
|
|
// This is a 'case .A:' after a 'case .A(let value):'
|
|
eqResult = mBfIRBuilder->CreateCmpEQ(enumTagVal.mValue, caseValue.mValue);
|
|
notEqBB = mBfIRBuilder->CreateBlock(StrFormat("switch.notEq.%d", blockIdx));
|
|
|
|
mayHaveMatch = true;
|
|
mBfIRBuilder->CreateCondBr(eqResult, caseBlock, notEqBB);
|
|
|
|
mBfIRBuilder->AddBlock(notEqBB);
|
|
mBfIRBuilder->SetInsertPoint(notEqBB);
|
|
}
|
|
else
|
|
{
|
|
if (caseState->mUncondBlock)
|
|
{
|
|
_ShowCaseError(constantInt->mInt64, caseExpr);
|
|
}
|
|
else
|
|
{
|
|
caseState->mUncondBlock = doBlock;
|
|
mBfIRBuilder->AddSwitchCase(switchStatement, caseIntVal.mValue, doBlock);
|
|
hadConstIntVals = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
mayHaveMatch = true;
|
|
}
|
|
else if (!handled)
|
|
{
|
|
hadCondCase = true;
|
|
|
|
if (!eqResult)
|
|
{
|
|
BfExprEvaluator exprEvaluator(this);
|
|
BfAstNode* refNode = switchCase->mColonToken;
|
|
|
|
if ((caseValue.mType->IsPayloadEnum()) && (caseValue.mValue.IsConst()) && (switchValue.mType == caseValue.mType) &&
|
|
((isEnumDescValue) || (constantInt != NULL)))
|
|
{
|
|
if (!enumTagVal)
|
|
{
|
|
enumTagVal = ExtractValue(switchValue, NULL, 2);
|
|
enumTagVal = LoadValue(enumTagVal);
|
|
}
|
|
eqResult = mBfIRBuilder->CreateCmpEQ(enumTagVal.mValue, caseValue.mValue);
|
|
}
|
|
else
|
|
{
|
|
exprEvaluator.PerformBinaryOperation(switchStmt->mSwitchValue, caseExpr, BfBinaryOp_Equality, refNode, (BfBinOpFlags)(BfBinOpFlag_ForceLeftType), switchValue, caseValue);
|
|
|
|
if (switchStmt->mSwitchValue != NULL)
|
|
UpdateSrcPos(switchStmt->mSwitchValue);
|
|
SetIllegalSrcPos();
|
|
|
|
eqResult = exprEvaluator.mResult.mValue;
|
|
if (!eqResult)
|
|
eqResult = GetConstValue(0, boolType);
|
|
}
|
|
}
|
|
|
|
ValueScopeEnd(valueScopeStartInner);
|
|
|
|
bool isConstResult = false;
|
|
bool constResult = false;
|
|
if (eqResult.IsConst())
|
|
{
|
|
auto constant = mBfIRBuilder->GetConstant(eqResult);
|
|
if (constant->mTypeCode == BfTypeCode_Boolean)
|
|
{
|
|
isConstResult = true;
|
|
constResult = constant->mBool;
|
|
}
|
|
}
|
|
|
|
if (isConstResult)
|
|
{
|
|
if (constResult)
|
|
{
|
|
mBfIRBuilder->CreateBr(caseBlock);
|
|
mayHaveMatch = true;
|
|
|
|
if (whenExpr == NULL)
|
|
{
|
|
hadConstMatch = true;
|
|
}
|
|
else
|
|
{
|
|
notEqBB = mBfIRBuilder->CreateBlock(StrFormat("switch.notEq.%d", blockIdx));
|
|
|
|
mBfIRBuilder->AddBlock(notEqBB);
|
|
mBfIRBuilder->SetInsertPoint(notEqBB);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
notEqBB = mBfIRBuilder->CreateBlock(StrFormat("switch.notEq.%d", blockIdx));
|
|
|
|
mayHaveMatch = true;
|
|
mBfIRBuilder->CreateCondBr(eqResult, caseBlock, notEqBB);
|
|
|
|
mBfIRBuilder->AddBlock(notEqBB);
|
|
mBfIRBuilder->SetInsertPoint(notEqBB);
|
|
}
|
|
}
|
|
else if (whenExpr != NULL)
|
|
{
|
|
mayHaveMatch = true;
|
|
}
|
|
|
|
if (notEqBB)
|
|
lastNotEqBlock = notEqBB;
|
|
|
|
if ((!hadCondCase) && (notEqBB))
|
|
lastDefaultBlock = notEqBB;
|
|
}
|
|
|
|
if ((whenExpr != NULL) && (switchCase->mCaseExpressions.size() == 1))
|
|
{
|
|
// This was a "case when" expression, always matches
|
|
mayHaveMatch = true;
|
|
|
|
auto notEqBB = mBfIRBuilder->CreateBlock(StrFormat("switch.notEq_when.%d", blockIdx));
|
|
|
|
mBfIRBuilder->CreateBr(caseBlock);
|
|
mBfIRBuilder->AddBlock(notEqBB);
|
|
mBfIRBuilder->SetInsertPoint(notEqBB);
|
|
|
|
lastNotEqBlock = notEqBB;
|
|
}
|
|
|
|
if ((lastDefaultBlock) && (switchStatement))
|
|
mBfIRBuilder->SetSwitchDefaultDest(switchStatement, lastDefaultBlock);
|
|
|
|
auto prevInsertBlock = mBfIRBuilder->GetInsertBlock();
|
|
|
|
bool isConstIgnore = !mayHaveMatch && !prevHadFallthrough;
|
|
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true, isConstIgnore);
|
|
SetAndRestoreValue<bool> prevInConstIgnore(mCurMethodState->mCurScope->mInConstIgnore, true, isConstIgnore);
|
|
|
|
mBfIRBuilder->AddBlock(caseBlock);
|
|
mBfIRBuilder->SetInsertPoint(caseBlock);
|
|
|
|
if (whenExpr != NULL)
|
|
{
|
|
UpdateSrcPos(whenExpr);
|
|
|
|
BfTypedValue whenValue;
|
|
if (whenExpr->mExpression != NULL)
|
|
whenValue = CreateValueFromExpression(whenExpr->mExpression, boolType, BfEvalExprFlags_AllowEnumId);
|
|
if (!whenValue)
|
|
{
|
|
AssertErrorState();
|
|
whenValue = GetDefaultTypedValue(boolType);
|
|
}
|
|
|
|
bool constResult = false;
|
|
if (mBfIRBuilder->TryGetBool(whenValue.mValue, constResult))
|
|
{
|
|
if (!constResult)
|
|
prevIgnoreWrites.Set();
|
|
}
|
|
else
|
|
{
|
|
BfIRBlock eqBB = mBfIRBuilder->CreateBlock(StrFormat("switch.when.%d", blockIdx));
|
|
|
|
mBfIRBuilder->CreateCondBr(whenValue.mValue, eqBB, lastNotEqBlock);
|
|
mBfIRBuilder->AddBlock(eqBB);
|
|
mBfIRBuilder->SetInsertPoint(eqBB);
|
|
}
|
|
}
|
|
|
|
BfIRBlock fallthroughBlock;
|
|
if (blockIdx < (int) blocks.size() - 1)
|
|
fallthroughBlock = blocks[blockIdx + 1];
|
|
else
|
|
fallthroughBlock = defaultBlock;
|
|
|
|
bool hadReturn = false;
|
|
if ((switchCase->mCodeBlock != NULL) && (!switchCase->mCodeBlock->mChildArr.IsEmpty()))
|
|
{
|
|
UpdateSrcPos(switchCase->mCodeBlock);
|
|
|
|
VisitCodeBlock(switchCase->mCodeBlock, BfIRBlock(), endBlock, fallthroughBlock, true, &hadReturn, switchStmt->mLabelNode, openedScope /*, BfEmbeddedStatementFlags_RescopeDLA*/);
|
|
|
|
openedScope = false;
|
|
deferredLocalAssignDataVec[blockIdx].mHadReturn = hadReturn;
|
|
caseCount++;
|
|
if ((!hadReturn) &&
|
|
((!mCurMethodState->mDeferredLocalAssignData->mHadFallthrough) || (mCurMethodState->mDeferredLocalAssignData->mHadBreak)))
|
|
allHadReturns = false;
|
|
|
|
if (auto block = BfNodeDynCast<BfBlock>(switchCase->mCodeBlock))
|
|
{
|
|
//
|
|
}
|
|
else
|
|
{
|
|
if (switchStmt->mCloseBrace != NULL)
|
|
{
|
|
UpdateSrcPos(switchStmt->mCloseBrace);
|
|
}
|
|
EmitEnsureInstructionAt();
|
|
}
|
|
|
|
//UpdateSrcPos(switchCase->mCodeBlock);
|
|
//SetIllegalSrcPos();
|
|
|
|
mBfIRBuilder->ClearDebugLocation();
|
|
}
|
|
else
|
|
{
|
|
if (openedScope)
|
|
RestoreScopeState();
|
|
mBfIRBuilder->CreateBr(endBlock);
|
|
allHadReturns = false;
|
|
}
|
|
|
|
prevIgnoreWrites.Restore();
|
|
|
|
mBfIRBuilder->SetInsertPoint(prevInsertBlock);
|
|
|
|
prevHadFallthrough = mCurMethodState->mDeferredLocalAssignData->mHadFallthrough;
|
|
|
|
blockIdx++;
|
|
}
|
|
|
|
// Check for comprehensiveness
|
|
bool isComprehensive = true;
|
|
if ((switchValue) && (switchStmt->mDefaultCase == NULL))
|
|
{
|
|
if (switchValue.mType->IsEnum())
|
|
{
|
|
if (hadConstMatch)
|
|
{
|
|
// Already handled
|
|
}
|
|
else
|
|
{
|
|
auto enumType = switchValue.mType->ToTypeInstance();
|
|
if (enumType->IsPayloadEnum())
|
|
{
|
|
int lastTagId = -1;
|
|
for (auto& field : enumType->mFieldInstances)
|
|
{
|
|
auto fieldDef = field.GetFieldDef();
|
|
if (fieldDef == NULL)
|
|
continue;
|
|
if (!fieldDef->IsEnumCaseEntry())
|
|
continue;
|
|
if (field.mDataIdx < 0)
|
|
lastTagId = -field.mDataIdx - 1;
|
|
}
|
|
isComprehensive = lastTagId == (int)handledCases.size() - 1;
|
|
}
|
|
else
|
|
{
|
|
for (auto& field : enumType->mFieldInstances)
|
|
{
|
|
auto fieldDef = field.GetFieldDef();
|
|
if ((fieldDef != NULL) && (fieldDef->IsEnumCaseEntry()))
|
|
{
|
|
if (field.mConstIdx != -1)
|
|
{
|
|
auto constant = enumType->mConstHolder->GetConstantById(field.mConstIdx);
|
|
isComprehensive &= handledCases.ContainsKey(constant->mInt64);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isComprehensive)
|
|
{
|
|
BfAstNode* refNode = switchStmt->mSwitchToken;
|
|
Fail("Switch must be exhaustive, consider adding a default clause", switchStmt->mSwitchToken);
|
|
if ((switchStmt->mCloseBrace) && (mCompiler->IsAutocomplete()) && (mCompiler->mResolvePassData->mAutoComplete->CheckFixit((refNode))))
|
|
{
|
|
BfParserData* parser = refNode->GetSourceData()->ToParserData();
|
|
if (parser != NULL)
|
|
{
|
|
int fileLoc = switchStmt->mCloseBrace->GetSrcStart();
|
|
mCompiler->mResolvePassData->mAutoComplete->AddEntry(AutoCompleteEntry("fixit", StrFormat("default:\tdefault:|%s|%d||default:", parser->mFileName.c_str(), fileLoc).c_str()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
isComprehensive = false;
|
|
}
|
|
|
|
if (!hadConstMatch)
|
|
mBfIRBuilder->CreateBr(defaultBlock);
|
|
|
|
mBfIRBuilder->SetInsertPoint(switchBlock);
|
|
if (!hadConstIntVals)
|
|
{
|
|
if (switchStatement)
|
|
mBfIRBuilder->EraseInstFromParent(switchStatement);
|
|
mBfIRBuilder->CreateBr(noSwitchBlock);
|
|
}
|
|
|
|
if (switchStmt->mDefaultCase != NULL)
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true, hadConstMatch);
|
|
SetAndRestoreValue<bool> prevInConstIgnore(mCurMethodState->mCurScope->mInConstIgnore, true, hadConstMatch);
|
|
|
|
mBfIRBuilder->AddBlock(defaultBlock);
|
|
mBfIRBuilder->SetInsertPoint(defaultBlock);
|
|
|
|
auto switchCase = switchStmt->mDefaultCase;
|
|
if (switchCase->mCodeBlock != NULL)
|
|
{
|
|
isComprehensive = true;
|
|
UpdateSrcPos(switchCase->mCodeBlock);
|
|
|
|
deferredLocalAssignDataVec[blockIdx].mScopeData = mCurMethodState->mCurScope;
|
|
deferredLocalAssignDataVec[blockIdx].ExtendFrom(mCurMethodState->mDeferredLocalAssignData);
|
|
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &deferredLocalAssignDataVec[blockIdx]);
|
|
mCurMethodState->mDeferredLocalAssignData->mVarIdBarrier = startingLocalVarId;
|
|
|
|
BfScopeData caseScopeData;
|
|
caseScopeData.mOuterIsConditional = true;
|
|
caseScopeData.mIsSharedTempBlock = true;
|
|
mCurMethodState->AddScope(&caseScopeData);
|
|
NewScopeState();
|
|
caseScopeData.mLabel = newScope.mLabel;
|
|
|
|
bool hadReturn = false;
|
|
VisitCodeBlock(switchCase->mCodeBlock, BfIRBlock(), endBlock, BfIRBlock(), true, &hadReturn, switchStmt->mLabelNode);
|
|
deferredLocalAssignDataVec[blockIdx].mHadReturn = hadReturn;
|
|
caseCount++;
|
|
if (!hadReturn)
|
|
allHadReturns = false;
|
|
|
|
RestoreScopeState();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mBfIRBuilder->AddBlock(defaultBlock);
|
|
mBfIRBuilder->SetInsertPoint(defaultBlock);
|
|
if (isComprehensive)
|
|
{
|
|
mBfIRBuilder->CreateUnreachable();
|
|
//TODO: This masks a bug in our backend
|
|
if (IsTargetingBeefBackend())
|
|
mBfIRBuilder->CreateBr(endBlock);
|
|
}
|
|
else
|
|
mBfIRBuilder->CreateBr(endBlock);
|
|
}
|
|
|
|
if (isComprehensive)
|
|
{
|
|
// Merge and apply deferred local assign data
|
|
// We only do this if there's a default case, otherwise we assume we may have missed a case
|
|
// that by definition had no local assigns
|
|
BfDeferredLocalAssignData* mergedDeferredLocalAssignData = NULL;
|
|
for (blockIdx = 0; blockIdx < (int)blocks.size(); blockIdx++)
|
|
{
|
|
auto deferredLocalAssignData = &deferredLocalAssignDataVec[blockIdx];
|
|
if (deferredLocalAssignData->mHadFallthrough)
|
|
continue;
|
|
|
|
if (mergedDeferredLocalAssignData == NULL)
|
|
mergedDeferredLocalAssignData = deferredLocalAssignData;
|
|
else
|
|
mergedDeferredLocalAssignData->SetIntersection(*deferredLocalAssignData);
|
|
}
|
|
|
|
if (mergedDeferredLocalAssignData != NULL)
|
|
mCurMethodState->ApplyDeferredLocalAssignData(*mergedDeferredLocalAssignData);
|
|
}
|
|
|
|
if ((caseCount > 0) && (allHadReturns) &&
|
|
((hasDefaultCase) || (isComprehensive)))
|
|
{
|
|
mCurMethodState->SetHadReturn(true);
|
|
mCurMethodState->mLeftBlockUncond = true;
|
|
|
|
if ((!hasDefaultCase) && (!isComprehensive))
|
|
mBfIRBuilder->DeleteBlock(endBlock);
|
|
else
|
|
{
|
|
if (switchStmt->mCloseBrace != NULL)
|
|
UpdateSrcPos(switchStmt->mCloseBrace);
|
|
mBfIRBuilder->AddBlock(endBlock);
|
|
mBfIRBuilder->SetInsertPoint(endBlock);
|
|
mBfIRBuilder->CreateUnreachable();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (switchStmt->mCloseBrace != NULL)
|
|
UpdateSrcPos(switchStmt->mCloseBrace);
|
|
|
|
mBfIRBuilder->AddBlock(endBlock);
|
|
mBfIRBuilder->SetInsertPoint(endBlock);
|
|
}
|
|
|
|
BfIRValue lifetimeExtendVal;
|
|
if (tryExtendValue)
|
|
{
|
|
if (localDef->mAddr)
|
|
lifetimeExtendVal = localDef->mAddr;
|
|
else
|
|
lifetimeExtendVal = localDef->mValue;
|
|
}
|
|
|
|
RestoreScopeState(); // newScope
|
|
RestoreScopeState(); // outerScope
|
|
|
|
if (lifetimeExtendVal)
|
|
mBfIRBuilder->CreateLifetimeExtend(lifetimeExtendVal);
|
|
|
|
ValueScopeEnd(valueScopeStartOuter);
|
|
}
|
|
|
|
static int gRetIdx = 0;
|
|
|
|
void BfModule::Visit(BfReturnStatement* returnStmt)
|
|
{
|
|
if ((mCurMethodInstance == NULL) || (mCurMethodState->mDisableReturns))
|
|
{
|
|
Fail("Unexpected return", returnStmt);
|
|
if (returnStmt->mExpression != NULL)
|
|
{
|
|
BfExprEvaluator exprEvaluator(this);
|
|
CreateValueFromExpression(exprEvaluator, returnStmt->mExpression);
|
|
}
|
|
return;
|
|
}
|
|
|
|
UpdateSrcPos(returnStmt);
|
|
EmitEnsureInstructionAt();
|
|
|
|
auto retType = mCurMethodInstance->mReturnType;
|
|
if (mCurMethodInstance->IsMixin())
|
|
retType = NULL;
|
|
|
|
bool inferReturnType = false;
|
|
if (mCurMethodState->mClosureState != NULL)
|
|
{
|
|
retType = mCurMethodState->mClosureState->mReturnType;
|
|
inferReturnType = (mCurMethodState->mClosureState->mReturnTypeInferState != BfReturnTypeInferState_None);
|
|
}
|
|
|
|
auto checkScope = mCurMethodState->mCurScope;
|
|
while (checkScope != NULL)
|
|
{
|
|
if (checkScope->mIsDeferredBlock)
|
|
{
|
|
Fail("Deferred blocks cannot contain 'return' statements", returnStmt);
|
|
if (returnStmt->mExpression != NULL)
|
|
{
|
|
BfExprEvaluator exprEvaluator(this);
|
|
CreateValueFromExpression(exprEvaluator, returnStmt->mExpression, GetPrimitiveType(BfTypeCode_Var), BfEvalExprFlags_None);
|
|
}
|
|
return;
|
|
}
|
|
if (checkScope->mInInitBlock)
|
|
{
|
|
Fail("Initialization blocks cannot contain 'return' statements", returnStmt);
|
|
}
|
|
checkScope = checkScope->mPrevScope;
|
|
}
|
|
|
|
auto checkLocalAssignData = mCurMethodState->mDeferredLocalAssignData;
|
|
while (checkLocalAssignData != NULL)
|
|
{
|
|
if (checkLocalAssignData->mScopeData != NULL)
|
|
checkLocalAssignData->mLeftBlock = true;
|
|
checkLocalAssignData = checkLocalAssignData->mChainedAssignData;
|
|
}
|
|
|
|
if ((retType == NULL) && (!inferReturnType))
|
|
{
|
|
if (returnStmt->mExpression != NULL)
|
|
{
|
|
BfExprEvaluator exprEvaluator(this);
|
|
CreateValueFromExpression(exprEvaluator, returnStmt->mExpression, GetPrimitiveType(BfTypeCode_Var), BfEvalExprFlags_None);
|
|
}
|
|
MarkScopeLeft(&mCurMethodState->mHeadScope);
|
|
return;
|
|
}
|
|
|
|
if (returnStmt->mExpression == NULL)
|
|
{
|
|
MarkScopeLeft(&mCurMethodState->mHeadScope);
|
|
|
|
if ((retType != NULL) && (retType->IsVoid()))
|
|
{
|
|
EmitReturn(BfTypedValue());
|
|
return;
|
|
}
|
|
|
|
Fail("Expected return value", returnStmt);
|
|
if (retType != NULL)
|
|
EmitReturn(GetDefaultTypedValue(retType));
|
|
else
|
|
EmitReturn(BfTypedValue());
|
|
return;
|
|
}
|
|
|
|
BfType* expectingReturnType = retType;
|
|
BfType* origType;
|
|
BfExprEvaluator exprEvaluator(this);
|
|
bool alreadyWritten = false;
|
|
if ((!mIsComptimeModule) && (mCurMethodInstance->GetStructRetIdx() != -1))
|
|
exprEvaluator.mReceivingValue = &mCurMethodState->mRetVal;
|
|
if (mCurMethodInstance->mMethodDef->mIsReadOnly)
|
|
exprEvaluator.mAllowReadOnlyReference = true;
|
|
|
|
if (inferReturnType)
|
|
expectingReturnType = NULL;
|
|
|
|
auto retValue = CreateValueFromExpression(exprEvaluator, returnStmt->mExpression, expectingReturnType, BfEvalExprFlags_AllowRefExpr, &origType);
|
|
|
|
if ((retValue) && (inferReturnType))
|
|
{
|
|
if (mCurMethodState->mClosureState->mReturnType == NULL)
|
|
mCurMethodState->mClosureState->mReturnType = retValue.mType;
|
|
else
|
|
{
|
|
if ((retValue.mType == mCurMethodState->mClosureState->mReturnType) ||
|
|
(CanCast(retValue, mCurMethodState->mClosureState->mReturnType)))
|
|
{
|
|
// Leave as-is
|
|
}
|
|
else if (CanCast(GetFakeTypedValue(mCurMethodState->mClosureState->mReturnType), retValue.mType))
|
|
{
|
|
mCurMethodState->mClosureState->mReturnType = retValue.mType;
|
|
}
|
|
else
|
|
{
|
|
mCurMethodState->mClosureState->mReturnTypeInferState = BfReturnTypeInferState_Fail;
|
|
}
|
|
}
|
|
}
|
|
if ((retType == NULL) && (inferReturnType))
|
|
retType = mCurMethodState->mClosureState->mReturnType;
|
|
if (retType == NULL)
|
|
retType = GetPrimitiveType(BfTypeCode_None);
|
|
|
|
if ((!mIsComptimeModule) && (mCurMethodInstance->GetStructRetIdx() != -1))
|
|
alreadyWritten = exprEvaluator.mReceivingValue == NULL;
|
|
MarkScopeLeft(&mCurMethodState->mHeadScope);
|
|
|
|
if (!retValue)
|
|
{
|
|
AssertErrorState();
|
|
if ((expectingReturnType != NULL) && (!expectingReturnType->IsVoid()))
|
|
{
|
|
retValue = GetDefaultTypedValue(expectingReturnType, true);
|
|
}
|
|
else
|
|
{
|
|
EmitReturn(BfTypedValue());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (retValue.mType->IsVar())
|
|
{
|
|
EmitReturn(BfTypedValue());
|
|
}
|
|
else if (retValue.mType->IsVoid())
|
|
{
|
|
if (retType->IsVoid())
|
|
{
|
|
Warn(0, "Returning void value", returnStmt->mReturnToken);
|
|
EmitReturn(BfTypedValue());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (retType->IsVoid())
|
|
{
|
|
expectingReturnType = NULL;
|
|
Fail("Attempting to return value from void method", returnStmt->mExpression);
|
|
EmitReturn(BfTypedValue());
|
|
return;
|
|
}
|
|
|
|
if ((origType != NULL) && (origType->IsStructOrStructPtr()) && (retValue.mType->IsObjectOrInterface()))
|
|
{
|
|
Fail(StrFormat("Stack boxing of type '%s' is not allowed on return statements. Use 'new box' to box on the heap.", TypeToString(origType).c_str()), returnStmt->mExpression);
|
|
}
|
|
|
|
if (!alreadyWritten)
|
|
EmitReturn(LoadOrAggregateValue(retValue));
|
|
else
|
|
EmitReturn(BfTypedValue());
|
|
}
|
|
}
|
|
|
|
void BfModule::Visit(BfYieldStatement* yieldStmt)
|
|
{
|
|
Fail("Yield not supported", yieldStmt);
|
|
}
|
|
|
|
void BfModule::Visit(BfBreakStatement* breakStmt)
|
|
{
|
|
bool inMixinDecl = (mCurMethodInstance != NULL) && (mCurMethodInstance->IsMixin());
|
|
|
|
UpdateSrcPos(breakStmt);
|
|
EmitEnsureInstructionAt();
|
|
|
|
BfBreakData* breakData = mCurMethodState->mBreakData;
|
|
if (breakStmt->mLabel != NULL)
|
|
{
|
|
breakData = FindBreakData(breakStmt->mLabel);
|
|
}
|
|
else
|
|
{
|
|
while (breakData != NULL)
|
|
{
|
|
if (breakData->mIRBreakBlock)
|
|
break;
|
|
breakData = breakData->mPrevBreakData;
|
|
}
|
|
}
|
|
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
|
|
{
|
|
BfScopeData* scope = NULL;
|
|
if (breakData != NULL)
|
|
scope = breakData->mScope;
|
|
if (auto identifer = BfNodeDynCast<BfIdentifierNode>(breakStmt->mLabel))
|
|
mCompiler->mResolvePassData->mAutoComplete->CheckLabel(identifer, breakStmt->mBreakNode, scope);
|
|
}
|
|
|
|
if ((breakData == NULL) || (!breakData->mIRBreakBlock))
|
|
{
|
|
if (inMixinDecl)
|
|
{
|
|
// Our mixin may just require that we're injected into a breakable scope
|
|
}
|
|
else
|
|
Fail("'break' not applicable in this block", breakStmt);
|
|
return;
|
|
}
|
|
|
|
if (mCurMethodState->mInDeferredBlock)
|
|
{
|
|
auto checkScope = mCurMethodState->mCurScope;
|
|
while (checkScope != NULL)
|
|
{
|
|
if (checkScope == breakData->mScope)
|
|
break;
|
|
if (checkScope->mIsDeferredBlock)
|
|
{
|
|
Fail("The break target crosses a deferred block boundary", breakStmt);
|
|
return;
|
|
}
|
|
checkScope = checkScope->mPrevScope;
|
|
}
|
|
}
|
|
|
|
if (HasDeferredScopeCalls(breakData->mScope))
|
|
{
|
|
EmitDeferredScopeCalls(true, breakData->mScope, breakData->mIRBreakBlock);
|
|
}
|
|
else
|
|
{
|
|
mBfIRBuilder->CreateBr(breakData->mIRBreakBlock);
|
|
}
|
|
mCurMethodState->mLeftBlockUncond = true;
|
|
|
|
bool isCond = false;
|
|
int uncondScopeDepth = 0;
|
|
if (mCurMethodState->mCurScope != NULL)
|
|
uncondScopeDepth = mCurMethodState->mCurScope->mScopeDepth + 1;
|
|
|
|
BfIRValue earliestValueScopeStart;
|
|
auto checkScope = mCurMethodState->mCurScope;
|
|
while (checkScope != NULL)
|
|
{
|
|
if (!isCond)
|
|
uncondScopeDepth = checkScope->mScopeDepth;
|
|
if ((checkScope->mOuterIsConditional) && (!checkScope->mIsSharedTempBlock))
|
|
isCond = true;
|
|
|
|
if (checkScope->mValueScopeStart)
|
|
earliestValueScopeStart = checkScope->mValueScopeStart;
|
|
if (checkScope == breakData->mScope)
|
|
break;
|
|
|
|
checkScope = checkScope->mPrevScope;
|
|
}
|
|
|
|
auto checkLocalAssignData = mCurMethodState->mDeferredLocalAssignData;
|
|
while (checkLocalAssignData != NULL)
|
|
{
|
|
if ((checkLocalAssignData->mScopeData != NULL) && (checkLocalAssignData->mScopeData->mScopeDepth >= breakData->mScope->mScopeDepth))
|
|
{
|
|
if (checkLocalAssignData->mScopeData->mScopeDepth >= uncondScopeDepth)
|
|
checkLocalAssignData->mLeftBlockUncond = true;
|
|
checkLocalAssignData->mLeftBlock = true;
|
|
checkLocalAssignData->mHadBreak = true;
|
|
}
|
|
checkLocalAssignData = checkLocalAssignData->mChainedAssignData;
|
|
}
|
|
|
|
MarkScopeLeft(breakData->mScope);
|
|
ValueScopeEnd(earliestValueScopeStart);
|
|
|
|
auto checkBreakData = mCurMethodState->mBreakData;
|
|
while (true)
|
|
{
|
|
checkBreakData->mHadBreak = true;
|
|
if (checkBreakData == breakData)
|
|
break;
|
|
checkBreakData = checkBreakData->mPrevBreakData;
|
|
}
|
|
}
|
|
|
|
void BfModule::Visit(BfContinueStatement* continueStmt)
|
|
{
|
|
bool inMixinDecl = (mCurMethodInstance != NULL) && (mCurMethodInstance->IsMixin());
|
|
|
|
UpdateSrcPos(continueStmt);
|
|
EmitEnsureInstructionAt();
|
|
|
|
// If we're in a switch, 'break' is valid but we need to continue looking outward for a 'continue' target
|
|
BfBreakData* breakData = mCurMethodState->mBreakData;
|
|
if (continueStmt->mLabel != NULL)
|
|
{
|
|
breakData = FindBreakData(continueStmt->mLabel);
|
|
if ((breakData != NULL) && (!breakData->mIRContinueBlock))
|
|
{
|
|
Fail(StrFormat("'continue' not applicable in '%s'", continueStmt->mLabel->ToString().c_str()), continueStmt);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (breakData != NULL)
|
|
{
|
|
if (breakData->mIRContinueBlock)
|
|
break;
|
|
breakData = breakData->mPrevBreakData;
|
|
}
|
|
}
|
|
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
|
|
{
|
|
BfScopeData* scope = NULL;
|
|
if (breakData != NULL)
|
|
scope = breakData->mScope;
|
|
if (auto identifer = BfNodeDynCast<BfIdentifierNode>(continueStmt->mLabel))
|
|
mCompiler->mResolvePassData->mAutoComplete->CheckLabel(identifer, continueStmt->mContinueNode, scope);
|
|
}
|
|
|
|
if ((breakData == NULL) || (!breakData->mIRContinueBlock))
|
|
{
|
|
if (inMixinDecl)
|
|
{
|
|
// Our mixin may just require that we're injected into a breakable scope
|
|
}
|
|
else
|
|
Fail("'continue' not applicable in this block", continueStmt);
|
|
return;
|
|
}
|
|
|
|
BfIRValue earliestValueScopeStart;
|
|
|
|
// We don't want to close out our own scope, we want to close out any scopes that were opened after us
|
|
auto nextScope = mCurMethodState->mCurScope;
|
|
while (nextScope != NULL)
|
|
{
|
|
if (nextScope->mValueScopeStart)
|
|
earliestValueScopeStart = nextScope->mValueScopeStart;
|
|
if (nextScope->mPrevScope == breakData->mScope)
|
|
break;
|
|
nextScope = nextScope->mPrevScope;
|
|
}
|
|
|
|
if (breakData->mInnerValueScopeStart)
|
|
earliestValueScopeStart = breakData->mInnerValueScopeStart;
|
|
|
|
if (mCurMethodState->mInDeferredBlock)
|
|
{
|
|
auto checkScope = mCurMethodState->mCurScope;
|
|
while (checkScope != NULL)
|
|
{
|
|
if (checkScope == breakData->mScope)
|
|
break;
|
|
if (checkScope->mIsDeferredBlock)
|
|
{
|
|
Fail("The continue target crosses a deferred block boundary", continueStmt);
|
|
return;
|
|
}
|
|
checkScope = checkScope->mPrevScope;
|
|
}
|
|
}
|
|
|
|
if ((nextScope != NULL) && (HasDeferredScopeCalls(nextScope)))
|
|
{
|
|
EmitDeferredScopeCalls(true, nextScope, breakData->mIRContinueBlock);
|
|
}
|
|
else
|
|
{
|
|
mBfIRBuilder->CreateBr(breakData->mIRContinueBlock);
|
|
}
|
|
MarkScopeLeft(breakData->mScope);
|
|
ValueScopeEnd(earliestValueScopeStart);
|
|
|
|
mCurMethodState->mLeftBlockUncond = true;
|
|
if (!mCurMethodState->mInPostReturn)
|
|
mCurMethodState->mHadContinue = true;
|
|
}
|
|
|
|
void BfModule::Visit(BfFallthroughStatement* fallthroughStmt)
|
|
{
|
|
UpdateSrcPos(fallthroughStmt);
|
|
BfBreakData* breakData = mCurMethodState->mBreakData;
|
|
if (fallthroughStmt->mLabel != NULL)
|
|
{
|
|
breakData = FindBreakData(fallthroughStmt->mLabel);
|
|
}
|
|
else
|
|
{
|
|
while (breakData != NULL)
|
|
{
|
|
if (breakData->mIRFallthroughBlock)
|
|
break;
|
|
breakData = breakData->mPrevBreakData;
|
|
}
|
|
}
|
|
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
|
|
{
|
|
BfScopeData* scope = NULL;
|
|
if (breakData != NULL)
|
|
scope = breakData->mScope;
|
|
if (auto identifer = BfNodeDynCast<BfIdentifierNode>(fallthroughStmt->mLabel))
|
|
mCompiler->mResolvePassData->mAutoComplete->CheckLabel(identifer, fallthroughStmt->mFallthroughToken, scope);
|
|
}
|
|
|
|
if (mCurMethodState->mInDeferredBlock)
|
|
{
|
|
auto checkScope = mCurMethodState->mCurScope;
|
|
while (checkScope != NULL)
|
|
{
|
|
if (checkScope == breakData->mScope)
|
|
break;
|
|
if (checkScope->mIsDeferredBlock)
|
|
{
|
|
Fail("The fallthrough crosses a deferred block boundary", fallthroughStmt);
|
|
return;
|
|
}
|
|
checkScope = checkScope->mPrevScope;
|
|
}
|
|
}
|
|
|
|
if (breakData == NULL)
|
|
{
|
|
Fail("'fallthrough' not applicable in this block", fallthroughStmt);
|
|
return;
|
|
}
|
|
EmitDeferredScopeCalls(true, breakData->mScope, breakData->mIRFallthroughBlock);
|
|
mCurMethodState->mLeftBlockUncond = true; // Not really a return, but handled the same way
|
|
if (mCurMethodState->mDeferredLocalAssignData != NULL)
|
|
mCurMethodState->mDeferredLocalAssignData->mHadFallthrough = true;
|
|
|
|
auto checkBreakData = mCurMethodState->mBreakData;
|
|
while (true)
|
|
{
|
|
if (checkBreakData == breakData)
|
|
break;
|
|
checkBreakData->mHadBreak = true;
|
|
checkBreakData = checkBreakData->mPrevBreakData;
|
|
}
|
|
}
|
|
|
|
void BfModule::Visit(BfUsingStatement* usingStmt)
|
|
{
|
|
UpdateSrcPos(usingStmt);
|
|
|
|
mCurMethodState->mInHeadScope = false;
|
|
BfScopeData newScope;
|
|
mCurMethodState->AddScope(&newScope);
|
|
NewScopeState();
|
|
if (usingStmt->mVariableDeclaration != NULL)
|
|
UpdateSrcPos(usingStmt->mVariableDeclaration);
|
|
|
|
BfTypedValue embeddedValue;
|
|
SizedArray<BfIRValue, 1> llvmArgs;
|
|
BfModuleMethodInstance moduleMethodInstance;
|
|
|
|
BfFunctionBindResult functionBindResult;
|
|
BfExprEvaluator exprEvaluator(this);
|
|
|
|
bool failed = false;
|
|
|
|
if (usingStmt->mVariableDeclaration == NULL)
|
|
{
|
|
AssertErrorState();
|
|
failed = true;
|
|
}
|
|
else if (usingStmt->mVariableDeclaration->mNameNode != NULL)
|
|
{
|
|
BfLocalVariable* localVar = HandleVariableDeclaration(usingStmt->mVariableDeclaration);
|
|
if (localVar == NULL)
|
|
{
|
|
AssertErrorState();
|
|
failed = true;
|
|
}
|
|
else
|
|
{
|
|
embeddedValue = exprEvaluator.LoadLocal(localVar);
|
|
}
|
|
//exprEvaluator.CheckModifyResult(embeddedValue, usingStmt->mVariableDeclaration->mNameNode,);
|
|
}
|
|
else
|
|
{
|
|
embeddedValue = CreateValueFromExpression(usingStmt->mVariableDeclaration->mInitializer);
|
|
if (!embeddedValue)
|
|
failed = true;
|
|
}
|
|
|
|
if (!failed)
|
|
{
|
|
auto iDisposableType = ResolveTypeDef(mCompiler->mIDisposableTypeDef)->ToTypeInstance();
|
|
auto dispMethod = GetMethodByName(iDisposableType, "Dispose");
|
|
|
|
if ((!dispMethod) || (!CanCast(embeddedValue, iDisposableType)))
|
|
{
|
|
Fail(StrFormat("Type '%s' must be implicitly convertible to 'System.IDisposable' for use in 'using' statement", TypeToString(embeddedValue.mType).c_str()), usingStmt->mVariableDeclaration);
|
|
failed = true;
|
|
}
|
|
else
|
|
{
|
|
bool mayBeNull = true;
|
|
if (embeddedValue.mType->IsStruct())
|
|
{
|
|
// It's possible that a struct can convert to an IDisposable through a conversion operator that CAN
|
|
// return null, so the only way we can know we are not null is if we are a struct that directly
|
|
// implements the interface
|
|
if (TypeIsSubTypeOf(embeddedValue.mType->ToTypeInstance(), iDisposableType))
|
|
mayBeNull = false;
|
|
}
|
|
|
|
exprEvaluator.mFunctionBindResult = &functionBindResult;
|
|
SizedArray<BfResolvedArg, 0> resolvedArgs;
|
|
BfMethodMatcher methodMatcher(usingStmt->mVariableDeclaration, this, dispMethod.mMethodInstance, resolvedArgs, BfMethodGenericArguments());
|
|
methodMatcher.CheckType(iDisposableType, embeddedValue, false);
|
|
methodMatcher.TryDevirtualizeCall(embeddedValue);
|
|
auto retVal = exprEvaluator.CreateCall(&methodMatcher, embeddedValue);
|
|
if (functionBindResult.mMethodInstance != NULL)
|
|
{
|
|
moduleMethodInstance = BfModuleMethodInstance(functionBindResult.mMethodInstance, functionBindResult.mFunc);
|
|
AddDeferredCall(moduleMethodInstance, functionBindResult.mIRArgs, mCurMethodState->mCurScope, NULL, false, mayBeNull);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (usingStmt->mEmbeddedStatement == NULL)
|
|
{
|
|
AssertErrorState();
|
|
}
|
|
else
|
|
{
|
|
VisitEmbeddedStatement(usingStmt->mEmbeddedStatement);
|
|
}
|
|
|
|
RestoreScopeState();
|
|
}
|
|
|
|
void BfModule::Visit(BfDoStatement* doStmt)
|
|
{
|
|
UpdateSrcPos(doStmt);
|
|
|
|
auto bodyBB = mBfIRBuilder->CreateBlock("do.body", true);
|
|
auto endBB = mBfIRBuilder->CreateBlock("do.end");
|
|
|
|
BfScopeData scopeData;
|
|
if (doStmt->mLabelNode != NULL)
|
|
scopeData.mLabelNode = doStmt->mLabelNode->mLabel;
|
|
mCurMethodState->AddScope(&scopeData);
|
|
NewScopeState();
|
|
|
|
BfBreakData breakData;
|
|
breakData.mIRBreakBlock = endBB;
|
|
breakData.mScope = &scopeData;
|
|
breakData.mPrevBreakData = mCurMethodState->mBreakData;
|
|
SetAndRestoreValue<BfBreakData*> prevBreakData(mCurMethodState->mBreakData, &breakData);
|
|
|
|
BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope);
|
|
deferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData, false);
|
|
deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
|
|
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &deferredLocalAssignData);
|
|
|
|
// We may have a call in the loop body
|
|
mCurMethodState->mMayNeedThisAccessCheck = true;
|
|
|
|
mBfIRBuilder->CreateBr(bodyBB);
|
|
mBfIRBuilder->SetInsertPoint(bodyBB);
|
|
VisitEmbeddedStatement(doStmt->mEmbeddedStatement);
|
|
|
|
prevDLA.Restore();
|
|
mCurMethodState->ApplyDeferredLocalAssignData(deferredLocalAssignData);
|
|
|
|
RestoreScopeState();
|
|
|
|
if (!mCurMethodState->mLeftBlockUncond)
|
|
mBfIRBuilder->CreateBr(endBB);
|
|
mCurMethodState->SetHadReturn(false);
|
|
mCurMethodState->mLeftBlockUncond = false;
|
|
|
|
mBfIRBuilder->AddBlock(endBB);
|
|
mBfIRBuilder->SetInsertPoint(endBB);
|
|
}
|
|
|
|
void BfModule::Visit(BfRepeatStatement* repeatStmt)
|
|
{
|
|
// if (repeatStmt->mCondition != NULL)
|
|
// UpdateSrcPos(repeatStmt->mCondition);
|
|
// else
|
|
UpdateSrcPos(repeatStmt);
|
|
|
|
if (repeatStmt->mRepeatToken->mToken == BfToken_Do)
|
|
{
|
|
Fail("Repeat block requires 'repeat' token", repeatStmt->mRepeatToken);
|
|
}
|
|
|
|
auto bodyBB = mBfIRBuilder->CreateBlock("repeat.body", true);
|
|
auto condBB = mBfIRBuilder->CreateBlock("repeat.cond");
|
|
auto endBB = mBfIRBuilder->CreateBlock("repeat.end");
|
|
|
|
BfScopeData scopeData;
|
|
// We set mIsLoop later
|
|
if (repeatStmt->mLabelNode != NULL)
|
|
scopeData.mLabelNode = repeatStmt->mLabelNode->mLabel;
|
|
mCurMethodState->AddScope(&scopeData);
|
|
NewScopeState();
|
|
|
|
BfBreakData breakData;
|
|
breakData.mIRContinueBlock = condBB;
|
|
breakData.mIRBreakBlock = endBB;
|
|
breakData.mScope = &scopeData;
|
|
breakData.mPrevBreakData = mCurMethodState->mBreakData;
|
|
SetAndRestoreValue<BfBreakData*> prevBreakData(mCurMethodState->mBreakData, &breakData);
|
|
|
|
// We may have a call in the loop body
|
|
mCurMethodState->mMayNeedThisAccessCheck = true;
|
|
|
|
mBfIRBuilder->CreateBr(bodyBB);
|
|
mBfIRBuilder->SetInsertPoint(bodyBB);
|
|
scopeData.mIsLoop = true;
|
|
|
|
BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope);
|
|
deferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData, false);
|
|
deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
|
|
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &deferredLocalAssignData);
|
|
|
|
VisitEmbeddedStatement(repeatStmt->mEmbeddedStatement);
|
|
|
|
if (!mCurMethodState->mLeftBlockUncond)
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
mCurMethodState->SetHadReturn(false);
|
|
mCurMethodState->mLeftBlockUncond = false;
|
|
mCurMethodState->mLeftBlockCond = false;
|
|
|
|
mBfIRBuilder->AddBlock(condBB);
|
|
mBfIRBuilder->SetInsertPoint(condBB);
|
|
|
|
bool isInfiniteLoop = false;
|
|
|
|
if (repeatStmt->mCondition != NULL)
|
|
{
|
|
UpdateSrcPos(repeatStmt->mCondition);
|
|
auto checkVal = CreateValueFromExpression(repeatStmt->mCondition, GetPrimitiveType(BfTypeCode_Boolean));
|
|
if (checkVal)
|
|
{
|
|
if ((!breakData.mHadBreak) && (checkVal.mValue.IsConst()))
|
|
{
|
|
auto constVal = mBfIRBuilder->GetConstantById(checkVal.mValue.mId);
|
|
if (constVal->mTypeCode == BfTypeCode_Boolean)
|
|
isInfiniteLoop = constVal->mBool;
|
|
}
|
|
|
|
mBfIRBuilder->CreateCondBr(checkVal.mValue, bodyBB, endBB);
|
|
mBfIRBuilder->AddBlock(endBB);
|
|
mBfIRBuilder->SetInsertPoint(endBB);
|
|
}
|
|
}
|
|
|
|
RestoreScopeState();
|
|
|
|
prevDLA.Restore();
|
|
mCurMethodState->ApplyDeferredLocalAssignData(deferredLocalAssignData);
|
|
|
|
if (isInfiniteLoop)
|
|
EmitDefaultReturn();
|
|
}
|
|
|
|
void BfModule::Visit(BfWhileStatement* whileStmt)
|
|
{
|
|
UpdateSrcPos(whileStmt);
|
|
|
|
bool prevHadReturn = mCurMethodState->mHadReturn;
|
|
|
|
auto condBB = mBfIRBuilder->CreateBlock("while.cond");
|
|
auto bodyBB = mBfIRBuilder->CreateBlock("while.body");
|
|
auto endBB = mBfIRBuilder->CreateBlock("while.end");
|
|
|
|
mCurMethodState->mInHeadScope = false;
|
|
BfScopeData scopeData;
|
|
scopeData.mScopeKind = BfScopeKind_StatementTarget_Conditional;
|
|
scopeData.mIsLoop = true;
|
|
if (whileStmt->mLabelNode != NULL)
|
|
scopeData.mLabelNode = whileStmt->mLabelNode->mLabel;
|
|
scopeData.mValueScopeStart = ValueScopeStart();
|
|
mCurMethodState->AddScope(&scopeData);
|
|
NewScopeState();
|
|
|
|
BfBreakData breakData;
|
|
breakData.mIRContinueBlock = condBB;
|
|
breakData.mIRBreakBlock = endBB;
|
|
breakData.mScope = &scopeData;
|
|
breakData.mPrevBreakData = mCurMethodState->mBreakData;
|
|
breakData.mInnerValueScopeStart = scopeData.mValueScopeStart;
|
|
SetAndRestoreValue<BfBreakData*> prevBreakData(mCurMethodState->mBreakData, &breakData);
|
|
|
|
mBfIRBuilder->AddBlock(condBB);
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
mBfIRBuilder->SetInsertPoint(condBB);
|
|
|
|
BfTypedValue checkVal;
|
|
if (whileStmt->mCondition != NULL)
|
|
{
|
|
UpdateSrcPos(whileStmt->mCondition);
|
|
checkVal = CreateValueFromExpression(whileStmt->mCondition, GetPrimitiveType(BfTypeCode_Boolean));
|
|
}
|
|
|
|
if (!checkVal)
|
|
{
|
|
AssertErrorState();
|
|
checkVal = GetDefaultTypedValue(GetPrimitiveType(BfTypeCode_Boolean));
|
|
}
|
|
|
|
bool isInfiniteLoop = false;
|
|
bool isFalseLoop = false;
|
|
|
|
if (checkVal.mValue.IsConst())
|
|
{
|
|
EmitEnsureInstructionAt();
|
|
|
|
auto constVal = mBfIRBuilder->GetConstantById(checkVal.mValue.mId);
|
|
if (constVal->mTypeCode == BfTypeCode_Boolean)
|
|
{
|
|
isInfiniteLoop = constVal->mBool;
|
|
isFalseLoop = !isInfiniteLoop;
|
|
}
|
|
}
|
|
|
|
// We may have a call in the loop body
|
|
mCurMethodState->mMayNeedThisAccessCheck = true;
|
|
|
|
// For BeefBackend we continue to do CondBr because it helps our flow analysis and we optimize it anyway
|
|
if ((isInfiniteLoop) && (!IsTargetingBeefBackend()))
|
|
mBfIRBuilder->CreateBr(bodyBB);
|
|
else if (isFalseLoop)
|
|
mBfIRBuilder->CreateBr(endBB);
|
|
else
|
|
mBfIRBuilder->CreateCondBr(checkVal.mValue, bodyBB, endBB);
|
|
|
|
// Apply deferred local assign to mEmbeddedStatement and itrStmt
|
|
BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope);
|
|
deferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData, false);
|
|
deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
|
|
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &deferredLocalAssignData);
|
|
deferredLocalAssignData.mIsUnconditional = isInfiniteLoop;
|
|
|
|
mBfIRBuilder->AddBlock(bodyBB);
|
|
mBfIRBuilder->SetInsertPoint(bodyBB);
|
|
if (whileStmt->mEmbeddedStatement != NULL)
|
|
{
|
|
if (isFalseLoop)
|
|
{
|
|
SetAndRestoreValue<bool> ignoreWrites(mBfIRBuilder->mIgnoreWrites, true);
|
|
SetAndRestoreValue<bool> prevInConstIgnore(mCurMethodState->mCurScope->mInConstIgnore, true);
|
|
VisitEmbeddedStatement(whileStmt->mEmbeddedStatement);
|
|
}
|
|
else
|
|
VisitEmbeddedStatement(whileStmt->mEmbeddedStatement);
|
|
}
|
|
else
|
|
{
|
|
AssertErrorState();
|
|
}
|
|
if (breakData.mHadBreak)
|
|
{
|
|
isInfiniteLoop = false;
|
|
}
|
|
|
|
if ((!mCurMethodState->mLeftBlockUncond) && (!isFalseLoop))
|
|
{
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
}
|
|
|
|
if (!isInfiniteLoop)
|
|
mCurMethodState->mHadReturn = prevHadReturn;
|
|
|
|
mCurMethodState->mLeftBlockUncond = false;
|
|
mCurMethodState->mLeftBlockCond = false;
|
|
|
|
mBfIRBuilder->AddBlock(endBB);
|
|
mBfIRBuilder->SetInsertPoint(endBB);
|
|
|
|
if (isFalseLoop)
|
|
{
|
|
mBfIRBuilder->EraseFromParent(bodyBB);
|
|
}
|
|
|
|
RestoreScopeState();
|
|
|
|
if ((isInfiniteLoop) && (mCurMethodInstance != NULL))
|
|
EmitDefaultReturn();
|
|
}
|
|
|
|
void BfModule::Visit(BfForStatement* forStmt)
|
|
{
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
if (autoComplete != NULL)
|
|
autoComplete->CheckIdentifier(forStmt->mForToken, true);
|
|
|
|
UpdateSrcPos(forStmt);
|
|
|
|
auto startBB = mBfIRBuilder->CreateBlock("for.start", true);
|
|
mBfIRBuilder->CreateBr(startBB);
|
|
mBfIRBuilder->SetInsertPoint(startBB);
|
|
|
|
BfScopeData scopeData;
|
|
scopeData.mIsLoop = true;
|
|
if (forStmt->mLabelNode != NULL)
|
|
scopeData.mLabelNode = forStmt->mLabelNode->mLabel;
|
|
scopeData.mCloseNode = forStmt;
|
|
scopeData.mValueScopeStart = ValueScopeStart();
|
|
mCurMethodState->AddScope(&scopeData);
|
|
NewScopeState();
|
|
|
|
for (auto initializer : forStmt->mInitializers)
|
|
{
|
|
VisitChild(initializer);
|
|
}
|
|
|
|
// We may have a call in the loop body
|
|
mCurMethodState->mMayNeedThisAccessCheck = true;
|
|
|
|
auto boolType = GetPrimitiveType(BfTypeCode_Boolean);
|
|
|
|
auto condBB = mBfIRBuilder->CreateBlock("for.cond", true);
|
|
auto bodyBB = mBfIRBuilder->CreateBlock("for.body");
|
|
auto incBB = mBfIRBuilder->CreateBlock("for.inc");
|
|
auto endBB = mBfIRBuilder->CreateBlock("for.end");
|
|
|
|
BfBreakData breakData;
|
|
breakData.mIRContinueBlock = incBB;
|
|
breakData.mIRBreakBlock = endBB;
|
|
breakData.mScope = &scopeData;
|
|
breakData.mPrevBreakData = mCurMethodState->mBreakData;
|
|
SetAndRestoreValue<BfBreakData*> prevBreakData(mCurMethodState->mBreakData, &breakData);
|
|
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
|
|
bool isInfiniteLoop = false;
|
|
mBfIRBuilder->SetInsertPoint(condBB);
|
|
if (forStmt->mCondition != NULL)
|
|
{
|
|
auto conditionValue = CreateValueFromExpression(forStmt->mCondition, boolType);
|
|
if (!conditionValue)
|
|
conditionValue = GetDefaultTypedValue(boolType);
|
|
auto constant = mBfIRBuilder->GetConstant(conditionValue.mValue);
|
|
if ((constant != NULL) && (constant->mTypeCode == BfTypeCode_Boolean))
|
|
isInfiniteLoop = constant->mBool;
|
|
ValueScopeEnd(scopeData.mValueScopeStart);
|
|
mBfIRBuilder->CreateCondBr(conditionValue.mValue, bodyBB, endBB);
|
|
}
|
|
else
|
|
{
|
|
isInfiniteLoop = true;
|
|
mBfIRBuilder->CreateBr(bodyBB);
|
|
}
|
|
|
|
// Apply deferred local assign to mEmbeddedStatement and itrStmt
|
|
BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope);
|
|
deferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData, false);
|
|
deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
|
|
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &deferredLocalAssignData);
|
|
|
|
mBfIRBuilder->AddBlock(bodyBB);
|
|
mBfIRBuilder->SetInsertPoint(bodyBB);
|
|
if (forStmt->mEmbeddedStatement != NULL)
|
|
{
|
|
VisitEmbeddedStatement(forStmt->mEmbeddedStatement);
|
|
}
|
|
if (!mCurMethodState->mLeftBlockUncond)
|
|
mBfIRBuilder->CreateBr(incBB);
|
|
|
|
mBfIRBuilder->AddBlock(incBB);
|
|
mBfIRBuilder->SetInsertPoint(incBB);
|
|
for (auto itrStmt : forStmt->mIterators)
|
|
{
|
|
VisitChild(itrStmt);
|
|
if ((mCurMethodState->mLeftBlockUncond) && (!mCurMethodState->mHadContinue) && (!mCurMethodState->mInPostReturn))
|
|
Warn(BfWarning_CS0162_UnreachableCode, "Unreachable code", itrStmt);
|
|
}
|
|
ValueScopeEnd(scopeData.mValueScopeStart);
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
|
|
mBfIRBuilder->AddBlock(endBB);
|
|
mBfIRBuilder->SetInsertPoint(endBB);
|
|
|
|
// The 'return' may have been inside the block, which may not have been entered if preconditions were not met
|
|
mCurMethodState->SetHadReturn(false);
|
|
mCurMethodState->mLeftBlockUncond = false;
|
|
mCurMethodState->mLeftBlockCond = false;
|
|
|
|
if (breakData.mHadBreak)
|
|
isInfiniteLoop = false;
|
|
|
|
RestoreScopeState();
|
|
|
|
if (isInfiniteLoop)
|
|
EmitDefaultReturn();
|
|
}
|
|
|
|
void BfModule::DoForLess(BfForEachStatement* forEachStmt)
|
|
{
|
|
UpdateSrcPos(forEachStmt);
|
|
|
|
auto startBB = mBfIRBuilder->GetInsertBlock();
|
|
auto condBB = mBfIRBuilder->CreateBlock("forless.cond", true);
|
|
mBfIRBuilder->SetInsertPoint(condBB);
|
|
|
|
BfScopeData scopeData;
|
|
// We set mIsLoop later
|
|
if (forEachStmt->mLabelNode != NULL)
|
|
scopeData.mLabelNode = forEachStmt->mLabelNode->mLabel;
|
|
mCurMethodState->AddScope(&scopeData);
|
|
NewScopeState();
|
|
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
|
|
auto isLet = BfNodeDynCast<BfLetTypeReference>(forEachStmt->mVariableTypeRef) != 0;
|
|
auto isVar = BfNodeDynCast<BfVarTypeReference>(forEachStmt->mVariableTypeRef) != 0;
|
|
|
|
BfTypedValue target;
|
|
BfType* varType = NULL;
|
|
bool didInference = false;
|
|
if (isLet || isVar)
|
|
{
|
|
if (forEachStmt->mCollectionExpression != NULL)
|
|
target = CreateValueFromExpression(forEachStmt->mCollectionExpression);
|
|
if (target)
|
|
{
|
|
FixIntUnknown(target);
|
|
varType = target.mType;
|
|
}
|
|
|
|
if (autoComplete != NULL)
|
|
autoComplete->CheckVarResolution(forEachStmt->mVariableTypeRef, varType);
|
|
didInference = true;
|
|
}
|
|
else
|
|
{
|
|
varType = ResolveTypeRef(forEachStmt->mVariableTypeRef, BfPopulateType_Data, BfResolveTypeRefFlag_AllowRef);
|
|
if (forEachStmt->mCollectionExpression != NULL)
|
|
target = CreateValueFromExpression(forEachStmt->mCollectionExpression, varType);
|
|
}
|
|
if (varType == NULL)
|
|
varType = GetPrimitiveType(BfTypeCode_IntPtr);
|
|
|
|
BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope);
|
|
deferredLocalAssignData.mIsIfCondition = true;
|
|
deferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData, true);
|
|
deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
|
|
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &deferredLocalAssignData);
|
|
|
|
deferredLocalAssignData.mIsIfCondition = false;
|
|
|
|
// The "extend chain" is only valid for the conditional -- since that expression may contain unconditionally executed and
|
|
// conditionally executed code (in the case of "(GetVal(out a) && GetVal(out b))" for example
|
|
mCurMethodState->mDeferredLocalAssignData->BreakExtendChain();
|
|
|
|
BfType* checkType = varType;
|
|
if (checkType->IsTypedPrimitive())
|
|
checkType = checkType->GetUnderlyingType();
|
|
if (!checkType->IsIntegral())
|
|
{
|
|
Fail(StrFormat("Cannot iterate over '%s' in for-less statements, only integer types are allowed", TypeToString(varType).c_str()), forEachStmt->mVariableTypeRef);
|
|
varType = GetPrimitiveType(BfTypeCode_IntPtr);
|
|
if (didInference)
|
|
target = GetDefaultTypedValue(varType);
|
|
}
|
|
PopulateType(varType, BfPopulateType_Data);
|
|
|
|
auto condEndBB = mBfIRBuilder->GetInsertBlock();
|
|
mBfIRBuilder->SetInsertPoint(startBB);
|
|
|
|
BfLocalVariable* localDef = new BfLocalVariable();
|
|
localDef->mNameNode = BfNodeDynCast<BfIdentifierNode>(forEachStmt->mVariableName);
|
|
localDef->mName = localDef->mNameNode->ToString();
|
|
localDef->mResolvedType = varType;
|
|
BfIRValue varInst;
|
|
if (!varType->IsValuelessType())
|
|
{
|
|
varInst = CreateAlloca(varType);
|
|
}
|
|
localDef->mAddr = varInst;
|
|
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
localDef->mReadFromId = 0;
|
|
localDef->mIsReadOnly = isLet || (forEachStmt->mReadOnlyToken != NULL);
|
|
|
|
CheckVariableDef(localDef);
|
|
|
|
mBfIRBuilder->CreateAlignedStore(GetDefaultValue(varType), localDef->mAddr, varType->mAlign);
|
|
|
|
localDef->Init();
|
|
UpdateExprSrcPos(forEachStmt->mVariableName);
|
|
AddLocalVariableDef(localDef, true);
|
|
|
|
auto bodyBB = mBfIRBuilder->CreateBlock("forless.body");
|
|
auto incBB = mBfIRBuilder->CreateBlock("forless.inc");
|
|
auto endBB = mBfIRBuilder->CreateBlock("forless.end");
|
|
|
|
BfBreakData breakData;
|
|
breakData.mIRContinueBlock = incBB;
|
|
breakData.mIRBreakBlock = endBB;
|
|
breakData.mScope = &scopeData;
|
|
breakData.mPrevBreakData = mCurMethodState->mBreakData;
|
|
|
|
SetAndRestoreValue<BfBreakData*> prevBreakData(mCurMethodState->mBreakData, &breakData);
|
|
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
mBfIRBuilder->SetInsertPoint(condEndBB);
|
|
if (forEachStmt->mCollectionExpression != NULL)
|
|
UpdateExprSrcPos(forEachStmt->mCollectionExpression);
|
|
BfIRValue conditionValue;
|
|
|
|
// We may have a call in the loop body
|
|
mCurMethodState->mMayNeedThisAccessCheck = true;
|
|
|
|
// Cond
|
|
auto valueScopeStart = ValueScopeStart();
|
|
auto localVal = mBfIRBuilder->CreateAlignedLoad(localDef->mAddr, localDef->mResolvedType->mAlign);
|
|
if (!target)
|
|
{
|
|
// Soldier on
|
|
target = GetDefaultTypedValue(varType);
|
|
}
|
|
if (forEachStmt->mInToken->mToken == BfToken_LessEquals)
|
|
conditionValue = mBfIRBuilder->CreateCmpLTE(localVal, target.mValue, varType->IsSigned());
|
|
else
|
|
conditionValue = mBfIRBuilder->CreateCmpLT(localVal, target.mValue, varType->IsSigned());
|
|
mBfIRBuilder->CreateCondBr(conditionValue, bodyBB, endBB);
|
|
ValueScopeEnd(valueScopeStart);
|
|
|
|
mBfIRBuilder->AddBlock(bodyBB);
|
|
mBfIRBuilder->SetInsertPoint(bodyBB);
|
|
|
|
// Body
|
|
scopeData.mIsLoop = true;
|
|
if (forEachStmt->mEmbeddedStatement != NULL)
|
|
{
|
|
VisitEmbeddedStatement(forEachStmt->mEmbeddedStatement);
|
|
}
|
|
|
|
// Inc
|
|
|
|
if (!mCurMethodState->mLeftBlockUncond)
|
|
{
|
|
mBfIRBuilder->CreateBr(incBB);
|
|
}
|
|
|
|
mBfIRBuilder->AddBlock(incBB);
|
|
mBfIRBuilder->SetInsertPoint(incBB);
|
|
if (forEachStmt->mCollectionExpression != NULL)
|
|
UpdateExprSrcPos(forEachStmt->mCollectionExpression);
|
|
|
|
auto one = GetConstValue(1, localDef->mResolvedType);
|
|
// We have to reload localVal before the inc, user logic could have changed it
|
|
localVal = mBfIRBuilder->CreateAlignedLoad(localDef->mAddr, localDef->mResolvedType->mAlign);
|
|
auto result = mBfIRBuilder->CreateAdd(localVal, one);
|
|
mBfIRBuilder->CreateAlignedStore(result, localDef->mAddr, localDef->mResolvedType->mAlign);
|
|
ValueScopeEnd(valueScopeStart);
|
|
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
|
|
mBfIRBuilder->AddBlock(endBB);
|
|
mBfIRBuilder->SetInsertPoint(endBB);
|
|
|
|
// The 'return' may have been inside the block, which may not have been entered if preconditions were not met
|
|
mCurMethodState->SetHadReturn(false);
|
|
mCurMethodState->mLeftBlockUncond = false;
|
|
mCurMethodState->mLeftBlockCond = false;
|
|
|
|
bool definitelyExecuted = false;
|
|
if (target.mValue.IsConst())
|
|
{
|
|
auto constant = mBfIRBuilder->GetConstant(target.mValue);
|
|
if (constant->mInt32 > 0)
|
|
definitelyExecuted = true;
|
|
}
|
|
|
|
prevDLA.Restore();
|
|
if (definitelyExecuted)
|
|
mCurMethodState->ApplyDeferredLocalAssignData(deferredLocalAssignData);
|
|
|
|
RestoreScopeState();
|
|
}
|
|
|
|
void BfModule::Visit(BfForEachStatement* forEachStmt)
|
|
{
|
|
if ((forEachStmt->mInToken != NULL) &&
|
|
((forEachStmt->mInToken->GetToken() == BfToken_LChevron) || (forEachStmt->mInToken->GetToken() == BfToken_LessEquals)))
|
|
{
|
|
DoForLess(forEachStmt);
|
|
return;
|
|
}
|
|
|
|
auto autoComplete = mCompiler->GetAutoComplete();
|
|
UpdateSrcPos(forEachStmt);
|
|
|
|
BfScopeData scopeData;
|
|
// We set mIsLoop after the non-looped initializations
|
|
scopeData.mValueScopeStart = ValueScopeStart();
|
|
mCurMethodState->AddScope(&scopeData);
|
|
NewScopeState();
|
|
|
|
bool isRefExpression = false;
|
|
BfExpression* collectionExpr = forEachStmt->mCollectionExpression;
|
|
if (auto unaryOpExpr = BfNodeDynCast<BfUnaryOperatorExpression>(collectionExpr))
|
|
{
|
|
if ((unaryOpExpr->mOp == BfUnaryOp_Ref) || (unaryOpExpr->mOp == BfUnaryOp_Mut))
|
|
{
|
|
isRefExpression = true;
|
|
collectionExpr = unaryOpExpr->mExpression;
|
|
}
|
|
}
|
|
|
|
BfTypedValue target;
|
|
if (collectionExpr != NULL)
|
|
target = CreateValueFromExpression(collectionExpr);
|
|
if (!target)
|
|
{
|
|
// Soldier on
|
|
target = BfTypedValue(GetDefaultValue(mContext->mBfObjectType), mContext->mBfObjectType);
|
|
}
|
|
|
|
bool isLet = (forEachStmt->mVariableTypeRef != NULL) && (forEachStmt->mVariableTypeRef->IsA<BfLetTypeReference>());
|
|
|
|
BfType* varType = NULL;
|
|
bool inferVarType = false;
|
|
if ((forEachStmt->mVariableTypeRef == NULL) || (forEachStmt->mVariableTypeRef->IsA<BfVarTypeReference>()) || (isLet))
|
|
{
|
|
if (target.mType->IsSizedArray())
|
|
{
|
|
varType = target.mType->GetUnderlyingType();
|
|
if (isRefExpression)
|
|
varType = CreateRefType(varType);
|
|
}
|
|
else if (target.mType->IsArray())
|
|
{
|
|
varType = target.mType->GetUnderlyingType();
|
|
if (isRefExpression)
|
|
varType = CreateRefType(varType);
|
|
}
|
|
else
|
|
inferVarType = true;
|
|
}
|
|
else
|
|
{
|
|
if (autoComplete != NULL)
|
|
autoComplete->CheckTypeRef(forEachStmt->mVariableTypeRef, false);
|
|
varType = ResolveTypeRef(forEachStmt->mVariableTypeRef, BfPopulateType_Data, BfResolveTypeRefFlag_AllowRef);
|
|
}
|
|
|
|
if (varType == NULL)
|
|
varType = GetPrimitiveType(BfTypeCode_Var);
|
|
bool isArray = target.mType->IsArray();
|
|
bool isSizedArray = target.mType->IsSizedArray();
|
|
bool isVarEnumerator = target.mType->IsVar();
|
|
|
|
// Array
|
|
BfType* itrType;
|
|
BfTypedValue itr;
|
|
BfTypeInstance* itrInterface = NULL;
|
|
BfTypeInstance* refItrInterface = NULL;
|
|
|
|
if (isVarEnumerator)
|
|
varType = GetPrimitiveType(BfTypeCode_Var);
|
|
|
|
BfGenericParamInstance* genericParamInst = NULL;
|
|
|
|
if (target.mType->IsGenericParam())
|
|
{
|
|
genericParamInst = GetGenericParamInstance((BfGenericParamType*)target.mType);
|
|
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0)
|
|
{
|
|
varType = GetPrimitiveType(BfTypeCode_Var);
|
|
isVarEnumerator = true;
|
|
}
|
|
|
|
if (genericParamInst->mTypeConstraint != NULL)
|
|
{
|
|
if (genericParamInst->mTypeConstraint->IsVar())
|
|
{
|
|
varType = GetPrimitiveType(BfTypeCode_Var);
|
|
isVarEnumerator = true;
|
|
}
|
|
|
|
if (genericParamInst->mTypeConstraint->IsGenericTypeInstance())
|
|
{
|
|
auto genericConstraintType = (BfTypeInstance*)genericParamInst->mTypeConstraint;
|
|
if (genericConstraintType->IsInstanceOf(mCompiler->mSizedArrayTypeDef))
|
|
{
|
|
varType = genericConstraintType->mGenericTypeInfo->mTypeGenericArguments[0];
|
|
isVarEnumerator = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (target.mType->IsConcreteInterfaceType())
|
|
target.mType = target.mType->GetUnderlyingType();
|
|
|
|
if (isArray || isSizedArray)
|
|
{
|
|
itrType = GetPrimitiveType(BfTypeCode_IntPtr);
|
|
BfIRValue itrInst = CreateAlloca(itrType);
|
|
itr = BfTypedValue(itrInst, itrType, true);
|
|
}
|
|
else if (isVarEnumerator)
|
|
{
|
|
// Generic method or mixin decl
|
|
}
|
|
else if ((!target.mType->IsTypeInstance()) && (genericParamInst == NULL))
|
|
{
|
|
Fail(StrFormat("Type '%s' cannot be used in enumeration", TypeToString(target.mType).c_str()), forEachStmt->mCollectionExpression);
|
|
}
|
|
else if (forEachStmt->mCollectionExpression != NULL)
|
|
{
|
|
auto targetTypeInstance = target.mType->ToTypeInstance();
|
|
|
|
itr = target;
|
|
bool hadGetEnumeratorType = false;
|
|
|
|
if (genericParamInst != NULL)
|
|
{
|
|
for (auto ifaceConstraint : genericParamInst->mInterfaceConstraints)
|
|
{
|
|
if (ifaceConstraint->IsInstanceOf(mCompiler->mGenericIEnumerableTypeDef))
|
|
{
|
|
if (targetTypeInstance != NULL)
|
|
{
|
|
targetTypeInstance = NULL;
|
|
break;
|
|
}
|
|
targetTypeInstance = ifaceConstraint->ToTypeInstance();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (targetTypeInstance != NULL)
|
|
{
|
|
PopulateType(targetTypeInstance, BfPopulateType_DataAndMethods);
|
|
|
|
auto getEnumeratorMethod = GetMethodByName(targetTypeInstance, "GetEnumerator", 0, true);
|
|
if (!getEnumeratorMethod)
|
|
{
|
|
hadGetEnumeratorType = false;
|
|
}
|
|
else if (getEnumeratorMethod.mMethodInstance->mMethodDef->mIsStatic)
|
|
{
|
|
hadGetEnumeratorType = true;
|
|
Fail(StrFormat("Type '%s' does not contain a non-static 'GetEnumerator' method", TypeToString(targetTypeInstance).c_str()), forEachStmt->mCollectionExpression);
|
|
}
|
|
else
|
|
{
|
|
if (getEnumeratorMethod.mMethodInstance->mMethodDef->mIsConcrete)
|
|
{
|
|
hadGetEnumeratorType = true;
|
|
if (genericParamInst != NULL)
|
|
{
|
|
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Concrete) == 0)
|
|
Fail(StrFormat("Iteration requires a concrete implementation of '%s', consider adding 'concrete' constraint to '%s'", TypeToString(targetTypeInstance).c_str(), genericParamInst->GetName().c_str()), forEachStmt->mCollectionExpression);
|
|
}
|
|
else
|
|
Fail(StrFormat("Iteration requires a concrete implementation of '%s'", TypeToString(targetTypeInstance).c_str()), forEachStmt->mCollectionExpression);
|
|
}
|
|
|
|
hadGetEnumeratorType = true;
|
|
BfExprEvaluator exprEvaluator(this);
|
|
SizedArray<BfIRValue, 1> args;
|
|
auto castedTarget = Cast(forEachStmt->mCollectionExpression, target, getEnumeratorMethod.mMethodInstance->GetOwner());
|
|
exprEvaluator.PushThis(forEachStmt->mCollectionExpression, castedTarget, getEnumeratorMethod.mMethodInstance, args);
|
|
itr = exprEvaluator.CreateCall(forEachStmt->mCollectionExpression, getEnumeratorMethod.mMethodInstance, IsSkippingExtraResolveChecks() ? BfIRValue() : getEnumeratorMethod.mFunc, false, args);
|
|
|
|
if (itr.mType->IsConcreteInterfaceType())
|
|
itr.mType = itr.mType->GetUnderlyingType();
|
|
}
|
|
}
|
|
|
|
if (itr)
|
|
{
|
|
PopulateType(itr.mType, BfPopulateType_DataAndMethods);
|
|
|
|
BfTypeInstance* genericItrInterface = NULL;
|
|
|
|
auto _CheckInterface = [&](BfTypeInstance* interface)
|
|
{
|
|
if (interface->IsInstanceOf(isRefExpression ? mCompiler->mGenericIRefEnumeratorTypeDef : mCompiler->mGenericIEnumeratorTypeDef))
|
|
{
|
|
if (genericItrInterface != NULL)
|
|
{
|
|
Fail(StrFormat("Type '%s' implements multiple %s<T> interfaces", TypeToString(itr.mType).c_str(), isRefExpression ? "IRefEnumerator" : "IEnumerator"), forEachStmt->mCollectionExpression);
|
|
}
|
|
|
|
itrInterface = interface;
|
|
genericItrInterface = itrInterface->ToGenericTypeInstance();
|
|
if (inferVarType)
|
|
{
|
|
varType = genericItrInterface->mGenericTypeInfo->mTypeGenericArguments[0];
|
|
if (isRefExpression)
|
|
{
|
|
if (varType->IsPointer())
|
|
varType = CreateRefType(varType->GetUnderlyingType());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
auto enumeratorTypeInst = itr.mType->ToTypeInstance();
|
|
while (enumeratorTypeInst != NULL)
|
|
{
|
|
PopulateType(enumeratorTypeInst, Beefy::BfPopulateType_Interfaces_All);
|
|
|
|
for (auto& interfaceRef : enumeratorTypeInst->mInterfaces)
|
|
{
|
|
BfTypeInstance* interface = interfaceRef.mInterfaceType;
|
|
_CheckInterface(interface);
|
|
}
|
|
|
|
if (enumeratorTypeInst->IsInstanceOf(isRefExpression ? mCompiler->mGenericIRefEnumeratorTypeDef : mCompiler->mGenericIEnumeratorTypeDef))
|
|
{
|
|
itrInterface = enumeratorTypeInst;
|
|
genericItrInterface = itrInterface->ToGenericTypeInstance();
|
|
if (inferVarType)
|
|
{
|
|
varType = genericItrInterface->mGenericTypeInfo->mTypeGenericArguments[0];
|
|
if (isRefExpression)
|
|
{
|
|
if (varType->IsPointer())
|
|
varType = CreateRefType(varType);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (itrInterface != NULL)
|
|
break;
|
|
|
|
enumeratorTypeInst = enumeratorTypeInst->mBaseType;
|
|
}
|
|
|
|
if ((genericItrInterface == NULL) && (genericParamInst != NULL))
|
|
{
|
|
for (auto interface : genericParamInst->mInterfaceConstraints)
|
|
_CheckInterface(interface);
|
|
}
|
|
|
|
if (genericItrInterface == NULL)
|
|
{
|
|
if (!hadGetEnumeratorType)
|
|
{
|
|
Fail(StrFormat("Type '%s' must contain a 'GetEnumerator' method or implement an IEnumerator<T> interface", TypeToString(target.mType).c_str()), forEachStmt->mCollectionExpression);
|
|
}
|
|
else
|
|
Fail(StrFormat("Enumerator type '%s' must implement an %s<T> interface", TypeToString(itr.mType).c_str(), isRefExpression ? "IRefEnumerator" : "IEnumerator"), forEachStmt->mCollectionExpression);
|
|
itrInterface = NULL;
|
|
itr = BfTypedValue();
|
|
}
|
|
else
|
|
{
|
|
itrInterface = genericItrInterface;
|
|
if (isRefExpression)
|
|
{
|
|
refItrInterface = itrInterface;
|
|
PopulateType(refItrInterface);
|
|
// Must IRefEnumeratorf<T> must include only IEnumerator<T>
|
|
// BF_ASSERT(refItrInterface->mInterfaces.size() == 1);
|
|
// if (refItrInterface->mInterfaces.size() == 1)
|
|
// itrInterface = refItrInterface->mInterfaces[0].mInterfaceType;
|
|
}
|
|
itr = MakeAddressable(itr);
|
|
itr = RemoveReadOnly(itr);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertErrorState();
|
|
}
|
|
|
|
PopulateType(varType, BfPopulateType_Data);
|
|
|
|
// Apply deferred local assign to mEmbeddedStatement and itrStmt
|
|
BfDeferredLocalAssignData deferredLocalAssignData(mCurMethodState->mCurScope);
|
|
deferredLocalAssignData.ExtendFrom(mCurMethodState->mDeferredLocalAssignData, false);
|
|
deferredLocalAssignData.mVarIdBarrier = mCurMethodState->GetRootMethodState()->mCurLocalVarId;
|
|
SetAndRestoreValue<BfDeferredLocalAssignData*> prevDLA(mCurMethodState->mDeferredLocalAssignData, &deferredLocalAssignData);
|
|
|
|
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites);
|
|
|
|
if ((target.mType->IsSizedArray()) && (((BfSizedArrayType*)target.mType)->mElementCount == 0))
|
|
{
|
|
EmitEnsureInstructionAt();
|
|
mBfIRBuilder->mIgnoreWrites = true;
|
|
}
|
|
|
|
BfIdentifierNode* nameNode = NULL;
|
|
String variableName;
|
|
|
|
struct _TupleBind
|
|
{
|
|
BfIdentifierNode* mNameNode;
|
|
String mName;
|
|
BfType* mType;
|
|
BfLocalVariable* mVariable;
|
|
};
|
|
|
|
Array<_TupleBind> tupleBinds;
|
|
|
|
if (forEachStmt->mVariableName != NULL)
|
|
{
|
|
if (auto tupleExpr = BfNodeDynCast<BfTupleExpression>(forEachStmt->mVariableName))
|
|
{
|
|
CheckTupleVariableDeclaration(tupleExpr, varType);
|
|
|
|
if (varType->IsTuple())
|
|
{
|
|
auto tupleType = (BfTypeInstance*)varType;
|
|
|
|
for (int idx = 0; idx < BF_MIN((int)tupleExpr->mValues.size(), (int)tupleType->mFieldInstances.size()); idx++)
|
|
{
|
|
auto nameNode = tupleExpr->mValues[idx];
|
|
|
|
_TupleBind tupleBind;
|
|
tupleBind.mNameNode = BfNodeDynCast<BfIdentifierNode>(nameNode);
|
|
if ((tupleBind.mNameNode == NULL) && (nameNode != NULL))
|
|
{
|
|
Fail("Variable name expected", nameNode);
|
|
}
|
|
tupleBind.mName = nameNode->ToString();
|
|
tupleBind.mType = tupleType->mFieldInstances[idx].mResolvedType;
|
|
tupleBind.mVariable = NULL;
|
|
|
|
tupleBinds.Add(tupleBind);
|
|
if (idx == 0)
|
|
variableName = tupleBind.mName;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nameNode = BfNodeDynCast<BfIdentifierNode>(forEachStmt->mVariableName);
|
|
if (nameNode != NULL)
|
|
variableName = nameNode->ToString();
|
|
}
|
|
}
|
|
|
|
if (variableName.IsEmpty())
|
|
variableName = "_";
|
|
|
|
BfModuleMethodInstance getNextMethodInst;
|
|
|
|
BfType* nextEmbeddedType = NULL;
|
|
BfTypedValue nextResult;
|
|
if ((refItrInterface) || (itrInterface))
|
|
{
|
|
if (isRefExpression)
|
|
{
|
|
PopulateType(refItrInterface, BfPopulateType_Full_Force);
|
|
getNextMethodInst = GetMethodByName(refItrInterface, "GetNextRef");
|
|
}
|
|
else
|
|
{
|
|
PopulateType(itrInterface, BfPopulateType_Full_Force);
|
|
getNextMethodInst = GetMethodByName(itrInterface, "GetNext");
|
|
}
|
|
if (getNextMethodInst)
|
|
{
|
|
nextResult = BfTypedValue(CreateAlloca(getNextMethodInst.mMethodInstance->mReturnType), getNextMethodInst.mMethodInstance->mReturnType, true);
|
|
|
|
if (nextResult.mType->IsGenericTypeInstance())
|
|
{
|
|
nextEmbeddedType = ((BfTypeInstance*)nextResult.mType)->mGenericTypeInfo->mTypeGenericArguments[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InternalError("Failed to find GetNext");
|
|
}
|
|
}
|
|
if (nextEmbeddedType == NULL)
|
|
nextEmbeddedType = GetPrimitiveType(BfTypeCode_Var);
|
|
|
|
BfLocalVariable* itrLocalDef = NULL;
|
|
|
|
// Iterator local def
|
|
if (itr)
|
|
{
|
|
BfLocalVariable* localDef = new BfLocalVariable();
|
|
itrLocalDef = localDef;
|
|
localDef->mNameNode = nameNode;
|
|
localDef->mName = variableName;
|
|
localDef->mResolvedType = itr.mType;
|
|
localDef->mAddr = itr.mValue;
|
|
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
localDef->mReadFromId = 0;
|
|
localDef->Init();
|
|
UpdateSrcPos(forEachStmt);
|
|
CheckVariableDef(localDef);
|
|
AddLocalVariableDef(localDef, true);
|
|
}
|
|
|
|
BfIRValue varInst;
|
|
BfTypedValue varTypedVal;
|
|
bool needsValCopy = true;
|
|
|
|
BfType* origVarType = varType;
|
|
|
|
// Local variable
|
|
{
|
|
if (!tupleBinds.IsEmpty())
|
|
{
|
|
BF_ASSERT(varType->IsTuple());
|
|
auto tupleType = (BfTypeInstance*)varType;
|
|
|
|
// Tuple binds
|
|
needsValCopy = false;
|
|
if (!nextResult)
|
|
{
|
|
varInst = CreateAlloca(varType);
|
|
varTypedVal = BfTypedValue(varInst, varType, true);
|
|
}
|
|
|
|
// Local
|
|
for (int idx = 0; idx < (int)tupleBinds.size(); idx++)
|
|
{
|
|
auto& tupleBind = tupleBinds[idx];
|
|
BfLocalVariable* localDef = new BfLocalVariable();
|
|
localDef->mNameNode = tupleBind.mNameNode;
|
|
localDef->mName = tupleBind.mName;
|
|
localDef->mResolvedType = tupleBind.mType;
|
|
if (!needsValCopy)
|
|
localDef->mResolvedType = CreateRefType(localDef->mResolvedType);
|
|
localDef->mAddr = CreateAlloca(localDef->mResolvedType);
|
|
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
localDef->mReadFromId = 0;
|
|
if ((isLet) || (forEachStmt->mReadOnlyToken != NULL))
|
|
localDef->mIsReadOnly = true;
|
|
localDef->Init();
|
|
|
|
auto fieldInstance = &tupleType->mFieldInstances[idx];
|
|
if (fieldInstance->mDataIdx >= 0)
|
|
{
|
|
auto tuplePtrType = CreatePointerType(varType);
|
|
BfIRValue tuplePtr;
|
|
if (nextResult)
|
|
tuplePtr = mBfIRBuilder->CreateBitCast(nextResult.mValue, mBfIRBuilder->MapType(tuplePtrType));
|
|
else
|
|
tuplePtr = mBfIRBuilder->CreateBitCast(varInst, mBfIRBuilder->MapType(tuplePtrType));
|
|
auto valAddr = mBfIRBuilder->CreateInBoundsGEP(tuplePtr, 0, fieldInstance->mDataIdx);
|
|
mBfIRBuilder->CreateAlignedStore(valAddr, localDef->mAddr, localDef->mResolvedType->mAlign);
|
|
}
|
|
|
|
UpdateSrcPos(forEachStmt);
|
|
if ((itrLocalDef != NULL) && (idx == 0))
|
|
{
|
|
localDef->mLocalVarId = itrLocalDef->mLocalVarId;
|
|
localDef->mIsShadow = true;
|
|
}
|
|
else
|
|
{
|
|
CheckVariableDef(localDef);
|
|
}
|
|
|
|
AddLocalVariableDef(localDef, true, false, BfIRValue(), BfIRInitType_NotNeeded_AliveOnDecl);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Normal case
|
|
if ((nextResult) && (varType->IsComposite()) && (!varType->IsValuelessType()) && (!isRefExpression))
|
|
{
|
|
needsValCopy = false;
|
|
varType = CreateRefType(varType);
|
|
}
|
|
|
|
// Local
|
|
BfLocalVariable* localDef = new BfLocalVariable();
|
|
localDef->mNameNode = nameNode;
|
|
localDef->mName = variableName;
|
|
localDef->mResolvedType = varType;
|
|
if (!varType->IsValuelessType())
|
|
varInst = CreateAlloca(varType);
|
|
else
|
|
varInst = mBfIRBuilder->GetFakeVal();
|
|
localDef->mAddr = varInst;
|
|
localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
localDef->mReadFromId = 0;
|
|
if ((isLet) || (forEachStmt->mReadOnlyToken != NULL))
|
|
localDef->mIsReadOnly = true;
|
|
localDef->Init();
|
|
|
|
if (!needsValCopy)
|
|
{
|
|
auto valAddr = mBfIRBuilder->CreateBitCast(nextResult.mValue, mBfIRBuilder->MapType(varType));
|
|
mBfIRBuilder->CreateAlignedStore(valAddr, varInst, varType->mAlign);
|
|
}
|
|
|
|
UpdateSrcPos(forEachStmt);
|
|
if (itrLocalDef != NULL)
|
|
{
|
|
localDef->mLocalVarId = itrLocalDef->mLocalVarId;
|
|
localDef->mIsShadow = true;
|
|
}
|
|
else
|
|
{
|
|
CheckVariableDef(localDef);
|
|
}
|
|
|
|
AddLocalVariableDef(localDef, true, false, BfIRValue(), BfIRInitType_NotNeeded_AliveOnDecl);
|
|
|
|
varTypedVal = BfTypedValue(varInst, varType, true);
|
|
}
|
|
}
|
|
|
|
// Iterator
|
|
if (itr)
|
|
{
|
|
if ((!isArray) && (!isSizedArray))
|
|
{
|
|
BfFunctionBindResult functionBindResult;
|
|
BfExprEvaluator exprEvaluator(this);
|
|
exprEvaluator.mFunctionBindResult = &functionBindResult;
|
|
|
|
// Allow for "Dispose" not to exist
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
|
|
BfResolvedArgs resolvedArgs;
|
|
exprEvaluator.mBfEvalExprFlags = (BfEvalExprFlags)(exprEvaluator.mBfEvalExprFlags | BfEvalExprFlags_NoAutoComplete);
|
|
exprEvaluator.MatchMethod(forEachStmt->mCollectionExpression, NULL, itr, false, false, "Dispose", resolvedArgs, BfMethodGenericArguments());
|
|
if (functionBindResult.mMethodInstance != NULL)
|
|
{
|
|
BfModuleMethodInstance moduleMethodInstance;
|
|
moduleMethodInstance = BfModuleMethodInstance(functionBindResult.mMethodInstance, functionBindResult.mFunc);
|
|
AddDeferredCall(moduleMethodInstance, functionBindResult.mIRArgs, mCurMethodState->mCurScope);
|
|
}
|
|
}
|
|
}
|
|
|
|
BfScopeData innerScopeData;
|
|
if (forEachStmt->mLabelNode != NULL)
|
|
innerScopeData.mLabelNode = forEachStmt->mLabelNode->mLabel;
|
|
innerScopeData.mValueScopeStart = ValueScopeStart();
|
|
mCurMethodState->AddScope(&innerScopeData);
|
|
NewScopeState(true, false);
|
|
innerScopeData.mIsLoop = true;
|
|
|
|
if ((autoComplete != NULL) && (forEachStmt->mVariableTypeRef != NULL))
|
|
autoComplete->CheckVarResolution(forEachStmt->mVariableTypeRef, origVarType);
|
|
|
|
if (isArray || isSizedArray)
|
|
mBfIRBuilder->CreateAlignedStore(GetConstValue(0), itr.mValue, itr.mType->mAlign);
|
|
|
|
auto valueScopeStartInner = ValueScopeStart();
|
|
|
|
// We may have a call in the loop body
|
|
mCurMethodState->mMayNeedThisAccessCheck = true;
|
|
|
|
auto condBB = mBfIRBuilder->CreateBlock("foreach.cond", true);
|
|
auto bodyBB = mBfIRBuilder->CreateBlock("foreach.body");
|
|
auto incBB = mBfIRBuilder->CreateBlock("foreach.inc");
|
|
auto endBB = mBfIRBuilder->CreateBlock("foreach.end");
|
|
|
|
BfBreakData breakData;
|
|
breakData.mIRContinueBlock = incBB;
|
|
breakData.mIRBreakBlock = endBB;
|
|
breakData.mScope = &innerScopeData;
|
|
breakData.mInnerValueScopeStart = valueScopeStartInner;
|
|
breakData.mPrevBreakData = mCurMethodState->mBreakData;
|
|
SetAndRestoreValue<BfBreakData*> prevBreakData(mCurMethodState->mBreakData, &breakData);
|
|
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
mBfIRBuilder->SetInsertPoint(condBB);
|
|
if (forEachStmt->mCollectionExpression != NULL)
|
|
UpdateExprSrcPos(forEachStmt->mCollectionExpression);
|
|
BfIRValue conditionValue;
|
|
if (isSizedArray) // if (i < lengthof(array)
|
|
{
|
|
auto itrVal = mBfIRBuilder->CreateLoad(itr.mValue);
|
|
auto arrayType = (BfSizedArrayType*)target.mType;
|
|
PopulateType(arrayType, BfPopulateType_DataAndMethods);
|
|
BfIRValue lengthVal = GetConstValue(arrayType->mElementCount);
|
|
conditionValue = mBfIRBuilder->CreateCmpLT(itrVal, lengthVal, true);
|
|
mBfIRBuilder->CreateCondBr(conditionValue, bodyBB, endBB);
|
|
ValueScopeEnd(valueScopeStartInner);
|
|
}
|
|
else if (isArray) // if (i < array.mLength)
|
|
{
|
|
auto itrVal = mBfIRBuilder->CreateAlignedLoad(itr.mValue, itr.mType->mAlign);
|
|
auto arrayType = (BfArrayType*)target.mType;
|
|
PopulateType(arrayType);
|
|
auto arrayBaseValue = mBfIRBuilder->CreateBitCast(target.mValue, mBfIRBuilder->MapType(arrayType->mBaseType, BfIRPopulateType_Full));
|
|
int getLengthBitCount = arrayType->GetLengthBitCount();
|
|
|
|
BfIRValue lengthVal;
|
|
if (arrayType->mBaseType->mTypeFailed)
|
|
{
|
|
AssertErrorState();
|
|
if (getLengthBitCount == 64)
|
|
lengthVal = GetConstValue64(0);
|
|
else
|
|
lengthVal = GetConstValue32(0);
|
|
}
|
|
else
|
|
{
|
|
auto fieldInst = GetFieldInstance(arrayType->mBaseType, 0, "mLength");
|
|
if (fieldInst != NULL)
|
|
{
|
|
auto lengthValAddr = mBfIRBuilder->CreateInBoundsGEP(arrayBaseValue, 0, fieldInst->mDataIdx);
|
|
lengthVal = mBfIRBuilder->CreateAlignedLoad(lengthValAddr, fieldInst->mResolvedType->mAlign);
|
|
}
|
|
}
|
|
lengthVal = mBfIRBuilder->CreateNumericCast(lengthVal, true, BfTypeCode_IntPtr);
|
|
conditionValue = mBfIRBuilder->CreateCmpLT(itrVal, lengthVal, true);
|
|
mBfIRBuilder->CreateCondBr(conditionValue, bodyBB, endBB);
|
|
ValueScopeEnd(valueScopeStartInner);
|
|
}
|
|
else // if (itr.MoveNext())
|
|
{
|
|
if (!itr)
|
|
{
|
|
if (!isVarEnumerator)
|
|
AssertErrorState();
|
|
|
|
mBfIRBuilder->CreateBr(endBB);
|
|
}
|
|
else
|
|
{
|
|
BfExprEvaluator exprEvaluator(this);
|
|
auto itrTypeInstance = itr.mType->ToTypeInstance();
|
|
SizedArray<BfResolvedArg, 0> resolvedArgs;
|
|
BfMethodMatcher methodMatcher(forEachStmt->mCollectionExpression, this, getNextMethodInst.mMethodInstance, resolvedArgs, BfMethodGenericArguments());
|
|
if (isRefExpression)
|
|
methodMatcher.CheckType(refItrInterface, itr, false);
|
|
else
|
|
methodMatcher.CheckType(itrInterface, itr, false);
|
|
methodMatcher.TryDevirtualizeCall(itr);
|
|
exprEvaluator.mReceivingValue = &nextResult;
|
|
auto retVal = exprEvaluator.CreateCall(&methodMatcher, itr);
|
|
if (exprEvaluator.mReceivingValue != NULL)
|
|
{
|
|
if (mIsComptimeModule)
|
|
{
|
|
retVal = LoadValue(retVal);
|
|
mBfIRBuilder->CreateStore(retVal.mValue, nextResult.mValue);
|
|
}
|
|
else
|
|
AssertErrorState();
|
|
}
|
|
if ((retVal) && (!retVal.mType->IsVar()))
|
|
{
|
|
auto i8Result = ExtractValue(nextResult, NULL, 2);
|
|
i8Result = LoadValue(i8Result);
|
|
BF_ASSERT(i8Result.mType == GetPrimitiveType(BfTypeCode_Int8));
|
|
conditionValue = mBfIRBuilder->CreateCmpEQ(i8Result.mValue, GetConstValue8(0));
|
|
}
|
|
else
|
|
conditionValue = GetDefaultValue(GetPrimitiveType(BfTypeCode_Boolean));
|
|
mBfIRBuilder->CreateCondBr(conditionValue, bodyBB, endBB);
|
|
ValueScopeEnd(valueScopeStartInner);
|
|
}
|
|
}
|
|
|
|
mBfIRBuilder->AddBlock(bodyBB);
|
|
mBfIRBuilder->SetInsertPoint(bodyBB);
|
|
if (!varTypedVal)
|
|
{
|
|
// Nothing to do...
|
|
}
|
|
else if (isSizedArray) // val = array[i]
|
|
{
|
|
auto itrVal = mBfIRBuilder->CreateLoad(itr.mValue);
|
|
auto arrayType = (BfSizedArrayType*)target.mType;
|
|
BfType* ptrType = CreatePointerType(arrayType->mElementType);
|
|
BfTypedValue arrayItem;
|
|
|
|
if (arrayType->mElementType->IsValuelessType())
|
|
{
|
|
arrayItem = GetDefaultTypedValue(arrayType->mElementType);
|
|
}
|
|
else
|
|
{
|
|
target = MakeAddressable(target);
|
|
arrayItem = BfTypedValue(CreateIndexedValue(arrayType->mElementType, target.mValue, itrVal, true), arrayType->mElementType, true);
|
|
if (isRefExpression)
|
|
arrayItem = BfTypedValue(arrayItem.mValue, CreateRefType(arrayItem.mType));
|
|
}
|
|
arrayItem = Cast(forEachStmt->mCollectionExpression, arrayItem, varType, BfCastFlags_Explicit);
|
|
if ((arrayItem) && (!arrayItem.mValue.IsFake()))
|
|
{
|
|
arrayItem = LoadValue(arrayItem);
|
|
if (arrayItem)
|
|
mBfIRBuilder->CreateStore(arrayItem.mValue, varInst);
|
|
}
|
|
}
|
|
else if (isArray) // val = array[i]
|
|
{
|
|
auto itrVal = mBfIRBuilder->CreateLoad(itr.mValue);
|
|
BfTypedValueExpression typedValueExpr;
|
|
typedValueExpr.Init(BfTypedValue(itrVal, itrType));
|
|
BfExprEvaluator exprEvaluator(this);
|
|
SizedArray<BfExpression*, 1> indices;
|
|
indices.push_back(&typedValueExpr);
|
|
BfSizedArray<BfExpression*> sizedArgExprs(indices);
|
|
BfResolvedArgs argValues(&sizedArgExprs);
|
|
exprEvaluator.ResolveArgValues(argValues);
|
|
bool boundsCheck = mCompiler->mOptions.mRuntimeChecks;
|
|
auto typeOptions = GetTypeOptions();
|
|
if (typeOptions != NULL)
|
|
boundsCheck = typeOptions->Apply(boundsCheck, BfOptionFlags_RuntimeChecks);
|
|
|
|
BfMethodMatcher methodMatcher(forEachStmt->mVariableName, this, "get__", argValues.mResolvedArgs, BfMethodGenericArguments());
|
|
methodMatcher.mMethodType = BfMethodType_PropertyGetter;
|
|
methodMatcher.CheckType(target.mType->ToTypeInstance(), target, false);
|
|
if (methodMatcher.mBestMethodDef == NULL)
|
|
{
|
|
Fail("Failed to find indexer method in array", forEachStmt);
|
|
}
|
|
else
|
|
{
|
|
methodMatcher.mCheckedKind = boundsCheck ? BfCheckedKind_Checked : BfCheckedKind_Unchecked;
|
|
BfTypedValue arrayItem = exprEvaluator.CreateCall(&methodMatcher, target);
|
|
|
|
if ((varInst) && (arrayItem))
|
|
{
|
|
if (isRefExpression)
|
|
arrayItem = BfTypedValue(arrayItem.mValue, CreateRefType(arrayItem.mType));
|
|
else if (!arrayItem.mType->IsComposite())
|
|
arrayItem = LoadValue(arrayItem);
|
|
arrayItem = Cast(forEachStmt->mCollectionExpression, arrayItem, varType, BfCastFlags_Explicit);
|
|
arrayItem = LoadValue(arrayItem);
|
|
if ((arrayItem) && (!arrayItem.mType->IsValuelessType()))
|
|
mBfIRBuilder->CreateStore(arrayItem.mValue, varInst);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!itr)
|
|
{
|
|
if (!isVarEnumerator)
|
|
AssertErrorState();
|
|
}
|
|
else if (mCompiler->IsAutocomplete())
|
|
{
|
|
// If we don't do this shortcut, we can end up creating temporary "boxed" objects
|
|
}
|
|
else
|
|
{
|
|
if (needsValCopy)
|
|
{
|
|
auto nextVal = BfTypedValue(mBfIRBuilder->CreateBitCast(nextResult.mValue, mBfIRBuilder->MapType(CreatePointerType(nextEmbeddedType))), nextEmbeddedType, true);
|
|
if (isRefExpression)
|
|
{
|
|
if (nextVal.mType->IsPointer())
|
|
nextVal = BfTypedValue(nextVal.mValue, CreateRefType(nextVal.mType->GetUnderlyingType()), true);
|
|
}
|
|
nextVal = Cast(forEachStmt->mCollectionExpression, nextVal, varType, BfCastFlags_Explicit);
|
|
nextVal = LoadValue(nextVal);
|
|
if ((nextVal) && (!nextVal.mType->IsValuelessType()))
|
|
mBfIRBuilder->CreateAlignedStore(nextVal.mValue, varInst, nextVal.mType->mAlign);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (forEachStmt->mEmbeddedStatement != NULL)
|
|
{
|
|
VisitEmbeddedStatement(forEachStmt->mEmbeddedStatement);
|
|
}
|
|
|
|
if (!mCurMethodState->mLeftBlockUncond)
|
|
{
|
|
ValueScopeEnd(valueScopeStartInner);
|
|
mBfIRBuilder->CreateBr(incBB);
|
|
}
|
|
|
|
mBfIRBuilder->AddBlock(incBB);
|
|
mBfIRBuilder->SetInsertPoint(incBB);
|
|
|
|
if (isArray || isSizedArray)
|
|
{
|
|
auto val = mBfIRBuilder->CreateLoad(itr.mValue);
|
|
auto result = mBfIRBuilder->CreateAdd(val, GetConstValue(1));
|
|
mBfIRBuilder->CreateStore(result, itr.mValue);
|
|
}
|
|
else
|
|
{
|
|
// Nothing to do
|
|
}
|
|
|
|
mBfIRBuilder->CreateBr(condBB);
|
|
|
|
mBfIRBuilder->AddBlock(endBB);
|
|
mBfIRBuilder->SetInsertPoint(endBB);
|
|
if ((itrLocalDef != NULL) && (itrLocalDef->mDbgVarInst) && (IsTargetingBeefBackend()))
|
|
{
|
|
// If this shadows another enumerator variable then we need to explicitly mark the end of this one
|
|
mBfIRBuilder->DbgLifetimeEnd(itrLocalDef->mDbgVarInst);
|
|
}
|
|
|
|
// The 'return' may have been inside the block, which may not have been entered if preconditions were not met
|
|
mCurMethodState->SetHadReturn(false);
|
|
mCurMethodState->mLeftBlockUncond = false;
|
|
mCurMethodState->mLeftBlockCond = false;
|
|
|
|
RestoreScopeState();
|
|
RestoreScopeState();
|
|
}
|
|
|
|
void BfModule::Visit(BfDeferStatement* deferStmt)
|
|
{
|
|
if (deferStmt->mTargetNode == NULL)
|
|
{
|
|
AssertErrorState();
|
|
return;
|
|
}
|
|
|
|
//TODO: Why in the world didn't we want to be able to step onto a defer statement?
|
|
// We only want the breakpoint to hit on execution of the defer, not on insertion of it
|
|
//SetAndRestoreValue<bool> prevSetIllegalSrcPos(mSetIllegalSrcPosition, true);
|
|
|
|
UpdateSrcPos(deferStmt);
|
|
EmitEnsureInstructionAt();
|
|
|
|
BfScopeData* scope = NULL;
|
|
|
|
if (deferStmt->mScopeToken != NULL)
|
|
{
|
|
if (deferStmt->mScopeToken->GetToken() == BfToken_Scope)
|
|
scope = mCurMethodState->mCurScope->GetTargetable();
|
|
else
|
|
scope = &mCurMethodState->mHeadScope;
|
|
}
|
|
else if (deferStmt->mScopeName != NULL)
|
|
{
|
|
scope = FindScope(deferStmt->mScopeName, true);
|
|
|
|
if (scope == NULL)
|
|
{
|
|
AssertErrorState();
|
|
// The scope doesn't exist, continue with the current scope so we still get an evaluation of the deferred code
|
|
scope = mCurMethodState->mCurScope;
|
|
}
|
|
}
|
|
else
|
|
scope = mCurMethodState->mCurScope;
|
|
|
|
if ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mAutoComplete != NULL))
|
|
{
|
|
auto targetIdentifier = BfNodeDynCast<BfIdentifierNode>(deferStmt->mScopeName);
|
|
if ((deferStmt->mScopeName == NULL) || (targetIdentifier != NULL))
|
|
mCompiler->mResolvePassData->mAutoComplete->CheckLabel(targetIdentifier, deferStmt->mColonToken, scope);
|
|
}
|
|
|
|
if ((scope == mCurMethodState->mCurScope) && (scope->mCloseNode == NULL))
|
|
{
|
|
auto parser = deferStmt->GetParser();
|
|
if ((parser == NULL) || (!parser->mIsEmitted))
|
|
Warn(0, "This defer will immediately execute. Consider specifying a wider scope target such as 'defer::'", deferStmt->mDeferToken);
|
|
}
|
|
|
|
if (auto block = BfNodeDynCast<BfBlock>(deferStmt->mTargetNode))
|
|
{
|
|
if (deferStmt->mBind != NULL)
|
|
{
|
|
Array<BfDeferredCapture> captures;
|
|
for (auto identifier : deferStmt->mBind->mParams)
|
|
{
|
|
BfDeferredCapture deferredCapture;
|
|
deferredCapture.mName = identifier->ToString();
|
|
deferredCapture.mValue = CreateValueFromExpression(identifier);
|
|
if (deferredCapture.mValue)
|
|
{
|
|
captures.push_back(deferredCapture);
|
|
}
|
|
}
|
|
AddDeferredBlock(block, scope, &captures);
|
|
}
|
|
else
|
|
AddDeferredBlock(block, scope);
|
|
}
|
|
else if (auto exprStmt = BfNodeDynCast<BfExpressionStatement>(deferStmt->mTargetNode))
|
|
{
|
|
BfDeferCallData deferCallData;
|
|
deferCallData.mRefNode = exprStmt->mExpression;
|
|
deferCallData.mScopeAlloc = scope;
|
|
|
|
BfExprEvaluator expressionEvaluator(this);
|
|
expressionEvaluator.mDeferCallData = &deferCallData;
|
|
expressionEvaluator.VisitChild(exprStmt->mExpression);
|
|
if (mCurMethodState->mPendingNullConditional != NULL)
|
|
FlushNullConditional(expressionEvaluator.mResult, true);
|
|
}
|
|
else if (auto deleteStmt = BfNodeDynCast<BfDeleteStatement>(deferStmt->mTargetNode))
|
|
{
|
|
if (deleteStmt->mExpression == NULL)
|
|
{
|
|
AssertErrorState();
|
|
return;
|
|
}
|
|
|
|
auto val = CreateValueFromExpression(deleteStmt->mExpression);
|
|
if (!val)
|
|
return;
|
|
|
|
if (mCompiler->IsAutocomplete())
|
|
return;
|
|
|
|
bool isGenericParam = false;
|
|
auto checkType = val.mType;
|
|
if (val.mType->IsGenericParam())
|
|
{
|
|
isGenericParam = true;
|
|
auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)val.mType);
|
|
if (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Delete)
|
|
return;
|
|
if (genericParamInst->mTypeConstraint != NULL)
|
|
checkType = genericParamInst->mTypeConstraint;
|
|
}
|
|
|
|
bool isAppendDelete = false;
|
|
BfTypedValue customAllocator;
|
|
if (deleteStmt->mAllocExpr != NULL)
|
|
{
|
|
if (auto expr = BfNodeDynCast<BfExpression>(deleteStmt->mAllocExpr))
|
|
customAllocator = CreateValueFromExpression(expr);
|
|
else if (auto tokenNode = BfNodeDynCast<BfTokenNode>(deleteStmt->mAllocExpr))
|
|
{
|
|
if (tokenNode->mToken == BfToken_Append)
|
|
isAppendDelete = true;
|
|
}
|
|
}
|
|
|
|
auto internalType = ResolveTypeDef(mCompiler->mInternalTypeDef);
|
|
PopulateType(checkType);
|
|
if (checkType->IsVar())
|
|
return;
|
|
|
|
if ((!checkType->IsObjectOrInterface()) && (!checkType->IsPointer()))
|
|
{
|
|
VisitChild(deferStmt->mTargetNode);
|
|
Fail(StrFormat("Cannot delete a value of type '%s'", TypeToString(val.mType).c_str()), deferStmt->mTargetNode);
|
|
return;
|
|
}
|
|
|
|
if (isGenericParam)
|
|
return;
|
|
|
|
bool isDtorOnly = false;
|
|
if (customAllocator.mType == GetPrimitiveType(BfTypeCode_NullPtr))
|
|
{
|
|
if (!checkType->IsObjectOrInterface())
|
|
Warn(0, "Type '%' has no destructor, so delete:null has no effect", deleteStmt->mExpression);
|
|
}
|
|
else if (customAllocator)
|
|
{
|
|
BfFunctionBindResult functionBindResult;
|
|
functionBindResult.mWantsArgs = true;
|
|
auto customAllocTypeInst = customAllocator.mType->ToTypeInstance();
|
|
if ((checkType->IsObjectOrInterface()) && (customAllocTypeInst != NULL) && (customAllocTypeInst->mTypeDef->GetMethodByName("FreeObject") != NULL))
|
|
{
|
|
BfTypedValueExpression typedValueExpr;
|
|
typedValueExpr.Init(val);
|
|
typedValueExpr.mRefNode = deleteStmt->mAllocExpr;
|
|
BfExprEvaluator exprEvaluator(this);
|
|
SizedArray<BfExpression*, 2> argExprs;
|
|
argExprs.push_back(&typedValueExpr);
|
|
BfSizedArray<BfExpression*> sizedArgExprs(argExprs);
|
|
BfResolvedArgs argValues(&sizedArgExprs);
|
|
exprEvaluator.ResolveArgValues(argValues);
|
|
exprEvaluator.mNoBind = true;
|
|
exprEvaluator.mFunctionBindResult = &functionBindResult;
|
|
exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, false, "FreeObject", argValues, BfMethodGenericArguments());
|
|
}
|
|
else
|
|
{
|
|
auto voidPtrType = GetPrimitiveType(BfTypeCode_NullPtr);
|
|
auto ptrValue = BfTypedValue(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(voidPtrType)), voidPtrType);
|
|
BfTypedValueExpression typedValueExpr;
|
|
typedValueExpr.Init(ptrValue);
|
|
BfExprEvaluator exprEvaluator(this);
|
|
SizedArray<BfExpression*, 2> argExprs;
|
|
argExprs.push_back(&typedValueExpr);
|
|
BfSizedArray<BfExpression*> sizedArgExprs(argExprs);
|
|
BfResolvedArgs argValues(&sizedArgExprs);
|
|
exprEvaluator.ResolveArgValues(argValues);
|
|
exprEvaluator.mNoBind = true;
|
|
exprEvaluator.mFunctionBindResult = &functionBindResult;
|
|
exprEvaluator.MatchMethod(deleteStmt->mAllocExpr, NULL, customAllocator, false, false, "Free", argValues, BfMethodGenericArguments());
|
|
}
|
|
|
|
if (functionBindResult.mMethodInstance != NULL)
|
|
{
|
|
AddDeferredCall(BfModuleMethodInstance(functionBindResult.mMethodInstance, functionBindResult.mFunc), functionBindResult.mIRArgs, scope, deleteStmt, true);
|
|
}
|
|
}
|
|
|
|
if (checkType->IsObjectOrInterface())
|
|
{
|
|
auto objectType = mContext->mBfObjectType;
|
|
PopulateType(objectType);
|
|
BfMethodInstance* methodInstance = objectType->mVirtualMethodTable[mCompiler->GetVTableMethodOffset() + 0].mImplementingMethod;
|
|
BF_ASSERT(methodInstance->mMethodDef->mName == "~this");
|
|
SizedArray<BfIRValue, 1> llvmArgs;
|
|
llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(objectType)));
|
|
|
|
if (!customAllocator)
|
|
{
|
|
if ((mCompiler->mOptions.mEnableRealtimeLeakCheck) && (!mIsComptimeModule))
|
|
{
|
|
auto moduleMethodInstance = GetInternalMethod("Dbg_MarkObjectDeleted");
|
|
AddDeferredCall(moduleMethodInstance, llvmArgs, scope, deleteStmt, false, true);
|
|
}
|
|
else
|
|
{
|
|
auto moduleMethodInstance = GetInternalMethod("Free");
|
|
SizedArray<BfIRValue, 1> llvmArgs;
|
|
llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->GetPrimitiveType(BfTypeCode_NullPtr)));
|
|
AddDeferredCall(moduleMethodInstance, llvmArgs, scope, deleteStmt, false, true);
|
|
}
|
|
}
|
|
|
|
auto moduleMethodInstance = GetMethodInstance(objectType, methodInstance->mMethodDef, BfTypeVector());
|
|
AddDeferredCall(moduleMethodInstance, llvmArgs, scope, deleteStmt, false, true);
|
|
|
|
if ((mCompiler->mOptions.mObjectHasDebugFlags) && (!mIsComptimeModule))
|
|
{
|
|
auto moduleMethodInstance = GetMethodByName(internalType->ToTypeInstance(), (deleteStmt->mTargetTypeToken != NULL) ? "Dbg_ObjectPreCustomDelete" : "Dbg_ObjectPreDelete");
|
|
AddDeferredCall(moduleMethodInstance, llvmArgs, scope, deleteStmt, false, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((!customAllocator) && (!isAppendDelete))
|
|
{
|
|
val = LoadValue(val);
|
|
BfModuleMethodInstance moduleMethodInstance;
|
|
if ((mCompiler->mOptions.mDebugAlloc) && (!mIsComptimeModule))
|
|
moduleMethodInstance = GetMethodByName(internalType->ToTypeInstance(), "Dbg_RawFree");
|
|
else
|
|
moduleMethodInstance = GetMethodByName(internalType->ToTypeInstance(), "Free");
|
|
SizedArray<BfIRValue, 1> llvmArgs;
|
|
llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->GetPrimitiveType(BfTypeCode_NullPtr)));
|
|
AddDeferredCall(moduleMethodInstance, llvmArgs, scope, deleteStmt, false, true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertErrorState();
|
|
VisitChild(deferStmt->mTargetNode);
|
|
}
|
|
}
|
|
|
|
void BfModule::Visit(BfBlock* block)
|
|
{
|
|
VisitEmbeddedStatement(block);
|
|
}
|
|
|
|
void BfModule::Visit(BfUnscopedBlock* block)
|
|
{
|
|
VisitEmbeddedStatement(block, NULL, BfEmbeddedStatementFlags_Unscoped);
|
|
}
|
|
|
|
void BfModule::Visit(BfLabeledBlock* labeledBlock)
|
|
{
|
|
VisitEmbeddedStatement(labeledBlock);
|
|
}
|
|
|
|
void BfModule::Visit(BfRootNode* rootNode)
|
|
{
|
|
VisitMembers(rootNode);
|
|
}
|
|
|
|
void BfModule::Visit(BfInlineAsmStatement* asmStmt)
|
|
{
|
|
#if 0
|
|
enum RegClobberFlags //CDH TODO add support for mmx/xmm/fpst etc (how are these signified in LLVM? check LangRef inline asm docs clobber list info)
|
|
{
|
|
// please keep eax through edx in alphabetical order (grep $BYTEREGS for why)
|
|
REGCLOBBERF_EAX = (1 << 0),
|
|
REGCLOBBERF_EBX = (1 << 1),
|
|
REGCLOBBERF_ECX = (1 << 2),
|
|
REGCLOBBERF_EDX = (1 << 3),
|
|
REGCLOBBERF_ESI = (1 << 4),
|
|
REGCLOBBERF_EDI = (1 << 5),
|
|
REGCLOBBERF_ESP = (1 << 6),
|
|
REGCLOBBERF_EBP = (1 << 7),
|
|
|
|
REGCLOBBERF_XMM0 = (1 << 8),
|
|
REGCLOBBERF_XMM1 = (1 << 9),
|
|
REGCLOBBERF_XMM2 = (1 << 10),
|
|
REGCLOBBERF_XMM3 = (1 << 11),
|
|
REGCLOBBERF_XMM4 = (1 << 12),
|
|
REGCLOBBERF_XMM5 = (1 << 13),
|
|
REGCLOBBERF_XMM6 = (1 << 14),
|
|
REGCLOBBERF_XMM7 = (1 << 15),
|
|
|
|
REGCLOBBERF_FPST0 = (1 << 16),
|
|
REGCLOBBERF_FPST1 = (1 << 17),
|
|
REGCLOBBERF_FPST2 = (1 << 18),
|
|
REGCLOBBERF_FPST3 = (1 << 19),
|
|
REGCLOBBERF_FPST4 = (1 << 20),
|
|
REGCLOBBERF_FPST5 = (1 << 21),
|
|
REGCLOBBERF_FPST6 = (1 << 22),
|
|
REGCLOBBERF_FPST7 = (1 << 23),
|
|
|
|
REGCLOBBERF_MM0 = (1 << 24),
|
|
REGCLOBBERF_MM1 = (1 << 25),
|
|
REGCLOBBERF_MM2 = (1 << 26),
|
|
REGCLOBBERF_MM3 = (1 << 27),
|
|
REGCLOBBERF_MM4 = (1 << 28),
|
|
REGCLOBBERF_MM5 = (1 << 29),
|
|
REGCLOBBERF_MM6 = (1 << 30),
|
|
REGCLOBBERF_MM7 = (1 << 31),
|
|
};
|
|
|
|
const char* regClobberNames[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "esp", "ebp", nullptr }; // must be in same order as flags
|
|
unsigned long regClobberFlags = 0;
|
|
|
|
std::function<bool(const StringImpl&, bool)> matchRegFunc = [this, ®ClobberNames, ®ClobberFlags](const StringImpl& name, bool isClobber)
|
|
{
|
|
bool found = false;
|
|
int nameLen = name.length();
|
|
if (nameLen == 3)
|
|
{
|
|
if ((name[0] == 's') && (name[1] == 't') && (name[2] >= '0') && (name[2] <= '7'))
|
|
{
|
|
// st# regs (unparenthesized at this point)
|
|
if (isClobber)
|
|
regClobberFlags |= (REGCLOBBERF_FPST0 << (name[2] - '0'));
|
|
found = true;
|
|
}
|
|
else if ((name[0] == 'm') && (name[1] == 'm') && (name[2] >= '0') && (name[2] <= '7'))
|
|
{
|
|
// mm regs
|
|
if (isClobber)
|
|
regClobberFlags |= (REGCLOBBERF_MM0 << (name[2] - '0'));
|
|
found = true;
|
|
}
|
|
else
|
|
{
|
|
// dword regs
|
|
for (int iRegCheck = 0; regClobberNames[iRegCheck] != nullptr; ++iRegCheck)
|
|
{
|
|
if (!strcmp(name.c_str(), regClobberNames[iRegCheck]))
|
|
{
|
|
if (isClobber)
|
|
regClobberFlags |= (1 << iRegCheck);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (nameLen == 2)
|
|
{
|
|
// word & byte regs
|
|
for (int iRegCheck = 0; regClobberNames[iRegCheck] != nullptr; ++iRegCheck)
|
|
{
|
|
if (!strcmp(name.c_str(), regClobberNames[iRegCheck] + 1)) // skip leading 'e'
|
|
{
|
|
if (isClobber)
|
|
regClobberFlags |= (1 << iRegCheck);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
// check for byte regs for eax through edx (e.g. al, ah, bl, bh....)
|
|
if ((nameLen == 2) && (name[0] >= 'a') && (name[0] <= 'd') && ((name[1] == 'l') || (name[1] == 'h')))
|
|
{
|
|
if (isClobber)
|
|
regClobberFlags |= (1 << (name[0] - 'a'));// $BYTEREGS this is why we want alphabetical order
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
else if ((nameLen == 4) && (name[0] == 'x') && (name[1] == 'm') && (name[2] == 'm') && (name[3] >= '0') && (name[3] <= '7'))
|
|
{
|
|
// xmm regs
|
|
if (isClobber)
|
|
regClobberFlags |= (REGCLOBBERF_XMM0 << (name[3] - '0'));
|
|
found = true;
|
|
}
|
|
return found;
|
|
};
|
|
|
|
int asmInstCount = (int)asmStmt->mInstructions.size();
|
|
typedef std::map<String, int> StrToVarIndexMap;
|
|
StrToVarIndexMap strToVarIndexMap;
|
|
|
|
if (mCompiler->IsAutocomplete())
|
|
{
|
|
// auto-complete "fast-pass" just to eliminate unused/unassigned variable yellow warning flashes
|
|
|
|
for (int iInst=0; iInst<asmInstCount; ++iInst)
|
|
{
|
|
auto instNode = asmStmt->mInstructions[iInst];
|
|
BfInlineAsmInstruction::AsmInst& asmInst = instNode->mAsmInst;
|
|
|
|
bool hasLabel = !asmInst.mLabel.empty();
|
|
bool hasOpCode = !asmInst.mOpCode.empty();
|
|
|
|
if (hasLabel || hasOpCode) // check against blank lines
|
|
{
|
|
if (hasOpCode) // check against label-only lines (which still get written out, but don't do any other processing)
|
|
{
|
|
int argCount = (int)asmInst.mArgs.size();
|
|
|
|
for (int iArg=0; iArg<argCount; ++iArg)
|
|
{
|
|
BfInlineAsmInstruction::AsmArg* arg = &asmInst.mArgs[iArg];
|
|
if (arg->mType == BfInlineAsmInstruction::AsmArg::ARGTYPE_IntReg)
|
|
{
|
|
bool found = matchRegFunc(arg->mReg, false);
|
|
|
|
if (!found)
|
|
{
|
|
StrToVarIndexMap::iterator it = strToVarIndexMap.find(arg->mReg);
|
|
if (it == strToVarIndexMap.end())
|
|
{
|
|
for (int i = 0; i < (int) mCurMethodState->mLocals.size(); i++)
|
|
{
|
|
auto& checkLocal = mCurMethodState->mLocals[i];
|
|
if (checkLocal.mName == arg->mReg)
|
|
{
|
|
// if you access a variable in asm, we suppress any warnings related to used or assigned, regardless of usage
|
|
checkLocal.mIsReadFrom = true;
|
|
checkLocal.mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int debugLocOffset = 0;
|
|
if (!asmStmt->mInstructions.empty())
|
|
debugLocOffset = asmStmt->mInstructions.front()->GetSrcStart() - asmStmt->GetSrcStart();
|
|
UpdateSrcPos(asmStmt, true, debugLocOffset);
|
|
|
|
mCurMethodState->mInHeadScope = false;
|
|
BfScopeData prevScope = mCurMethodState->mCurScope;
|
|
mCurMethodState->mCurScope->mPrevScope = &prevScope;
|
|
NewScopeState();
|
|
|
|
bool failed = false;
|
|
|
|
if (!mCpu)
|
|
mCpu = new Beefy::X86Cpu();
|
|
|
|
//const char* srcAsmText = "nop\nnop\n\n\nmov eax, i\ninc eax\nmov i, eax\n_emit 0x0F\n_emit 0xC7\n_emit 0xF0\n\n\nnop\nnop"; //CDH TODO extract from actual lexical text block
|
|
//const char* srcAsmText = "nop\nnop\n\n\nmov eax, i\ninc eax\nmov i, eax\nrdrand eax\n\n\nnop\nnop"; //CDH TODO extract from actual lexical text block
|
|
//String srcAsmTextStr(&asmStmt->mParser->mSrc[asmStmt->mSrcStart], asmStmt->mSrcEnd - asmStmt->mSrcStart);
|
|
//const char* srcAsmText = srcAsmTextStr.c_str();
|
|
|
|
String dstAsmText;
|
|
String constraintStr, clobberStr;
|
|
int constraintCount = 0;
|
|
bool hasMemoryClobber = false;
|
|
Array<Type*> paramTypes;
|
|
SizedArray<Value*, 1> llvmArgs;
|
|
|
|
int lastDebugLine = -1;
|
|
int curDebugLineDeltaValue = 0, curDebugLineDeltaRunCount = 0;
|
|
String debugLineSequenceStr;
|
|
int isFirstDebugLine = 1;
|
|
|
|
auto maybeEmitDebugLineRun = [&curDebugLineDeltaValue, &curDebugLineDeltaRunCount, &debugLineSequenceStr, &isFirstDebugLine]()
|
|
{
|
|
if (curDebugLineDeltaRunCount > 0)
|
|
{
|
|
for (int i=isFirstDebugLine; i<2; ++i)
|
|
{
|
|
int value = i ? curDebugLineDeltaValue : curDebugLineDeltaRunCount;
|
|
String encodedValue;
|
|
EncodeULEB32(value, encodedValue);
|
|
if (encodedValue.length() > 1)
|
|
debugLineSequenceStr += String("$") + encodedValue + "$";
|
|
else
|
|
debugLineSequenceStr += encodedValue;
|
|
}
|
|
|
|
curDebugLineDeltaRunCount = 0;
|
|
isFirstDebugLine = 0;
|
|
}
|
|
};
|
|
auto mangledLabelName = [&asmStmt](const StringImpl& labelName, int numericLabel) -> String
|
|
{
|
|
return StrFormat("%d", numericLabel);
|
|
//return String(".") + labelName;
|
|
//return StrFormat("%s_%p_%d", labelName.c_str(), asmStmt->mSource, asmStmt->mSrcStart); // suffix label name with location information to make it block-specific (since labels get external linkage)
|
|
};
|
|
|
|
typedef std::pair<String, int> LabelPair;
|
|
std::unordered_map<String, LabelPair> labelNames;
|
|
|
|
// pre-scan instructions for label names
|
|
for (int iInst=0; iInst<asmInstCount; ++iInst)
|
|
{
|
|
auto instNode = asmStmt->mInstructions[iInst];
|
|
BfInlineAsmInstruction::AsmInst& asmInst = instNode->mAsmInst;
|
|
if (!asmInst.mLabel.empty())
|
|
{
|
|
if (labelNames.find(asmInst.mLabel) != labelNames.end())
|
|
{
|
|
Fail(StrFormat("Label \"%s\" already defined in asm block", asmInst.mLabel.c_str()), instNode, true);
|
|
failed = true;
|
|
}
|
|
else
|
|
{
|
|
String mangledLabel(mangledLabelName(asmInst.mLabel, instNode->GetSrcStart()));
|
|
labelNames[asmInst.mLabel] = LabelPair(mangledLabel, instNode->GetSrcStart());
|
|
asmInst.mLabel = mangledLabel;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int iInst=0; iInst<asmInstCount; ++iInst)
|
|
{
|
|
auto instNode = asmStmt->mInstructions[iInst];
|
|
BfInlineAsmInstruction::AsmInst& asmInst = instNode->mAsmInst;
|
|
|
|
bool hasLabel = !asmInst.mLabel.empty();
|
|
bool hasOpCode = !asmInst.mOpCode.empty();
|
|
|
|
if (hasLabel || hasOpCode) // check against blank lines
|
|
{
|
|
if (hasOpCode) // check against label-only lines (which still get written out, but don't do any other processing)
|
|
{
|
|
int argCount = (int)asmInst.mArgs.size();
|
|
|
|
// reasonable defaults for clobber info
|
|
int clobberCount = 1; // destination is usually first arg in Intel syntax
|
|
bool mayClobberMem = true; // we only care about this when it gets turned to false by GetClobbersForMnemonic (no operand form of the instruction clobbers mem)
|
|
|
|
// pseudo-ops
|
|
if (asmInst.mOpCode == "_emit")
|
|
{
|
|
asmInst.mOpCode = ".byte";
|
|
}
|
|
else
|
|
{
|
|
Array<int> opcodes;
|
|
if (!mCpu->GetOpcodesForMnemonic(asmInst.mOpCode, opcodes))
|
|
{
|
|
Fail(StrFormat("Unrecognized instruction mnemonic \"%s\"", asmInst.mOpCode.c_str()), instNode, true);
|
|
failed = true;
|
|
}
|
|
else
|
|
{
|
|
Array<int> implicitClobbers;
|
|
mCpu->GetClobbersForMnemonic(asmInst.mOpCode, argCount, implicitClobbers, clobberCount, mayClobberMem);
|
|
for (int iClobberReg : implicitClobbers)
|
|
{
|
|
String regName = CPURegisters::GetRegisterName(iClobberReg);
|
|
std::transform(regName.begin(), regName.end(), regName.begin(), ::tolower);
|
|
|
|
matchRegFunc(regName, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
String fakeLabel; // used when running label-using instructions through LLVM semantic pre-check
|
|
for (int iArg=0; iArg<argCount; ++iArg)
|
|
{
|
|
BfInlineAsmInstruction::AsmArg* arg = &asmInst.mArgs[iArg];
|
|
if (arg->mType == BfInlineAsmInstruction::AsmArg::ARGTYPE_IntReg)
|
|
{
|
|
bool isClobber = (iArg < clobberCount);
|
|
bool found = matchRegFunc(arg->mReg, isClobber);
|
|
|
|
if (!found)
|
|
{
|
|
StrToVarIndexMap::iterator it = strToVarIndexMap.find(arg->mReg);
|
|
if (it != strToVarIndexMap.end())
|
|
{
|
|
arg->mReg = StrFormat("$%d", it->second);
|
|
|
|
if (isClobber)
|
|
mayClobberMem = true;
|
|
|
|
found = true;
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < (int) mCurMethodState->mLocals.size(); i++)
|
|
{
|
|
auto& checkLocal = mCurMethodState->mLocals[i];
|
|
if (checkLocal.mName == arg->mReg)
|
|
{
|
|
BfIRValue testValue = checkLocal.mAddr;
|
|
|
|
llvmArgs.push_back(testValue);
|
|
paramTypes.push_back(testValue->getType());
|
|
|
|
arg->mReg = StrFormat("$%d", constraintCount);//CDH TODO does this need size qualifiers for "dword ptr $0" or whatever?
|
|
strToVarIndexMap[checkLocal.mName] = constraintCount;
|
|
|
|
constraintStr += "=*m,";
|
|
++constraintCount;
|
|
if (isClobber)
|
|
mayClobberMem = true;
|
|
|
|
// if you access a variable in asm, we suppress any warnings related to used or assigned, regardless of usage
|
|
checkLocal.mIsReadFrom = true;
|
|
checkLocal.mAssignedKind = BfLocalVarAssignKind_Unconditional;
|
|
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
auto labelIt = labelNames.find(arg->mReg);
|
|
if (labelIt != labelNames.end())
|
|
{
|
|
arg->mReg = labelIt->second.first;
|
|
if (labelIt->second.second <= instNode->GetSrcStart())
|
|
{
|
|
fakeLabel = arg->mReg;
|
|
arg->mReg += "b";
|
|
}
|
|
else
|
|
arg->mReg += "f";
|
|
}
|
|
else
|
|
{
|
|
Fail(StrFormat("Unrecognized variable \"%s\"", arg->mReg.c_str()), instNode, true);
|
|
failed = true;
|
|
}
|
|
}
|
|
}
|
|
else if (arg->mType == BfInlineAsmInstruction::AsmArg::ARGTYPE_FloatReg)
|
|
{
|
|
//CDH individual reg clobber is probably insufficient for fp regs since it's stack-based; without deeper knowledge of how individual instructions
|
|
// manipulate the FP stack, the safest approach is to clobber all FP regs as soon as one of them is involved
|
|
//bool isClobber = (iArg == 0); // destination is first arg in Intel syntax
|
|
//bool found = matchRegFunc(StrFormat("st%d", arg->mInt), isClobber);
|
|
//BF_ASSERT(found);
|
|
for (int iRegCheck=0; iRegCheck<8; ++iRegCheck)
|
|
regClobberFlags |= (REGCLOBBERF_FPST0 << iRegCheck);
|
|
}
|
|
else if (arg->mType == BfInlineAsmInstruction::AsmArg::ARGTYPE_Memory)
|
|
{
|
|
bool isClobber = (iArg < clobberCount);
|
|
|
|
// check regs for clobber flags
|
|
/*
|
|
//CDH TODO do we need to set clobber flags for regs that are used *indirectly* like this? Actually I don't think so; commenting out for now
|
|
if (!arg->mReg.empty())
|
|
matchRegFunc(arg->mReg, isClobber);
|
|
if (!arg->mAdjReg.empty())
|
|
matchRegFunc(arg->mAdjReg, isClobber);
|
|
*/
|
|
|
|
if (isClobber)
|
|
mayClobberMem = true;
|
|
|
|
if (!arg->mMemberSuffix.empty())
|
|
{
|
|
//CDH TODO add member support once I know the right way to look up struct member offsets. Once we know the offset,
|
|
// add it to arg->mInt, and set ARGMEMF_ImmediateDisp if it's not set already (member support is just used as an offset)
|
|
Fail("Member suffix syntax is not yet supported", instNode, true);
|
|
failed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mayClobberMem)
|
|
hasMemoryClobber = true;
|
|
|
|
//debugLineSequenceStr += StrFormat("%d_", asmInst.mDebugLine);
|
|
int curDebugLine = asmInst.mDebugLine;
|
|
int debugLineDelta = (lastDebugLine > 0) ? curDebugLine - lastDebugLine : curDebugLine;
|
|
lastDebugLine = curDebugLine;
|
|
|
|
//String encodedDebugLineDelta;
|
|
//EncodeULEB32(debugLineDelta, encodedDebugLineDelta);
|
|
//debugLineSequenceStr += encodedDebugLineDelta + "_";
|
|
if (curDebugLineDeltaValue != debugLineDelta)
|
|
{
|
|
maybeEmitDebugLineRun();
|
|
curDebugLineDeltaValue = debugLineDelta;
|
|
}
|
|
++curDebugLineDeltaRunCount;
|
|
|
|
// run instruction through LLVM for better semantic errors (can be slow in debug; feel free to comment out this scopeData if it's intolerable; the errors will still be caught at compile time)
|
|
//if (false)
|
|
{
|
|
BfInlineAsmInstruction::AsmInst tempAsmInst(asmInst);
|
|
tempAsmInst.mLabel = fakeLabel;
|
|
for (auto & arg : tempAsmInst.mArgs)
|
|
{
|
|
if ((arg.mType == BfInlineAsmInstruction::AsmArg::ARGTYPE_IntReg) && !arg.mReg.empty() && (arg.mReg[0] == '$'))
|
|
{
|
|
// if we've rewritten a local variable instruction arg to use a $-prefixed input, we can't pass that to LLVM
|
|
// at this stage as it won't recognize it; the actual compilation would have changed all these to use actual
|
|
// memory operand syntax first. However, those changes all work down in LLVM to printIntelMemReference inside
|
|
// of X86AsmPrinter.cpp, and that always results in a [bracketed] memory access string no matter what, which
|
|
// means for our semantic checking purposes here it's sufficient to just use "[eax]" for all such cases,
|
|
// rather than go through an even more expensive setup & teardown process to use the AsmPrinter itself.
|
|
arg.mType = BfInlineAsmInstruction::AsmArg::ARGTYPE_Memory;
|
|
arg.mMemFlags = BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg;
|
|
arg.mReg = "eax";
|
|
}
|
|
}
|
|
|
|
String llvmError;
|
|
if (!mCpu->ParseInlineAsmInstructionLLVM(tempAsmInst.ToString(), llvmError))
|
|
{
|
|
Fail(StrFormat("Inline asm error: %s", llvmError.c_str()), instNode, true);
|
|
failed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
dstAsmText += asmInst.ToString();
|
|
dstAsmText += "\n";
|
|
}
|
|
}
|
|
|
|
maybeEmitDebugLineRun(); // leftovers
|
|
|
|
if (failed)
|
|
{
|
|
RestoreScopeState(&prevScope);
|
|
return;
|
|
}
|
|
|
|
// prepare constraints/clobbers
|
|
{
|
|
for (int iRegCheck = 0; regClobberNames[iRegCheck] != nullptr; ++iRegCheck)
|
|
{
|
|
if (regClobberFlags & (1 << iRegCheck))
|
|
clobberStr += StrFormat("~{%s},", regClobberNames[iRegCheck]);
|
|
}
|
|
for (int iRegCheck=0; iRegCheck<8; ++iRegCheck)
|
|
{
|
|
if (regClobberFlags & (REGCLOBBERF_XMM0 << iRegCheck))
|
|
clobberStr += StrFormat("~{xmm%d},", iRegCheck);
|
|
if (regClobberFlags & (REGCLOBBERF_FPST0 << iRegCheck))
|
|
clobberStr += StrFormat("~{fp%d},~{st(%d)},", iRegCheck, iRegCheck); // both fp# and st(#) are listed in X86RegisterInfo.td
|
|
if (regClobberFlags & (REGCLOBBERF_MM0 << iRegCheck))
|
|
clobberStr += StrFormat("~{mm%d},", iRegCheck);
|
|
}
|
|
|
|
// add wrapping instructions to preserve certain regs (e.g. ESI), due to LLVM bypassing register allocator when choosing a base register
|
|
//CDH TODO currently I'm only shielding against ESI stompage; people generally know not to mess with ESP & EBP, but ESI is still a "general" reg and should be allowed to be clobbered
|
|
//if (regClobberFlags & REGCLOBBERF_ESI)
|
|
//{
|
|
//CDH TODO Bah! This doesn't actually work, because if you do any local variable access after mutating ESI, the variable substitution could have generated a base address dependency
|
|
// on ESI which will not expect it to have changed, e.g. "mov esi, var\ninc esi\nmov var, esi\n" dies if "var" gets internally rewritten to "[esi + displacement]". What to do? Hmm.
|
|
//dstAsmText = String("push esi\n") + dstAsmText + String("pop esi\n");
|
|
//}
|
|
|
|
if (hasMemoryClobber)
|
|
clobberStr += "~{memory},";
|
|
clobberStr += "~{dirflag},~{fpsr},~{flags}";
|
|
|
|
constraintStr += clobberStr;
|
|
}
|
|
|
|
bool wantsDIData = (mBfIRBuilder->DbgHasInfo()) && (!mCurMethodInstance->mIsUnspecialized) && (mHasFullDebugInfo);
|
|
if (wantsDIData)
|
|
{
|
|
static int sVarNum = 0;
|
|
String varName(StrFormat("__asmLines_%d.%s", ++sVarNum, debugLineSequenceStr.c_str()));
|
|
|
|
auto varType = GetPrimitiveType(BfTypeCode_Int32);
|
|
|
|
auto allocaInst = mBfIRBuilder->CreateAlloca(varType->mLLVMType, 0, varName + ".addr");
|
|
allocaInst->setAlignment(varType->mAlign);
|
|
//paramVar->mAddr = allocaInst;
|
|
|
|
auto varValue = GetConstValue(0, varType);
|
|
|
|
auto diVariable = mDIBuilder->createAutoVariable(mCurMethodState->mCurScope->mDIScope,
|
|
varName.c_str(), mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, varType->mDIType/*, true*/);
|
|
|
|
//auto varValue = llvm::ConstantInt::getTrue(*mLLVMContext);
|
|
//auto varValue = GetDefaultValue(varType);
|
|
//auto varValue = CreateGlobalConstValue(varName, llvm::ConstantInt::getTrue(*mLLVMContext), true);
|
|
//auto varValue = AllocGlobalVariable(*mIRModule, diType->getType(), false, GlobalValue::ExternalLinkage, llvm::ConstantInt::getTrue(*mLLVMContext), varName.c_str());
|
|
|
|
//auto diVariable = mDIBuilder->createGlobalVariable(mCurMethodState->mCurScope->mDIScope, varName.c_str(), "", mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, diType, false, varValue);
|
|
|
|
//BasicBlock* block = mBfIRBuilder->GetInsertBlock();
|
|
//auto declareVar = mDIBuilder->insertDeclare(varValue, diVariable, mBfIRBuilder->GetInsertBlock());
|
|
auto declareVar = mDIBuilder->insertDeclare(allocaInst, diVariable, mDIBuilder->createExpression(),
|
|
mIRBuilder->getCurrentDebugLocation(), mBfIRBuilder->GetInsertBlock());
|
|
//auto declareVar = mDIBuilder->insertDbgValueIntrinsic(varValue, 0, diVariable, mBfIRBuilder->GetInsertBlock());
|
|
declareVar->setDebugLoc(mIRBuilder->getCurrentDebugLocation());
|
|
}
|
|
|
|
/*
|
|
BfIRValue testValue = NULL;
|
|
for (int i = 0; i < (int) mCurMethodState->mLocals.size(); i++)
|
|
{
|
|
auto& checkLocal = mCurMethodState->mLocals[i];
|
|
if (checkLocal.mName == "i")
|
|
{
|
|
testValue = checkLocal.mAddr;
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
//BF_ASSERT((testValue != NULL) && "Need local variable \"i\"");
|
|
//if (testValue != NULL)
|
|
{
|
|
//Type* voidPtrType = Type::getInt8PtrTy(*mLLVMContext);
|
|
//if (mContext->mAsmObjectCheckFuncType == NULL)
|
|
//{
|
|
//Array<Type*> paramTypes;
|
|
//paramTypes.push_back(voidPtrType);
|
|
//mContext->mAsmObjectCheckFuncType = FunctionType::get(Type::getVoidTy(*mLLVMContext), paramTypes, false);
|
|
//}
|
|
FunctionType* funcType = FunctionType::get(Type::getVoidTy(*mLLVMContext), paramTypes, false);
|
|
|
|
//SizedArray<Value*, 1> llvmArgs;
|
|
//llvmArgs.push_back(testValue);
|
|
|
|
//CDH REMOVE NOTE
|
|
//generates IR (e.g.):
|
|
// call void asm sideeffect "#4\0Anop\0Anop\0Amovl %eax, %eax\0Anop\0Anop", "~{cc},~{dirflag},~{fpsr},~{flags},~{eax}"() #0, !dbg !492
|
|
|
|
static int asmIdx = 0;
|
|
asmIdx++;
|
|
String asmStr = StrFormat("#%d\n", asmIdx) +
|
|
//"nop\nnop\nmovl $0, %eax\nincl %eax\nmovl %eax, $0\nmovl $$0, %ecx\nmovl $$0, %esp\nnop\nnop";
|
|
//"nop\nnop\nmovl ($0), %eax\nincl %eax\nmovl %eax, ($0)\nmovl $$0, %ecx\nmovl $$0, %esp\nnop\nnop";
|
|
//"nop\nnop\nmovl %eax, %eax\nmovl $$0, %ecx\nmovl $$0, %esp\nnop\nnop";
|
|
//"nop\nnop\nmovl %eax, %eax\nnop\nnop";
|
|
//"nop\nnop\n.byte 0x0F\n.byte 0xC7\n.byte 0xF0\nmov eax, 7\nmov ecx, 0\ncpuid\nmov eax, dword ptr $0\ninc eax\nmov dword ptr $0, eax\nmov ecx, 0\nmov esp, 0\nnop\nnop"; // rdrand test
|
|
dstAsmText;
|
|
|
|
llvm::InlineAsm* inlineAsm = llvm::InlineAsm::get(funcType,
|
|
//asmStr.c_str(), "~{r},~{cc},~{dirflag},~{fpsr},~{flags},~{eax},~{memory},~{esi},~{esp}", true,
|
|
//asmStr.c_str(), "~{cc},~{dirflag},~{fpsr},~{flags},~{memory},~{ecx},~{esp}", true,
|
|
//DOES NOT WORK (mem not written back to from reg:
|
|
//asmStr.c_str(), "+r,~{cc},~{dirflag},~{fpsr},~{flags},~{memory},~{eax},~{ecx},~{esp}", true,
|
|
//asmStr.c_str(), "+rm,~{cc},~{dirflag},~{fpsr},~{flags},~{memory},~{eax},~{ecx},~{esp}", true,
|
|
asmStr.c_str(), constraintStr.c_str(), true,
|
|
false, /*llvm::InlineAsm::AD_ATT*/llvm::InlineAsm::AD_Intel);
|
|
|
|
llvm::CallInst* callInst = mIRBuilder->CreateCall(inlineAsm, llvmArgs);
|
|
//llvm::CallInst* callInst = mIRBuilder->CreateCall(inlineAsm);
|
|
|
|
callInst->addAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::NoUnwind);
|
|
}
|
|
|
|
RestoreScopeState(&prevScope);
|
|
|
|
#endif
|
|
} |