#include "BeIRCodeGen.h"
#include "../Compiler/BfIRCodeGen.h"
#include "BeDbgModule.h"
#include "BeefySysLib/util/BeefPerf.h"
#include "llvm/IR/DIBuilder.h"

#include "BeefySysLib/util/AllocDebug.h"
#include "BeefySysLib/util/Hash.h"
#include "BeModule.h"
#include "BeContext.h"
#include "../Compiler/CeMachine.h"

#ifdef _DEBUG
#define BE_EXTRA_CHECKS
#endif

USING_NS_BF;

//#define CODEGEN_TRACK

#ifdef CODEGEN_TRACK
#include "../Compiler/MemReporter.h"
MemReporter gBEMemReporter;
CritSect gBEMemReporterCritSect;
int gBEMemReporterSize = 0;

#define BE_MEM_START \
	int startPos = mStream->GetReadPos();

#define BE_MEM_END(name) \
	gBEMemReporter.Add(name, mStream->GetReadPos() - startPos);

static const char* gIRCmdNames[] =
{
	"Module_Start",
	"Module_SetTargetTriple",
	"Module_AddModuleFlag",
	"WriteIR",
	"SetType",
	"SetInstType",
	"PrimitiveType",
	"CreateStruct",
	"StructSetBody",
	"Type",
	"TypeInst",
	"TypeInstPtr",
	"GetType",
	"GetPointerToFuncType",
	"GetPointerToType",
	"GetSizedArrayType",
	"CreateConstStruct",
	"CreateConstStructZero",
	"CreateConstArray",
	"CreateConstString",
	"SetName",
	"CreateUndefValue",
	"NumericCast",
	"CmpEQ",
	"CmpNE",
	"CmpSLT",
	"CmpULT",
	"CmpSLE",
	"CmpULE",
	"CmpSGT",
	"CmpUGT",
	"CmpSGE",
	"CmpUGE",
	"Add",
	"Sub",
	"Mul",
	"SDiv",
	"UDiv",
	"SRem",
	"URem",
	"And",
	"Or",
	"Xor",
	"Shl",
	"AShr",
	"LShr",
	"Neg",
	"Not",
	"BitCast",
	"PtrToInt",
	"IntToPtr",
	"InboundsGEP1_32",
	"InboundsGEP2_32",
	"InBoundsGEP1",
	"InBoundsGEP2",
	"IsNull",
	"IsNotNull",
	"ExtractValue",
	"InsertValue",
	"Alloca",
	"AllocaArray",
	"SetAllocaAlignment",
	"SetAllocaNoChkStkHint",
	"SetAllocaForceMem",
	"LifetimeStart",
	"LifetimeEnd",
	"LifetimeExtend",
	"ValueScopeStart",
	"ValueScopeRetain",
	"ValueScopeSoftEnd",
	"ValueScopeHardEnd",
	"Load",
	"AlignedLoad",
	"Store",
	"AlignedStore",
	"MemSet",
	"Fence",
	"StackSave",
	"StackRestore",
	"GlobalVariable",
	"GlobalVar_SetUnnamedAddr",
	"GlobalVar_SetInitializer",
	"GlobalVar_SetAlignment",
	"GlobalStringPtr",
	"CreateBlock",
	"MaybeChainNewBlock",
	"AddBlock",
	"DropBlocks",
	"MergeBlockDown",
	"SetInsertPoint",
	"SetInsertPointAtStart",
	"EraseFromParent",
	"DeleteBlock",
	"EraseInstFromParent",
	"CreateBr",
	"CreateBr_NoCollapse",
	"CreateCondBr",
	"MoveBlockToEnd",
	"CreateSwitch",
	"AddSwitchCase",
	"SetSwitchDefaultDest",
	"CreatePhi",
	"AddPhiIncoming",
	"GetIntrinsic",
	"CreateFunctionType",
	"CreateFunction",
	"EnsureFunctionPatchable",
	"RemapBindFunction",
	"SetActiveFunction",
	"CreateCall",
	"SetCallCallingConv",
	"SetFuncCallingConv",
	"SetTailCall",
	"SetCallAttribute",
	"CreateRet",
	"CreateRetVoid",
	"CreateUnreachable",
	"Call_AddAttribute",
	"Call_AddAttribute1",
	"Func_AddAttribute",
	"Func_AddAttribute1",
	"Func_SetParamName",
	"Func_DeleteBody",
	"Func_EraseFromParent",
	"Func_SetLinkage",
	"SaveDebugLocation",
	"RestoreDebugLocation",
	"ClearDebugLocation",
	"ClearDebugLocationInst",
	"UpdateDebugLocation",
	"SetCurrentDebugLocation",
	"Nop",
	"EnsureInstructionAt",
	"StatementStart",
	"ObjectAccessCheck",
	"DbgInit",
	"DbgFinalize",
	"DbgCreateCompileUnit",
	"DbgCreateFile",
	"ConstValueI64",
	"DbgGetCurrentLocation",
	"DbgSetType",
	"DbgSetInstType",
	"DbgGetType",
	"DbgGetTypeInst",
	"DbgTrackDITypes",
	"DbgCreateNamespace",
	"DbgCreateImportedModule",
	"DbgCreateBasicType",
	"DbgCreateStructType",
	"DbgCreateEnumerationType",
	"DbgCreatePointerType",
	"DbgCreateReferenceType",
	"DbgCreateConstType",
	"DbgCreateArtificialType",
	"DbgCreateArrayType",
	"DbgCreateReplaceableCompositeType",
	"DbgCreateForwardDecl",
	"DbgCreateSizedForwardDecl",
	"BeIRCmd_DbgSetTypeSize",
	"DbgReplaceAllUses",
	"DbgDeleteTemporary",
	"DbgMakePermanent",
	"CreateEnumerator",
	"DbgCreateMemberType",
	"DbgStaticCreateMemberType",
	"DbgCreateInheritance",
	"DbgCreateMethod",
	"DbgCreateFunction",
	"DbgCreateParameterVariable",
	"DbgCreateSubroutineType",
	"DbgCreateAutoVariable",
	"DbgInsertValueIntrinsic",
	"DbgInsertDeclare",
	"DbgLifetimeEnd",
	"DbgCreateGlobalVariable",
	"DbgCreateLexicalBlock",
	"DbgCreateLexicalBlockFile",
	"DbgCreateAnnotation"
};

BF_STATIC_ASSERT(BF_ARRAY_COUNT(gIRCmdNames) == BfIRCmd_COUNT);

#else
#define BE_MEM_START
#define BE_MEM_END(name)
#endif

#pragma warning(disable:4146)

#define CMD_PARAM(ty, name) ty name; Read(name);

BeIRCodeGen::BeIRCodeGen()
{
	mBfIRBuilder = NULL;
	mStream = NULL;
	mActiveFunction = NULL;
	mBeContext = NULL;
	mBeModule = NULL;
	mHasDebugLoc = false;
	mDebugging = false;
	mCmdCount = 0;
}

BeIRCodeGen::~BeIRCodeGen()
{
	BF_ASSERT(mSavedDebugLocs.size() == 0);
	delete mBeModule;
	delete mBeContext;
	delete mStream;
}

void BeIRCodeGen::Hash(BeHashContext& hashCtx)
{
// 	if (mBeModule->mModuleName == "IDE_IDEApp")
// 	{
// 		hashCtx.mDbgViz = true;
// 		NOP;
// 	}

	hashCtx.Mixin(mPtrSize);
	hashCtx.Mixin(mIsOptimized);
	if (mBeModule != NULL)
		mBeModule->Hash(hashCtx);

	Array<BeStructType*> structHashList;

	for (auto beType : mBeContext->mTypes)
	{
		if (!beType->IsStruct())
			continue;
		auto beStructType = (BeStructType*)beType;
		if (beStructType->mHashId != -1)
			continue;
		structHashList.Add(beStructType);
	}

	structHashList.Sort([](BeStructType* lhs, BeStructType* rhs)
	{
		return lhs->mName < rhs->mName;
	});

	for (auto beStructType : structHashList)
	{
		beStructType->HashReference(hashCtx);
	}
}

bool BeIRCodeGen::IsModuleEmpty()
{
	if (!mBeModule->mFunctions.IsEmpty())
		return false;
	if (!mBeModule->mGlobalVariables.IsEmpty())
		return false;
	return true;
}

void BeIRCodeGen::FatalError(const StringImpl& err)
{
	String failStr = "Fatal Error in Module: ";
	if (mBeModule != NULL)
		failStr += mBeModule->mModuleName;
	failStr += "\n";
	if (mBeModule != NULL)
	{
		BeDumpContext dumpCtx;

		if (mActiveFunction != NULL)
		{
			failStr += "Function: ";
			failStr += mActiveFunction->mName;
			failStr += "\n";
		}

		if (mBeModule->mCurDbgLoc != NULL)
		{
			failStr += "DbgLoc: ";
			dumpCtx.ToString(failStr, mBeModule->mCurDbgLoc);
			failStr += "\n";
		}
	}

	failStr += err;
	BF_FATAL(failStr);
}

void BeIRCodeGen::NotImpl()
{
	BF_FATAL("Not implemented");
}

BeType* BeIRCodeGen::GetBeType(BfTypeCode typeCode, bool& isSigned)
{
	isSigned = false;
	BeTypeCode beTypeCode = BeTypeCode_None;
	switch (typeCode)
	{
	case BfTypeCode_None:
		beTypeCode = BeTypeCode_None;
		break;
	case BfTypeCode_NullPtr:
		beTypeCode = BeTypeCode_NullPtr;
		break;
	case BfTypeCode_Boolean:
		beTypeCode = BeTypeCode_Boolean;
		break;
	case BfTypeCode_Int8:
		isSigned = true;
		beTypeCode = BeTypeCode_Int8;
		break;
	case BfTypeCode_UInt8:
	case BfTypeCode_Char8:
		beTypeCode = BeTypeCode_Int8;
		break;
	case BfTypeCode_Int16:
		isSigned = true;
		beTypeCode = BeTypeCode_Int16;
		break;
	case BfTypeCode_Char16:
	case BfTypeCode_UInt16:
		beTypeCode = BeTypeCode_Int16;
		break;
	case BfTypeCode_Int32:
		isSigned = true;
		beTypeCode = BeTypeCode_Int32;
		break;
	case BfTypeCode_UInt32:
	case BfTypeCode_Char32:
		beTypeCode = BeTypeCode_Int32;
		break;
	case BfTypeCode_Int64:
		isSigned = true;
		beTypeCode = BeTypeCode_Int64;
		break;
	case BfTypeCode_UInt64:
		beTypeCode = BeTypeCode_Int64;
		break;
	case BfTypeCode_IntPtr:
		BF_FATAL("Illegal");
		/*isSigned = true;
		if (mModule->mSystem->mPtrSize == 4)
		return llvm::Type::getInt32Ty(*mLLVMContext);
		else
		return llvm::Type::getInt64Ty(*mLLVMContext);*/
	case BfTypeCode_UIntPtr:
		BF_FATAL("Illegal");
		/*if (mModule->mSystem->mPtrSize == 4)
		return llvm::Type::getInt32Ty(*mLLVMContext);
		else
		return llvm::Type::getInt64Ty(*mLLVMContext);*/
	case BfTypeCode_Float:
		isSigned = true;
		beTypeCode = BeTypeCode_Float;
		break;
	case BfTypeCode_Double:
		isSigned = true;
		beTypeCode = BeTypeCode_Double;
		break;
	}

	return mBeContext->GetPrimitiveType(beTypeCode);
}

BeIRTypeEntry& BeIRCodeGen::GetTypeEntry(int typeId)
{
	BeIRTypeEntry& typeEntry = mTypes[typeId];
	if (typeEntry.mTypeId == -1)
		typeEntry.mTypeId = typeId;
	return typeEntry;
}

void BeIRCodeGen::FixValues(BeStructType* structType, CmdParamVec<BeValue*>& values)
{
	if (values.size() >= structType->mMembers.size())
		return;

	int readIdx = values.size() - 1;
	values.resize(structType->mMembers.size());
	for (int i = (int)values.size() - 1; i >= 0; i--)
	{
		if (mBeContext->AreTypesEqual(values[readIdx]->GetType(), structType->mMembers[i].mType))
		{
			values[i] = values[readIdx];
			readIdx--;
		}
		else if (structType->mMembers[i].mType->IsSizedArray())
		{
			auto beConst = mBeModule->mAlloc.Alloc<BeConstant>();
			beConst->mType = structType->mMembers[i].mType;
			values[i] = beConst;
		}
		else
		{
			FatalError("Malformed structure values");
		}
	}
}

void BeIRCodeGen::FixValues(BeStructType* structType, SizedArrayImpl<BeConstant*>& values)
{
	if (values.size() >= structType->mMembers.size())
		return;

	int readIdx = values.size() - 1;
	values.resize(structType->mMembers.size());
	for (int i = (int)values.size() - 1; i >= 0; i--)
	{
		if (mBeContext->AreTypesEqual(values[readIdx]->GetType(), structType->mMembers[i].mType))
		{
			values[i] = values[readIdx];
			readIdx--;
		}
		else if (structType->mMembers[i].mType->IsSizedArray())
		{
			auto beConst = mBeModule->mAlloc.Alloc<BeConstant>();
			beConst->mType = structType->mMembers[i].mType;
			values[i] = beConst;
		}
		else
		{
			FatalError("Malformed structure values");
		}
	}
}

void BeIRCodeGen::Init(const BfSizedArray<uint8>& buffer)
{
	BP_ZONE("BeIRCodeGen::Init");

	BF_ASSERT(mStream == NULL);
	mStream = new ChunkedDataBuffer();
	mStream->InitFlatRef(buffer.mVals, buffer.mSize);

#ifdef CODEGEN_TRACK
	AutoCrit autoCrit(gBEMemReporterCritSect);
	AutoMemReporter autoMemReporter(&gBEMemReporter, "BeIRCodeGen");
#endif

	//
	{
		BP_ZONE("BeIRCodeGen::ProcessBfIRData.HandleNextCmds");
		while (mStream->GetReadPos() < buffer.mSize)
		{
			if (mFailed)
				break;
			HandleNextCmd();
		}
	}

	BF_ASSERT((mFailed) || (mStream->GetReadPos() == buffer.mSize));
}

void BeIRCodeGen::Process()
{
	BP_ZONE("BeIRCodeGen::process");

	//mDebugging |= ((mBeModule->mDbgModule != NULL) && (mBeModule->mDbgModule->mFileName == "ClassQ"));
	if (mDebugging)
	{
		String dbgStr;
		dbgStr = mBeModule->ToString();
		OutputDebugStr(dbgStr);
	}

	mBeModule->DoInlining();

	if (mDebugging)
	{
		String dbgStr = "-------------- AFTER INLINING --------------\n";
		dbgStr += mBeModule->ToString();
		OutputDebugStr(dbgStr);
	}
}

void BeIRCodeGen::ProcessBfIRData(const BfSizedArray<uint8>& buffer)
{
	BP_ZONE("BeIRCodeGen::ProcessBfIRData");

	Init(buffer);
	Process();
}

BfTypeCode BeIRCodeGen::GetTypeCode(BeType * type, bool isSigned)
{
	switch (type->mTypeCode)
	{
	case BeTypeCode_Int8:
		return (isSigned) ? BfTypeCode_Int8 : BfTypeCode_UInt8;
	case BeTypeCode_Int16:
		return (isSigned) ? BfTypeCode_Int16 : BfTypeCode_UInt16;
	case BeTypeCode_Int32:
		return (isSigned) ? BfTypeCode_Int32 : BfTypeCode_UInt32;
	case BeTypeCode_Int64:
		return (isSigned) ? BfTypeCode_Int64 : BfTypeCode_UInt64;
	case BeTypeCode_Float:
		return BfTypeCode_Float;
	case BeTypeCode_Double:
		return BfTypeCode_Double;
	default:
		return BfTypeCode_None;
	}
}

void BeIRCodeGen::SetResult(int id, BeValue* value)
{
	BeIRCodeGenEntry entry;
	entry.mKind = BeIRCodeGenEntryKind_Value;
	entry.mBeValue = value;
	mResults.TryAdd(id, entry);
}

void BeIRCodeGen::SetResult(int id, BeType* type)
{
	BeIRCodeGenEntry entry;
	entry.mKind = BeIRCodeGenEntryKind_Type;
	entry.mBeType = type;
	mResults.TryAdd(id, entry);
}

void BeIRCodeGen::SetResult(int id, BeBlock* value)
{
	BeIRCodeGenEntry entry;
	entry.mKind = BeIRCodeGenEntryKind_Block;
	entry.mBeBlock = value;
	mResults.TryAdd(id, entry);
}

void BeIRCodeGen::SetResult(int id, BeMDNode* md)
{
	BeIRCodeGenEntry entry;
	entry.mKind = BeIRCodeGenEntryKind_Metadata;
	entry.mBeMetadata = md;
	mResults.TryAdd(id, entry);
}

int64 BeIRCodeGen::ReadSLEB128()
{
	int64 val = 0;
	int64 shift = 0;
	uint8 byteVal;
	do
	{
		byteVal = mStream->Read();
		val |= ((int64)(byteVal & 0x7f)) << shift;
		shift += 7;
	} while (byteVal >= 128);
	// Sign extend negative numbers.
	if ((byteVal & 0x40) && (shift < 64))
		val |= (-1ULL) << shift;
	return val;
}

void BeIRCodeGen::Read(StringImpl& str)
{
	BE_MEM_START;
	int len = (int)ReadSLEB128();
	str.Append('?', len);
	mStream->Read((void*)str.c_str(), len);
	BE_MEM_END("String");
}

void BeIRCodeGen::Read(int& i)
{
	BE_MEM_START;
	i = (int)ReadSLEB128();
	BE_MEM_END("int");
}

void BeIRCodeGen::Read(int64& i)
{
	BE_MEM_START;
	i = ReadSLEB128();
	BE_MEM_END("int64");
}

void BeIRCodeGen::Read(Val128& i)
{
	i.mLow = (uint64)ReadSLEB128();
	i.mHigh = (uint64)ReadSLEB128();
}

void BeIRCodeGen::Read(bool& val)
{
	BE_MEM_START;
	val = mStream->Read() != 0;
	BE_MEM_END("bool");
}

void BeIRCodeGen::Read(int8& val)
{
	val = mStream->Read();
}

void BeIRCodeGen::Read(BeIRTypeEntry*& type)
{
	BE_MEM_START;
	int typeId = (int)ReadSLEB128();
	type = &GetTypeEntry(typeId);
	BE_MEM_END("BeIRTypeEntry");
}

void BeIRCodeGen::Read(BeType*& beType)
{
	BE_MEM_START;

	BfIRType::TypeKind typeKind = (BfIRType::TypeKind)mStream->Read();
	if (typeKind == BfIRType::TypeKind::TypeKind_None)
	{
		beType = NULL;
		BE_MEM_END("BeType");
		return;
	}

	if (typeKind == BfIRType::TypeKind::TypeKind_Stream)
	{
		int streamId = (int)ReadSLEB128();
		if (streamId == -1)
		{
			beType = NULL;
			BE_MEM_END("BeType");
			return;
		}
		auto& result = mResults[streamId];
		BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Type);
		beType = result.mBeType;
		BE_MEM_END("BeType");
		return;
	}

	if (typeKind == BfIRType::TypeKind::TypeKind_SizedArray)
	{
		CMD_PARAM(BeType*, elementType);
		CMD_PARAM(int, length);
		beType = mBeContext->CreateSizedArrayType(elementType, length);
		return;
	}

	int typeId = (int)ReadSLEB128();

	if (typeKind == BfIRType::TypeKind::TypeKind_TypeCode)
	{
		bool isSigned = false;
		beType = GetBeType((BfTypeCode)typeId, isSigned);
		return;
	}

	auto& typeEntry = GetTypeEntry(typeId);
	if (typeKind == BfIRType::TypeKind::TypeKind_TypeId)
		beType = typeEntry.mBeType;
	else if (typeKind == BfIRType::TypeKind::TypeKind_TypeInstId)
		beType = typeEntry.mInstBeType;
	else if (typeKind == BfIRType::TypeKind::TypeKind_TypeInstPtrId)
		beType = mBeContext->GetPointerTo(typeEntry.mInstBeType);
	BE_MEM_END("BeType");
}

void BeIRCodeGen::Read(BeFunctionType*& beType)
{
	BE_MEM_START;
	int streamId = (int)ReadSLEB128();
	auto& result = mResults[streamId];
	BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Type);
	beType = (BeFunctionType*)result.mBeType;
	BE_MEM_END("BeFunctionType");
}

void BeIRCodeGen::Read(BeValue*& beValue)
{
	BE_MEM_START;

	BfIRParamType paramType = (BfIRParamType)mStream->Read();
	if (paramType == BfIRParamType_None)
	{
		beValue = NULL;
		BE_MEM_END("ParamType_None");
	}
	else if (paramType == BfIRParamType_Const)
	{
		BfTypeCode typeCode = (BfTypeCode)mStream->Read();
		BfConstType constType = (BfConstType)typeCode;
		if (constType == BfConstType_GlobalVar)
		{
			CMD_PARAM(int, streamId);
			if (streamId == -1)
			{
				int streamId = mCmdCount++;

				CMD_PARAM(BeType*, varType);
				CMD_PARAM(bool, isConstant);
				BfIRLinkageType linkageType = (BfIRLinkageType)mStream->Read();
				CMD_PARAM(BeConstant*, initializer);
				CMD_PARAM(String, name);
				CMD_PARAM(bool, isTLS);

				BF_ASSERT(varType != NULL);

				auto globalVariable = mBeModule->mGlobalVariables.Alloc();
				globalVariable->mModule = mBeModule;
				globalVariable->mType = varType;
				globalVariable->mIsConstant = isConstant;
				globalVariable->mLinkageType = linkageType;
				globalVariable->mInitializer = initializer;
				globalVariable->mName = name;
				globalVariable->mIsTLS = isTLS;
				globalVariable->mAlign = varType->mAlign;
				globalVariable->mUnnamedAddr = false;
				globalVariable->mStorageKind = BfIRStorageKind_Normal;
				if (initializer != NULL)
					BF_ASSERT(varType->mAlign > 0);

				SetResult(streamId, globalVariable);
				beValue = globalVariable;
			}
			else
				beValue = GetBeValue(streamId);
			beValue->mRefCount++;
			BE_MEM_END("ParamType_Const_GlobalVar");
			return;
		}
		else if ((constType == BfConstType_BitCast) || (constType == BfConstType_BitCastNull))
		{
			CMD_PARAM(BeConstant*, target);
			CMD_PARAM(BeType*, toType);

			auto castedVal = mBeModule->mAlloc.Alloc<BeCastConstant>();
			castedVal->mInt64 = target->mInt64;
			castedVal->mType = toType;
			castedVal->mTarget = target;
			BF_ASSERT(target->GetType() != NULL);
			BF_ASSERT(!target->GetType()->IsComposite());
			beValue = castedVal;
			BE_MEM_END("ParamType_Const_BitCast");
			return;
		}
		else if (constType == BfConstType_GEP32_1)
		{
			CMD_PARAM(BeConstant*, target);
			CMD_PARAM(int, idx0);

			BF_ASSERT(target->GetType()->IsPointer());
			auto gepConstant = mBeModule->mAlloc.Alloc<BeGEP1Constant>();
			gepConstant->mTarget = target;
			gepConstant->mIdx0 = idx0;

			beValue = gepConstant;
			BE_MEM_END("ParamType_Const_GEP32_1");
			return;
		}
		else if (constType == BfConstType_GEP32_2)
		{
			CMD_PARAM(BeConstant*, target);
			CMD_PARAM(int, idx0);
			CMD_PARAM(int, idx1);

			BF_ASSERT(target->GetType()->IsPointer());
			auto gepConstant = mBeModule->mAlloc.Alloc<BeGEP2Constant>();
			gepConstant->mTarget = target;
			gepConstant->mIdx0 = idx0;
			gepConstant->mIdx1 = idx1;

			beValue = gepConstant;
			BE_MEM_END("ParamType_Const_GEP32_2");
			return;
		}
		else if (constType == BfConstType_ExtractValue)
		{
			CMD_PARAM(BeConstant*, target);
			CMD_PARAM(int, idx0);

			auto gepConstant = mBeModule->mAlloc.Alloc<BeExtractValueConstant>();
			gepConstant->mTarget = target;
			gepConstant->mIdx0 = idx0;

			beValue = gepConstant;
			BE_MEM_END("ParamType_Const_ExtractValue");
			return;
		}
		else if (constType == BfConstType_PtrToInt)
		{
			CMD_PARAM(BeConstant*, target);
			BfTypeCode toTypeCode = (BfTypeCode)mStream->Read();
			BF_ASSERT(target->GetType()->IsPointer());

			bool isSigned = false;
			BeType* toType = GetBeType(toTypeCode, isSigned);

			auto castedVal = mBeModule->mAlloc.Alloc<BeCastConstant>();
			castedVal->mInt64 = target->mInt64;
			castedVal->mType = toType;
			castedVal->mTarget = target;
			BF_ASSERT(target->GetType() != NULL);
			beValue = castedVal;
			BE_MEM_END("ParamType_Const_PtrToInt");
			return;
		}
		else if (constType == BfConstType_IntToPtr)
		{
			CMD_PARAM(BeConstant*, target);
			CMD_PARAM(BeType*, toType);

			auto castedVal = mBeModule->mAlloc.Alloc<BeCastConstant>();
			castedVal->mInt64 = target->mInt64;
			castedVal->mType = toType;
			castedVal->mTarget = target;
			BF_ASSERT(target->GetType() != NULL);
			BF_ASSERT(!target->GetType()->IsComposite());
			BF_ASSERT(toType->IsPointer());
			beValue = castedVal;
			BE_MEM_END("ParamType_Const_IntToPtr");
			return;
		}
		else if (constType == BfConstType_AggZero)
		{
			CMD_PARAM(BeType*, type);
			auto beConst = mBeModule->mAlloc.Alloc<BeConstant>();
			beConst->mType = type;
			beValue = beConst;
			BE_MEM_END("ParamType_Const_AggZero");
			return;
		}
		else if (constType == BfConstType_Agg)
		{
			CMD_PARAM(BeType*, type);
			CMD_PARAM(CmdParamVec<BeConstant*>, values);

			if (type->IsSizedArray())
			{
				auto arrayType = (BeSizedArrayType*)type;
				int fillCount = (int)(arrayType->mLength - values.size());
				if (fillCount > 0)
				{
					auto lastValue = values.back();
					for (int i = 0; i < fillCount; i++)
						values.push_back(lastValue);
				}
			}
			else if (type->IsVector())
			{
				auto vecType = (BeVectorType*)type;
				int fillCount = (int)(vecType->mLength - values.size());
				if (fillCount > 0)
				{
					auto lastValue = values.back();
					for (int i = 0; i < fillCount; i++)
						values.push_back(lastValue);
				}
			}
			else
			{
				BF_ASSERT(type->IsStruct());
			}

			auto constStruct = mBeModule->mOwnedValues.Alloc<BeStructConstant>();
			constStruct->mType = type;

			if (type->IsStruct())
				FixValues((BeStructType*)type, values);

			for (int i = 0; i < (int)values.size(); i++)
			{
				auto val = values[i];
				BeConstant* constant = BeValueDynCast<BeConstant>(val);

				if (type->IsSizedArray())
				{
					auto arrayType = (BeSizedArrayType*)type;
					auto memberType = constant->GetType();
					if (memberType != arrayType->mElementType)
						Fail("ConstAgg array member type mismatch");
				}
				else if (type->IsVector())
				{
					auto vecType = (BeVectorType*)type;
					auto memberType = constant->GetType();
					if (memberType != vecType->mElementType)
						Fail("ConstAgg vector member type mismatch");
				}
				else
				{
					BF_ASSERT(type->IsStruct());
					auto structType = (BeStructType*)type;
					auto valType = constant->GetType();
					if (structType->mIsOpaque)
					{
						Fail("ConstAgg with opaque struct");
					}
					else if (valType != structType->mMembers[i].mType)
					{
						if (valType->IsSizedArray())
						{
							auto valSizedType = (BeSizedArrayType*)valType;
							if (valSizedType->mSize == 0)
							{
								constant->mType = structType->mMembers[i].mType;
								constStruct->mMemberValues.Add(constant);
								continue;
							}
						}

						Fail("ConstAgg struct member type mismatch");
					}
				}

				constStruct->mMemberValues.Add(constant);
			}

			beValue = constStruct;

			BE_MEM_END("ParamType_Const_Array");
			return;
		}
		else if (constType == BfConstType_ArrayZero8)
		{
			CMD_PARAM(int, count);
			auto beType = mBeContext->CreateSizedArrayType(mBeContext->GetPrimitiveType(BeTypeCode_Int8), count);
			auto beConst = mBeModule->mAlloc.Alloc<BeConstant>();
			beConst->mType = beType;
			beValue = beConst;
			return;
		}
		else if (constType == BfConstType_Undef)
		{
			CMD_PARAM(BeType*, type);
			auto constUndef = mBeModule->mOwnedValues.Alloc<BeUndefConstant>();
			constUndef->mType = type;
			beValue = constUndef;
			return;
		}
		else if (constType == BfConstType_TypeOf)
		{
			CMD_PARAM(BeType*, type);	
			beValue = mReflectDataMap[type];
			BF_ASSERT(beValue != NULL);
			return;
		}
		else if (constType == BfConstType_TypeOf_Comptime)
		{
			CMD_PARAM(BeType*, typeType);
			CMD_PARAM(int, bfTypeId);
			auto beConst = mBeModule->mAlloc.Alloc<BeTypeOfConstant>();
			beConst->mType = typeType;
			beConst->mBfTypeId = bfTypeId;
			beValue = beConst;
			return;
		}
		else if (constType == BfConstType_TypeOf_WithData)
		{
			CMD_PARAM(BeType*, type);
			CMD_PARAM(BeValue*, value);
			mReflectDataMap[type] = value;
			beValue = value;
			return;
		}

		bool isSigned = false;
		BeType* llvmConstType = GetBeType(typeCode, isSigned);

		if (typeCode == BfTypeCode_Float)
		{
			float f;
			mStream->Read(&f, sizeof(float));
			beValue = mBeModule->GetConstant(llvmConstType, f);
			BE_MEM_END("ParamType_Single");
		}
		else if (typeCode == BfTypeCode_Double)
		{
			double d;
			mStream->Read(&d, sizeof(double));
			beValue = mBeModule->GetConstant(llvmConstType, d);
			BE_MEM_END("ParamType_Const_Double");
		}
		else if (typeCode == BfTypeCode_Boolean)
		{
			CMD_PARAM(bool, boolVal);
			beValue = mBeModule->GetConstant(llvmConstType, boolVal);
			BE_MEM_END("ParamType_Const_Boolean");
		}
		else if (typeCode == BfTypeCode_None)
		{
			beValue = NULL;
			BE_MEM_END("ParamType_Const_None");
		}
		else if (typeCode == BfTypeCode_NullPtr)
		{
			CMD_PARAM(BeType*, nullType);
			beValue = mBeModule->GetConstantNull((BePointerType*)nullType);
			BE_MEM_END("ParamType_Const_NullPtr");
		}
		else if (BfIRBuilder::IsInt(typeCode))
		{
			int64 intVal = ReadSLEB128();
			auto constVal = mBeModule->GetConstant(llvmConstType, intVal);
			beValue = constVal;
			BE_MEM_END("ParamType_Const_Int");
		}
		else
		{
			BF_FATAL("Unhandled");
		}
	}
	else if (paramType == BfIRParamType_Arg)
	{
		int argIdx = mStream->Read();
		beValue = mBeModule->GetArgument(argIdx);
		BE_MEM_END("ParamType_Arg");
	}
	else if (paramType == BfIRParamType_StreamId_Abs8)
	{
		int cmdId = mStream->Read();
		auto& result = mResults[cmdId];
		BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Value);
		beValue = result.mBeValue;
		BE_MEM_END("ParamType_StreamId");
	}
	else if (paramType == BfIRParamType_StreamId_Rel)
	{
		int cmdId = mCmdCount - (int)ReadSLEB128();
		auto& result = mResults[cmdId];
		BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Value);
		beValue = result.mBeValue;
		BE_MEM_END("ParamType_StreamId");
	}
	else
	{
		int cmdId = mCmdCount - (paramType - BfIRParamType_StreamId_Back1) - 1;
		auto& result = mResults[cmdId];
		BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Value);
		beValue = result.mBeValue;
		BE_MEM_END("ParamType_StreamId");
	}

	if (beValue != NULL)
		beValue->mRefCount++;
}

void BeIRCodeGen::Read(BeConstant*& llvmConstant)
{
	BE_MEM_START;
	BeValue* value;
	Read(value);
	if (value == NULL)
	{
		llvmConstant = NULL;
	}
	else
	{
		BF_ASSERT(BeValueDynCast<BeConstant>(value));
		llvmConstant = (BeConstant*)value;
	}
	BE_MEM_END("BeConstant");
}

void BeIRCodeGen::Read(BeFunction*& beFunc)
{
	BE_MEM_START;
	int streamId = (int)ReadSLEB128();
	if (streamId == -1)
	{
		beFunc = NULL;
		return;
	}
	auto& result = mResults[streamId];
	BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Value);
	BF_ASSERT(BeValueDynCast<BeFunction>(result.mBeValue));
	beFunc = (BeFunction*)result.mBeValue;
	BE_MEM_END("BeFunction");

	beFunc->mRefCount++;
}

void BeIRCodeGen::Read(BeBlock*& beBlock)
{
	BE_MEM_START;
	int streamId = (int)ReadSLEB128();
	auto& result = mResults[streamId];
	BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Block);
	beBlock = (BeBlock*)result.mBeType;
	BE_MEM_END("BeBlock");

	beBlock->mRefCount++;
}

void BeIRCodeGen::Read(BeMDNode*& llvmMD)
{
	BE_MEM_START;
	int streamId = (int)ReadSLEB128();
	if (streamId == -1)
	{
		llvmMD = NULL;
		return;
	}
	auto& result = mResults[streamId];
	BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Metadata);
	llvmMD = result.mBeMetadata;
	BE_MEM_END("BeMDNode");

	if (llvmMD != NULL)
		llvmMD->mRefCount++;
}

void BeIRCodeGen::HandleNextCmd()
{	
	if (mFailed)
	{
		mStream->SetReadPos(mStream->GetSize());
		return;
	}

	int curId = mCmdCount;

	BfIRCmd cmd = (BfIRCmd)mStream->Read();
	mCmdCount++;

#ifdef CODEGEN_TRACK
	gBEMemReporter.BeginSection(gIRCmdNames[cmd]);
	gBEMemReporter.Add(1);
#endif

	switch (cmd)
	{
	case BfIRCmd_Module_Start:
		{
			CMD_PARAM(String, moduleName);
			CMD_PARAM(int, ptrSize);
			CMD_PARAM(bool, isOptimized);

			BF_ASSERT(mBeModule == NULL);
			mPtrSize = ptrSize;
			mIsOptimized = isOptimized;
			mBeContext = new BeContext();
			mBeModule = new BeModule(moduleName, mBeContext);
			mBeModule->mBeIRCodeGen = this;
			mBeContext->mPointerSize = ptrSize;

			for (auto constInt : mConfigConsts)
			{
				auto constVal = mBeModule->mAlloc.Alloc<BeConstant>();
				constVal->mType = mBeContext->GetPrimitiveType(BeTypeCode_Int32);
				constVal->mInt64 = constInt;
				mBeModule->mConfigConsts32.Add(constVal);

				constVal = mBeModule->mAlloc.Alloc<BeConstant>();
				constVal->mType = mBeContext->GetPrimitiveType(BeTypeCode_Int64);
				constVal->mInt64 = constInt;
				mBeModule->mConfigConsts64.Add(constVal);
			}
		}
		break;
	case BfIRCmd_Module_SetTargetTriple:
		{
			CMD_PARAM(String, targetTriple);
			CMD_PARAM(String, targetCPU);
			mBeModule->mTargetTriple = targetTriple;
			mBeModule->mTargetCPU = targetCPU;
		}
		break;
	case BfIRCmd_Module_AddModuleFlag:
		{
			CMD_PARAM(String, flag);
			CMD_PARAM(int, val);
			//mBeModule->addModuleFlag(BeModule::Warning, flag, val);
		}
		break;
	case BfIRCmd_WriteIR:
		{
			/*CMD_PARAM(String, fileName);
			std::error_code ec;
			Beraw_fd_ostream outStream(fileName.c_str(), ec, Besys::fs::OpenFlags::F_Text);
			if (ec)
			{
				Fail("Failed writing IR '" + fileName + "': " + ec.message());
			}
			else
				mBeModule->print(outStream, NULL);*/
		}
		break;
	case BfIRCmd_Abort:
		{
			Fail("Stream aborted");			
		}
		break;
	case BfIRCmd_SetType:
		{
			CMD_PARAM(int, typeId);
			CMD_PARAM(BeType*, type);
			auto& typeEntry = GetTypeEntry(typeId);
			typeEntry.mBeType = type;
			if (typeEntry.mInstBeType == NULL)
				typeEntry.mInstBeType = type;
		}
		break;
	case BfIRCmd_SetInstType:
		{
			CMD_PARAM(int, typeId);
			CMD_PARAM(BeType*, type);
			GetTypeEntry(typeId).mInstBeType = type;
		}
		break;
	case BfIRCmd_PrimitiveType:
		{
			BfTypeCode typeCode = (BfTypeCode)mStream->Read();
			bool isSigned = false;
			SetResult(curId, GetBeType(typeCode, isSigned));
		}
		break;
	case BfIRCmd_CreateStruct:
		{
			CMD_PARAM(String, typeName);
			SetResult(curId, mBeContext->CreateStruct(typeName));
		}
		break;
	case BfIRCmd_CreateAnonymousStruct:
		{
			CMD_PARAM(CmdParamVec<BeType*>, members);
			BeStructType* structType = mBeContext->CreateStruct(members);
			SetResult(curId, structType);
		}
		break;
	case BfIRCmd_StructSetBody:
		{
			CMD_PARAM(BeType*, type);
			CMD_PARAM(CmdParamVec<BeType*>, members);
			CMD_PARAM(int, instSize);
			CMD_PARAM(int, instAlign);
			CMD_PARAM(bool, isPacked);

			if ((type == NULL) || (type->mTypeCode != BeTypeCode_Struct))
			{
				Fail("StructSetBody invalid type");
				break;
			}

			bool failed = false;
			for (auto member : members)
			{
				if (member->mSize < 0)
				{
					Fail("StructSetBody invalid member type");
					failed = true;
				}
			}

			if (!failed)
			{
				BF_ASSERT(type->mTypeCode == BeTypeCode_Struct);
				auto structType = (BeStructType*)type;
				mBeContext->SetStructBody(structType, members, isPacked);
				structType->mSize = instSize;
				structType->mAlign = instAlign;
			}
		}
		break;
	case  BfIRCmd_Type:
		{
			CMD_PARAM(BeIRTypeEntry*, typeEntry);
			auto type = typeEntry->mBeType;
			SetResult(curId, type);
		}
		break;
	case  BfIRCmd_TypeInst:
		{
			CMD_PARAM(BeIRTypeEntry*, typeEntry);
			SetResult(curId, typeEntry->mInstBeType);
		}
		break;
	case BfIRCmd_TypeInstPtr:
		{
			CMD_PARAM(BeIRTypeEntry*, typeEntry);
			SetResult(curId, mBeContext->GetPointerTo(typeEntry->mInstBeType));
		}
		break;
	case BfIRCmd_GetType:
		{
			CMD_PARAM(BeValue*, value);
			auto type = value->GetType();
			SetResult(curId, type);
		}
		break;
	case BfIRCmd_GetPointerToFuncType:
		{
			CMD_PARAM(BeFunctionType*, funcType);
			SetResult(curId, mBeContext->GetPointerTo(funcType));
		}
		break;
	case BfIRCmd_GetPointerToType:
		{
			CMD_PARAM(BeType*, type);
			SetResult(curId, mBeContext->GetPointerTo(type));
		}
		break;
	case BfIRCmd_GetSizedArrayType:
		{
			CMD_PARAM(BeType*, elementType);
			CMD_PARAM(int, length);
			SetResult(curId, mBeContext->CreateSizedArrayType(elementType, length));
		}
		break;
	case BfIRCmd_GetVectorType:
		{
			CMD_PARAM(BeType*, elementType);
			CMD_PARAM(int, length);
			SetResult(curId, mBeContext->CreateVectorType(elementType, length));
		}
		break;
	case BfIRCmd_CreateConstAgg:
		{
			CMD_PARAM(BeType*, type);
			CMD_PARAM(CmdParamVec<BeValue*>, values);

			auto constStruct = mBeModule->mOwnedValues.Alloc<BeStructConstant>();
			constStruct->mType = type;

			if (type->IsStruct())
			{
				FixValues((BeStructType*)type, values);

				BF_ASSERT(((BeStructType*)type)->mMembers.size() == values.size());
				for (int i = 0; i < (int)values.size(); i++)
				{
					auto val = values[i];
					BF_ASSERT(mBeContext->AreTypesEqual(((BeStructType*)type)->mMembers[i].mType, val->GetType()));
					constStruct->mMemberValues.push_back(BeValueDynCast<BeConstant>(val));
				}
			}
			else
			{
				BF_ASSERT(type->IsSizedArray());
				auto arrayType = (BeSizedArrayType*)type;

				int fillCount = (int)(arrayType->mLength - values.size());
				if (fillCount > 0)
				{
					auto lastValue = values.back();
					for (int i = 0; i < fillCount; i++)
						values.push_back(lastValue);
				}

				BF_ASSERT(arrayType->mLength == values.size());
				for (int i = 0; i < (int)values.size(); i++)
				{
					auto val = values[i];
					BF_ASSERT(mBeContext->AreTypesEqual(((BeSizedArrayType*)type)->mElementType, val->GetType()));
					constStruct->mMemberValues.push_back(BeValueDynCast<BeConstant>(val));
				}
			}
			SetResult(curId, constStruct);
		}
		break;
	case BfIRCmd_CreateConstStructZero:
		{
			CMD_PARAM(BeType*, type);
			auto beConst = mBeModule->mAlloc.Alloc<BeConstant>();
			beConst->mType = type;
			SetResult(curId, beConst);
		}
		break;
	case BfIRCmd_CreateConstString:
		{
			CMD_PARAM(String, str);
			auto constStruct = mBeModule->mOwnedValues.Alloc<BeStringConstant>();
			constStruct->mString = str;
			auto charType = mBeContext->GetPrimitiveType(BeTypeCode_Int8);
			constStruct->mType = mBeContext->CreateSizedArrayType(charType, str.length() + 1);
			SetResult(curId, constStruct);
		}
		break;
	case BfIRCmd_ConfigConst:
		{
			CMD_PARAM(int, constIdx);
			BfTypeCode typeCode = (BfTypeCode)mStream->Read();
			if (typeCode == BfTypeCode_IntPtr)
				typeCode = (mPtrSize == 4) ? BfTypeCode_Int32 : BfTypeCode_Int64;
			BeConstant* constVal = (typeCode == BfTypeCode_Int32) ?
				mBeModule->mConfigConsts32[constIdx] :
				mBeModule->mConfigConsts64[constIdx];
			SetResult(curId, constVal);
		}
		break;
	case BfIRCmd_SetName:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(String, name);
			val->SetName(name);
		}
		break;
	case BfIRCmd_CreateUndefValue:
		{
			CMD_PARAM(BeType*, type);
			SetResult(curId, mBeModule->CreateUndefValue(type));
		}
		break;
	case BfIRCmd_NumericCast:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(bool, valIsSigned);

			BfTypeCode typeCode = (BfTypeCode)mStream->Read();
			auto valType = val->GetType();
			if ((!valType->IsIntable()) && (!valType->IsFloat()))
			{
				Fail("Invalid NumericCast target");
			}

			BfTypeCode valTypeCode = GetTypeCode(valType, valIsSigned);

			if (auto srcCastConstant = BeValueDynCast<BeCastConstant>(val))
			{
				BeType* toType = GetBeType(typeCode, valIsSigned);

				auto castedVal = mBeModule->mAlloc.Alloc<BeCastConstant>();
				castedVal->mInt64 = srcCastConstant->mInt64;
				castedVal->mType = toType;
				castedVal->mTarget = srcCastConstant->mTarget;

				SetResult(curId, castedVal);
				break;
			}

			bool toSigned = false;
			auto toBeType = GetBeType(typeCode, toSigned);
			BeValue* retVal = mBeModule->CreateNumericCast(val, toBeType, valIsSigned, toSigned);
			SetResult(curId, retVal);
		}
		break;
	case BfIRCmd_CmpEQ:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			if (lhs->GetType() != rhs->GetType())
				Fail("Type mismatch for CmpEQ");
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_EQ, lhs, rhs));
		}
		break;
	case BfIRCmd_CmpNE:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_NE, lhs, rhs));
		}
		break;
	case BfIRCmd_CmpSLT:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_SLT, lhs, rhs));
		}
		break;
	case BfIRCmd_CmpULT:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_ULT, lhs, rhs));
		}
		break;
	case BfIRCmd_CmpSLE:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_SLE, lhs, rhs));
		}
		break;
	case BfIRCmd_CmpULE:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_ULE, lhs, rhs));
		}
		break;
	case BfIRCmd_CmpSGT:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_SGT, lhs, rhs));
		}
		break;
	case BfIRCmd_CmpUGT:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_UGT, lhs, rhs));
		}
		break;
	case BfIRCmd_CmpSGE:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_SGE, lhs, rhs));
		}
		break;
	case BfIRCmd_CmpUGE:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_UGE, lhs, rhs));
		}
		break;
	case BfIRCmd_Add:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			CMD_PARAM(int8, overflowCheckKind);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_Add, lhs, rhs, (BfOverflowCheckKind)overflowCheckKind));
		}
		break;
	case BfIRCmd_Sub:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			CMD_PARAM(int8, overflowCheckKind);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_Subtract, lhs, rhs, (BfOverflowCheckKind)overflowCheckKind));
		}
		break;
	case BfIRCmd_Mul:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			CMD_PARAM(int8, overflowCheckKind);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_Multiply, lhs, rhs, (BfOverflowCheckKind)overflowCheckKind));
		}
		break;
	case BfIRCmd_SDiv:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_SDivide, lhs, rhs));
		}
		break;
	case BfIRCmd_UDiv:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_UDivide, lhs, rhs));
		}
		break;
	case BfIRCmd_SRem:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_SModulus, lhs, rhs));
		}
		break;
	case BfIRCmd_URem:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_UModulus, lhs, rhs));
		}
		break;
	case BfIRCmd_And:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_BitwiseAnd, lhs, rhs));
		}
		break;
	case BfIRCmd_Or:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_BitwiseOr, lhs, rhs));
		}
		break;
	case BfIRCmd_Xor:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_ExclusiveOr, lhs, rhs));
		}
		break;
	case BfIRCmd_Shl:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_LeftShift, lhs, rhs));
		}
		break;
	case BfIRCmd_AShr:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_ARightShift, lhs, rhs));
		}
		break;
	case BfIRCmd_LShr:
		{
			CMD_PARAM(BeValue*, lhs);
			CMD_PARAM(BeValue*, rhs);
			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_RightShift, lhs, rhs));
		}
		break;
	case BfIRCmd_Neg:
		{
			CMD_PARAM(BeValue*, val);

			auto negInst = mBeModule->AllocInst<BeNegInst>();
			negInst->mValue = val;
			SetResult(curId, negInst);
		}
		break;
	case BfIRCmd_Not:
		{
			CMD_PARAM(BeValue*, val);

			auto negInst = mBeModule->AllocInst<BeNotInst>();
			negInst->mValue = val;
			SetResult(curId, negInst);
		}
		break;
	case BfIRCmd_BitCast:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(BeType*, toType);
			if (auto funcVal = BeValueDynCast<BeFunction>(val))
			{
				auto beConst = mBeModule->mAlloc.Alloc<BeConstant>();
				beConst->mTarget = funcVal;
				BF_ASSERT(funcVal->mType != NULL);
				beConst->mType = toType;
				SetResult(curId, beConst);
				break;
			}

			SetResult(curId, mBeModule->CreateBitCast(val, toType));
		}
		break;
	case BfIRCmd_PtrToInt:
		{
			CMD_PARAM(BeValue*, val);
			auto typeCode = (BfTypeCode)mStream->Read();
			bool isSigned = false;
			auto beType = GetBeType(typeCode, isSigned);

			BF_ASSERT(beType != NULL);
			auto numericCastInst = mBeModule->AllocInst<BeNumericCastInst>();
			numericCastInst->mValue = val;
			numericCastInst->mValSigned = false;
			numericCastInst->mToType = beType;
			numericCastInst->mToSigned = isSigned;
			SetResult(curId, numericCastInst);
		}
		break;
	case BfIRCmd_IntToPtr:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(BeType*, toType);

			auto bitcastInst = mBeModule->AllocInst<BeBitCastInst>();
			bitcastInst->mValue = val;
			bitcastInst->mToType = toType;
			SetResult(curId, bitcastInst);
		}
		break;
	case BfIRCmd_InboundsGEP1_32:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(int, idx0);
			BF_ASSERT(val->GetType()->IsPointer());
			BeType* int32Type = mBeContext->GetPrimitiveType(BeTypeCode_Int32);
			SetResult(curId, mBeModule->CreateGEP(val, mBeModule->GetConstant(int32Type, (int64)idx0), NULL));
		}
		break;
	case BfIRCmd_InboundsGEP2_32:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(int, idx0);
			CMD_PARAM(int, idx1);
			BF_ASSERT(val->GetType()->IsPointer());
			BeType* int32Type = mBeContext->GetPrimitiveType(BeTypeCode_Int32);
			SetResult(curId, mBeModule->CreateGEP(val, mBeModule->GetConstant(int32Type, (int64)idx0), mBeModule->GetConstant(int32Type, (int64)idx1)));
		}
		break;
	case BfIRCmd_InBoundsGEP1:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(BeValue*, idx0);
			BF_ASSERT(val->GetType()->IsPointer());
			SetResult(curId, mBeModule->CreateGEP(val, idx0, NULL));
		}
		break;
	case BfIRCmd_InBoundsGEP2:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(BeValue*, idx0);
			CMD_PARAM(BeValue*, idx1);
			BF_ASSERT(val->GetType()->IsPointer());
			SetResult(curId, mBeModule->CreateGEP(val, idx0, idx1));
		}
		break;
	case BfIRCmd_IsNull:
		{
			CMD_PARAM(BeValue*, val);
			BF_ASSERT(val->GetType()->IsPointer());
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_EQ, val, mBeModule->GetConstantNull((BePointerType*)val->GetType())));
		}
		break;
	case BfIRCmd_IsNotNull:
		{
			CMD_PARAM(BeValue*, val);
			BF_ASSERT(val->GetType()->IsPointer());
			SetResult(curId, mBeModule->CreateCmp(BeCmpKind_NE, val, mBeModule->GetConstantNull((BePointerType*)val->GetType())));
		}
		break;
	case BfIRCmd_ExtractValue:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(int, idx);

			BF_ASSERT(val->GetType()->IsComposite());
			if (val->GetType()->mTypeCode == BeTypeCode_Struct)
			{ 
				auto structType = (BeStructType*)val->GetType();
				if (idx >= structType->mMembers.mSize)
				{
					FatalError("ExtractValue OOB");
				}
			}			

			auto extractValueInst = mBeModule->AllocInst<BeExtractValueInst>();
			extractValueInst->mAggVal = val;
			extractValueInst->mIdx = idx;
			SetResult(curId, extractValueInst);
		}
		break;
	case BfIRCmd_InsertValue:
		{
			CMD_PARAM(BeValue*, agg);
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(int, idx);

			auto insertValueInst = mBeModule->AllocInst<BeInsertValueInst>();
			insertValueInst->mAggVal = agg;
			insertValueInst->mMemberVal = val;
			insertValueInst->mIdx = idx;
			SetResult(curId, insertValueInst);
		}
		break;
	case BfIRCmd_Alloca:
		{
			CMD_PARAM(BeType*, type);
			if (type->IsStruct())
			{
				BF_ASSERT(!((BeStructType*)type)->mIsOpaque);
			}

			auto allocaInst = mBeModule->CreateAlloca(type);
			allocaInst->mAlign = type->mAlign;
			SetResult(curId, allocaInst);
		}
		break;
	case BfIRCmd_AllocaArray:
		{
			CMD_PARAM(BeType*, type);
			CMD_PARAM(BeValue*, arraySize);

			if (auto constant = BeValueDynCast<BeConstant>(arraySize))
			{
				//BF_ASSERT(constant->mInt64 >= 0);
			}

			auto allocaInst = mBeModule->AllocInst<BeAllocaInst>();
			allocaInst->mType = type;
			allocaInst->mAlign = type->mAlign;
			allocaInst->mArraySize = arraySize;

			SetResult(curId, allocaInst);
		}
		break;
	case BfIRCmd_SetAllocaAlignment:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(int, alignment);
			auto inst = BeValueDynCast<BeAllocaInst>(val);

			inst->mAlign = alignment;
			//TODO: Implement
			/*inst->setAlignment(alignment);*/
		}
		break;
	case BfIRCmd_AliasValue:
		{
			CMD_PARAM(BeValue*, val);
			auto inst = mBeModule->AllocInst<BeAliasValueInst>();
			inst->mPtr = val;
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_LifetimeStart:
		{
			CMD_PARAM(BeValue*, val);
			auto inst = mBeModule->AllocInst<BeLifetimeStartInst>();
			inst->mPtr = val;
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_LifetimeEnd:
		{
			CMD_PARAM(BeValue*, val);
#ifdef _DEBUG
			val->mLifetimeEnded = true;
#endif
			auto inst = mBeModule->AllocInst<BeLifetimeEndInst>();
			inst->mPtr = val;
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_LifetimeSoftEnd:
		{
			CMD_PARAM(BeValue*, val);
			auto inst = mBeModule->AllocInst<BeLifetimeSoftEndInst>();
			inst->mPtr = val;
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_LifetimeExtend:
		{
			CMD_PARAM(BeValue*, val);
			auto inst = mBeModule->AllocInst<BeLifetimeExtendInst>();
			inst->mPtr = val;
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_ValueScopeStart:
		{
			auto inst = mBeModule->AllocInst<BeValueScopeStartInst>();
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_ValueScopeRetain:
		{
			CMD_PARAM(BeValue*, val);
			auto inst = mBeModule->AllocInst<BeValueScopeRetainInst>();
			inst->mValue = val;
		}
		break;
	case BfIRCmd_ValueScopeSoftEnd:
		{
			CMD_PARAM(BeValue*, val);
			auto inst = mBeModule->AllocInst<BeValueScopeEndInst>();
			inst->mScopeStart = (BeValueScopeStartInst*)val;
			inst->mIsSoft = true;
			//TODO: Is this always correct? This keeps there from being nops inserted on block opens ( { )
			inst->mDbgLoc = NULL;
		}
		break;
	case BfIRCmd_ValueScopeHardEnd:
		{
			CMD_PARAM(BeValue*, val);
			auto inst = mBeModule->AllocInst<BeValueScopeEndInst>();
			inst->mScopeStart = (BeValueScopeStartInst*)val;
			inst->mIsSoft = false;
			//TODO: Is this always correct? This keeps there from being nops inserted on block opens ( { )
			inst->mDbgLoc = NULL;
		}
		break;
	case BfIRCmd_SetAllocaNoChkStkHint:
		{
			CMD_PARAM(BeValue*, val);
			auto inst = BeValueDynCast<BeAllocaInst>(val);
			inst->mNoChkStk = true;
		}
		break;
	case BfIRCmd_SetAllocaForceMem:
		{
			CMD_PARAM(BeValue*, val);
			auto inst = BeValueDynCast<BeAllocaInst>(val);
			inst->mForceMem = true;
		}
		break;
	case BfIRCmd_Load:
		{
			CMD_PARAM(BeValue*, val);
#ifdef _DEBUG
			auto ptrType = val->GetType();
			BF_ASSERT(ptrType->IsPointer());
			// We call via a function pointer so there's never a reason to allow loading of a funcPtr
			BF_ASSERT(((BePointerType*)ptrType)->mElementType->mTypeCode != BeTypeCode_Function);

			// Disallow loading from a NULL constant
			if (val->GetTypeId() == BeConstant::TypeId)
			{
				if (auto constant = BeValueDynCast<BeConstant>(val))
				{
					BF_ASSERT(constant->mTarget != NULL);
				}
			}

#endif
			CMD_PARAM(bool, isVolatile);
			SetResult(curId, mBeModule->CreateLoad(val, isVolatile));
		}
		break;
	case BfIRCmd_AlignedLoad:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(int, alignment);
			CMD_PARAM(bool, isVolatile);
#ifdef _DEBUG
			// Disallow loading from a NULL constant
			if (val->GetTypeId() == BeConstant::TypeId)
			{
				if (auto constant = BeValueDynCast<BeConstant>(val))
				{
					BF_ASSERT(constant->mTarget != NULL);
				}
			}

			auto ptrType = val->GetType();
			BF_ASSERT(ptrType->IsPointer());
#endif
			SetResult(curId, mBeModule->CreateAlignedLoad(val, alignment, isVolatile));
		}
		break;
	case BfIRCmd_Store:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(BeValue*, ptr);

#ifdef _DEBUG
			auto ptrType = ptr->GetType();
			auto valType = val->GetType();

			if ((!ptrType->IsPointer()) || (!mBeContext->AreTypesEqual(((BePointerType*)ptrType)->mElementType, valType)))
			{
				String errStr;
				errStr += "BfIRCmd_Store Match Failure:\n";
				BeDumpContext dumpCtx;
				errStr += "Val: ";
				dumpCtx.ToString(errStr, val);
				errStr += "\nPtr: ";
				dumpCtx.ToString(errStr, ptr);
				FatalError(errStr);
			}
#endif

			CMD_PARAM(bool, isVolatile);
			SetResult(curId, mBeModule->CreateStore(val, ptr, isVolatile));
		}
		break;
	case BfIRCmd_AlignedStore:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(BeValue*, ptr);
			CMD_PARAM(int, alignment);
			CMD_PARAM(bool, isVolatile);

#ifdef _DEBUG
			auto ptrType = ptr->GetType();
			auto valType = val->GetType();
			if ((!ptrType->IsPointer()) || (!mBeContext->AreTypesEqual(((BePointerType*)ptrType)->mElementType, valType)))
			{
				String errStr;
				errStr += "BfIRCmd_Store Match Failure:\n";
				BeDumpContext dumpCtx;
				errStr += "Val: ";
				dumpCtx.ToString(errStr, val);
				errStr += "\nPtr: ";
				dumpCtx.ToString(errStr, ptr);
				FatalError(errStr);
			}
#endif

			SetResult(curId, mBeModule->CreateAlignedStore(val, ptr, alignment, isVolatile));
		}
		break;
	case BfIRCmd_MemSet:
		{
			auto inst = mBeModule->AllocInst<BeMemSetInst>();
			Read(inst->mAddr);
			Read(inst->mVal);
			Read(inst->mSize);
			Read(inst->mAlignment);
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_Fence:
		{
			BfIRFenceType fenceType = (BfIRFenceType)mStream->Read();
			mBeModule->AllocInst<BeFenceInst>();
		}
		break;
	case BfIRCmd_StackSave:
		{
			SetResult(curId, mBeModule->AllocInst<BeStackSaveInst>());
		}
		break;
	case BfIRCmd_StackRestore:
		{
			CMD_PARAM(BeValue*, stackVal);
			auto stackRestoreInst = mBeModule->AllocInst<BeStackRestoreInst>();
			stackRestoreInst->mStackVal = stackVal;
			SetResult(curId, stackRestoreInst);
		}
		break;
	case BfIRCmd_GlobalVariable:
		{
			CMD_PARAM(BeType*, varType);
			CMD_PARAM(bool, isConstant);
			BfIRLinkageType linkageType = (BfIRLinkageType)mStream->Read();
			CMD_PARAM(StringT<256>, name);
			CMD_PARAM(bool, isTLS);
			CMD_PARAM(BeConstant*, initializer);

			BF_ASSERT(varType != NULL);

			auto globalVariable = mBeModule->mGlobalVariables.Alloc();
			globalVariable->mModule = mBeModule;
			globalVariable->mType = varType;
			globalVariable->mIsConstant = isConstant;
			globalVariable->mLinkageType = linkageType;
			globalVariable->mInitializer = initializer;
			globalVariable->mName = name;
			globalVariable->mIsTLS = isTLS;
			globalVariable->mUnnamedAddr = false;
			globalVariable->mStorageKind = BfIRStorageKind_Normal;
			if (initializer != NULL)
			{
				globalVariable->mAlign = varType->mAlign;
				BF_ASSERT(varType->mAlign > 0);
				BF_ASSERT(mBeContext->AreTypesEqual(varType, initializer->GetType()));
			}
			else
				globalVariable->mAlign = -1;

			SetResult(curId, globalVariable);
		}
		break;
	case BfIRCmd_GlobalVar_SetUnnamedAddr:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(bool, unnamedAddr);

			BF_ASSERT(BeValueDynCast<BeGlobalVariable>(val) != NULL);

			((BeGlobalVariable*)val)->mUnnamedAddr = true;
		}
		break;
	case BfIRCmd_GlobalVar_SetInitializer:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(BeConstant*, initializer);

			BF_ASSERT(BeValueDynCast<BeGlobalVariable>(val) != NULL);

			auto globalVariable = (BeGlobalVariable*)val;
			globalVariable->mInitializer = initializer;

			if (globalVariable->mInitializer != NULL)
			{
				globalVariable->mAlign = globalVariable->mType->mAlign;
				BF_ASSERT(globalVariable->mAlign != -1);
			}
		}
		break;
	case BfIRCmd_GlobalVar_SetAlignment:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(int, alignment);

			BF_ASSERT(BeValueDynCast<BeGlobalVariable>(val) != NULL);

			auto globalVariable = (BeGlobalVariable*)val;
			globalVariable->mAlign = alignment;
			BF_ASSERT(alignment > 0);

			if (globalVariable->mInitializer != NULL)
			{
				BF_ASSERT(globalVariable->mAlign != -1);
			}
		}
		break;
	case BfIRCmd_GlobalVar_SetStorageKind:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(int, storageKind);

			BF_ASSERT(BeValueDynCast<BeGlobalVariable>(val) != NULL);

			auto globalVariable = (BeGlobalVariable*)val;
			globalVariable->mStorageKind = (BfIRStorageKind)storageKind;
		}
		break;
	case BfIRCmd_GlobalStringPtr:
		{
			CMD_PARAM(String, str);

			auto constStruct = mBeModule->mOwnedValues.Alloc<BeStringConstant>();
			constStruct->mString = str;
			auto charType = mBeContext->GetPrimitiveType(BeTypeCode_Int8);
			constStruct->mType = mBeContext->CreateSizedArrayType(charType, str.length() + 1);

			auto globalVariable = mBeModule->mGlobalVariables.Alloc();
			globalVariable->mModule = mBeModule;
			globalVariable->mType = constStruct->mType;
			globalVariable->mIsConstant = true;
			globalVariable->mLinkageType = BfIRLinkageType_Internal;
			globalVariable->mInitializer = constStruct;
			globalVariable->mName = StrFormat("__str%d", (int)mBeModule->mGlobalVariables.size() - 1);
			globalVariable->mIsTLS = false;
			globalVariable->mAlign = 1;
			globalVariable->mUnnamedAddr = false;

			auto castedVal = mBeModule->mAlloc.Alloc<BeCastConstant>();
			castedVal->mType = mBeContext->GetPointerTo(charType);
			castedVal->mTarget = globalVariable;
			SetResult(curId, castedVal);

			//SetResult(curId, globalVariable);
		}
		break;
	case BfIRCmd_SetReflectTypeData:
		{
			CMD_PARAM(BeType*, type);
			CMD_PARAM(BeValue*, value);
			mReflectDataMap[type] = value;
		}
		break;
	case BfIRCmd_CreateBlock:
		{
			CMD_PARAM(String, name);
			CMD_PARAM(bool, addNow);
			auto block = mBeModule->CreateBlock(name);
			if (addNow)
				mBeModule->AddBlock(mActiveFunction, block);

			SetResult(curId, block);
		}
		break;
	case BfIRCmd_MaybeChainNewBlock:
		{
			CMD_PARAM(String, name);
			auto newBlock = mBeModule->GetInsertBlock();
			if (!newBlock->IsEmpty())
			{
				auto bb = mBeModule->CreateBlock(name);
				mBeModule->CreateBr(bb);
				mBeModule->AddBlock(mActiveFunction, bb);
				mBeModule->SetInsertPoint(bb);
				newBlock = bb;
			}
			SetResult(curId, newBlock);
		}
		break;
	case BfIRCmd_AddBlock:
		{
			CMD_PARAM(BeBlock*, block);
			mBeModule->AddBlock(mActiveFunction, block);
		}
		break;
	case BfIRCmd_DropBlocks:
		{
			CMD_PARAM(BeBlock*, startingBlock);
			auto& basicBlockList = mActiveFunction->mBlocks;
			int postExitBlockIdx = -1;

			/*auto itr = basicBlockList.begin();
			while (itr != basicBlockList.end())
			{
				auto block = *itr;
				if (block == startingBlock)
				{
					basicBlockList.erase(itr, basicBlockList.end());
					break;
				}
				++itr;
			}*/

			for (int i = 0; i < (int)basicBlockList.size(); i++)
			{
				if (basicBlockList[i] == startingBlock)
				{
					basicBlockList.RemoveRange(i, basicBlockList.size() - i);
					break;
				}
			}
		}
		break;
	case BfIRCmd_MergeBlockDown:
		{
			CMD_PARAM(BeBlock*, fromBlock);
			CMD_PARAM(BeBlock*, intoBlock);
			for (auto inst : fromBlock->mInstructions)
				inst->mParentBlock = intoBlock;
			if (!fromBlock->mInstructions.IsEmpty())
				intoBlock->mInstructions.Insert(0, &fromBlock->mInstructions[0], fromBlock->mInstructions.size());
			mBeModule->RemoveBlock(mActiveFunction, fromBlock);
		}
		break;
	case BfIRCmd_GetInsertBlock:
		{
			SetResult(curId, mBeModule->mActiveBlock);
		}
		break;
	case BfIRCmd_SetInsertPoint:
		{
			CMD_PARAM(BeBlock*, block);
			mBeModule->SetInsertPoint(block);
		}
		break;
	case BfIRCmd_SetInsertPointAtStart:
		{
			CMD_PARAM(BeBlock*, block);
			mBeModule->SetInsertPointAtStart(block);
		}
		break;
	case BfIRCmd_EraseFromParent:
		{
			CMD_PARAM(BeBlock*, block);
			mBeModule->RemoveBlock(mActiveFunction, block);
		}
		break;
	case BfIRCmd_DeleteBlock:
		{
			CMD_PARAM(BeBlock*, block);
		}
		break;
	case BfIRCmd_EraseInstFromParent:
		{
			CMD_PARAM(BeValue*, instVal);

			BeInst* inst = (BeInst*)instVal;
			bool wasRemoved = inst->mParentBlock->mInstructions.Remove(inst);
			BF_ASSERT(wasRemoved);
#ifdef _DEBUG
			inst->mWasRemoved = true;
#endif
		}
		break;
	case BfIRCmd_CreateBr:
		{
			CMD_PARAM(BeBlock*, block);
			mBeModule->CreateBr(block);
		}
		break;
	case BfIRCmd_CreateBr_Fake:
		{
			CMD_PARAM(BeBlock*, block);
			auto inst = mBeModule->CreateBr(block);
			inst->mIsFake = true;
		}
		break;
	case BfIRCmd_CreateBr_NoCollapse:
		{
			CMD_PARAM(BeBlock*, block);
			auto inst = mBeModule->CreateBr(block);
			inst->mNoCollapse = true;
		}
		break;
	case BfIRCmd_CreateCondBr:
		{
			CMD_PARAM(BeValue*, condVal);
			CMD_PARAM(BeBlock*, trueBlock);
			CMD_PARAM(BeBlock*, falseBlock);
			mBeModule->CreateCondBr(condVal, trueBlock, falseBlock);
		}
		break;
	case BfIRCmd_MoveBlockToEnd:
		{
			CMD_PARAM(BeBlock*, block);
			mBeModule->RemoveBlock(mActiveFunction, block);
			mBeModule->AddBlock(mActiveFunction, block);
		}
		break;
	case BfIRCmd_CreateSwitch:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(BeBlock*, dest);
			CMD_PARAM(int, numCases);

			auto switchInst = mBeModule->AllocInstOwned<BeSwitchInst>();
			switchInst->mValue = val;
			switchInst->mDefaultBlock = dest;
			switchInst->mCases.Reserve(numCases);
			SetResult(curId, switchInst);
		}
		break;
	case BfIRCmd_AddSwitchCase:
		{
			CMD_PARAM(BeValue*, switchVal);
			CMD_PARAM(BeValue*, caseVal);
			CMD_PARAM(BeBlock*, caseBlock);

			BeSwitchCase switchCase;
			switchCase.mValue = (BeConstant*)caseVal;
			switchCase.mBlock = caseBlock;
			((BeSwitchInst*)switchVal)->mCases.push_back(switchCase);
		}
		break;
	case BfIRCmd_SetSwitchDefaultDest:
		{
			CMD_PARAM(BeValue*, switchVal);
			CMD_PARAM(BeBlock*, caseBlock);
			((BeSwitchInst*)switchVal)->mDefaultBlock = caseBlock;
		}
		break;
	case BfIRCmd_CreatePhi:
		{
			CMD_PARAM(BeType*, type);
			CMD_PARAM(int, incomingCount);

			auto phiInst = mBeModule->AllocInstOwned<BePhiInst>();
			phiInst->mType = type;
			SetResult(curId, phiInst);
		}
		break;
	case BfIRCmd_AddPhiIncoming:
		{
			CMD_PARAM(BeValue*, phiValue);
			CMD_PARAM(BeValue*, value);
			CMD_PARAM(BeBlock*, comingFrom);

			if (phiValue->GetType() != value->GetType())
			{
				Fail("AddPhiIncoming type mismatch");
				break;
			}			

			auto phiIncoming = mBeModule->mAlloc.Alloc<BePhiIncoming>();
			phiIncoming->mBlock = comingFrom;
			phiIncoming->mValue = value;

			((BePhiInst*)phiValue)->mIncoming.push_back(phiIncoming);
		}
		break;
	case BfIRCmd_GetIntrinsic:
		{
			CMD_PARAM(String, intrinName);
			CMD_PARAM(int, intrinId);
			CMD_PARAM(BeType*, returnType);
			CMD_PARAM(CmdParamVec<BeType*>, paramTypes);

			auto intrin = mBeModule->mAlloc.Alloc<BeIntrinsic>();
			intrin->mName = intrinName;
			intrin->mKind = (BfIRIntrinsic)intrinId;
			intrin->mReturnType = returnType;
			SetResult(curId, intrin);
		}
		break;
	case BfIRCmd_CreateFunctionType:
		{
			CMD_PARAM(BeType*, resultType);
			CMD_PARAM(CmdParamVec<BeType*>, paramTypes);
			CMD_PARAM(bool, isVarArg);
			auto functionType = mBeContext->CreateFunctionType(resultType, paramTypes, isVarArg);
			SetResult(curId, functionType);
		}
		break;
	case BfIRCmd_CreateFunction:
		{
			CMD_PARAM(BeFunctionType*, type);
			BfIRLinkageType linkageType = (BfIRLinkageType)mStream->Read();
			CMD_PARAM(String, name);
			SetResult(curId, mBeModule->CreateFunction(type, linkageType, name));
		}
		break;
	case BfIRCmd_SetFunctionName:
		{
			CMD_PARAM(BeValue*, func);
			CMD_PARAM(String, name);
			BeFunction* beFunc = BeValueDynCast<BeFunction>(func);
			beFunc->mName = name;
		}
		break;
	case BfIRCmd_EnsureFunctionPatchable:
		{
		}
		break;
	case BfIRCmd_RemapBindFunction:
		{
			CMD_PARAM(BeValue*, func);
			// We need to store this value to a data segment so we get a symbol we can remap during hot swap
			//  We actually do this to ensure that we don't bind to the NEW method but rather the old one- so
			//  delegate equality checks still work

			BeFunction* beFunc = BeValueDynCast<BeFunction>(func);
			if (beFunc != NULL)
			{
				if (beFunc->mRemapBindVar == NULL)
				{
					auto globalVariable = mBeModule->mGlobalVariables.Alloc();
					globalVariable->mModule = mBeModule;
					globalVariable->mType = beFunc->mType;
					globalVariable->mIsConstant = true;
					globalVariable->mLinkageType = BfIRLinkageType_External;
					globalVariable->mInitializer = beFunc;
					globalVariable->mName = StrFormat("bf_hs_preserve@%s_%s", beFunc->mName.c_str(), mBeModule->mModuleName.c_str());
					globalVariable->mIsTLS = false;
					globalVariable->mAlign = 8;
					globalVariable->mUnnamedAddr = false;
					beFunc->mRemapBindVar = globalVariable;

					/*if (mBeModule->mDbgModule != NULL)
					{
						auto dbgGlobalVariable = mBeModule->mDbgModule->mGlobalVariables.Alloc();
						dbgGlobalVariable->mContext = mBeContext;
						dbgGlobalVariable->mName = name;
						dbgGlobalVariable->mLinkageName = globalVariable->mName;
						dbgGlobalVariable->mFile = (BeDbgFile*)file;
						dbgGlobalVariable->mLineNum = lineNum;
						dbgGlobalVariable->mType = (BeDbgType*)type;
						dbgGlobalVariable->mIsLocalToUnit = isLocalToUnit;
						dbgGlobalVariable->mValue = val;
						dbgGlobalVariable->mDecl = decl;
					}*/
				}

				SetResult(curId, mBeModule->CreateLoad(beFunc->mRemapBindVar, false));
			}
			else
				SetResult(curId, func);
		}
		break;
	case BfIRCmd_SetActiveFunction:
		{
			CMD_PARAM(BeFunction*, func);
			mActiveFunction = func;
			mBeModule->mActiveFunction = func;
		}
		break;
	case BfIRCmd_CreateCall:
		{
			CMD_PARAM(BeValue*, func);
			CMD_PARAM(CmdParamVec<BeValue*>, args);

#ifdef BE_EXTRA_CHECKS
			auto funcPtrType = func->GetType();
			if (funcPtrType != NULL)
			{
				BF_ASSERT(funcPtrType->IsPointer());
				auto funcType = (BeFunctionType*)((BePointerType*)funcPtrType)->mElementType;
				BF_ASSERT(funcType->mTypeCode == BeTypeCode_Function);

				bool argsMatched = true;
				if (!funcType->mIsVarArg)
				{
					if (funcType->mParams.size() != args.size())
					{
						argsMatched = false;
					}
					else
					{
						int argIdx = 0;
						for (int argIdx = 0; argIdx < (int)args.size(); argIdx++)
						{
							if (args[argIdx] == NULL)
								argsMatched = false;
							else if (funcType->mParams[argIdx].mType != args[argIdx]->GetType())
								argsMatched = false;
						}
					}
				}

				if (!argsMatched)
				{
					String errStr;
					errStr += "BfIRCmd_CreateCall Match Failure:\n";
					BeDumpContext dumpCtx;
					dumpCtx.ToString(errStr, func);
					errStr += "\n";
					dumpCtx.ToString(errStr, funcType);
					errStr += "\n";
					for (int argIdx = 0; argIdx < (int)args.size(); argIdx++)
					{
						errStr += StrFormat("ARG #%d: ", argIdx);
						dumpCtx.ToString(errStr, args[argIdx]);
						errStr += "\n";
					}
					FatalError(errStr);
				}
			}
			else
			{
				BF_ASSERT(func->GetTypeId() == BeIntrinsic::TypeId);
			}
#endif
			SetResult(curId, mBeModule->CreateCall(func, args));
		}
		break;
	case BfIRCmd_SetCallCallingConv:
		{
			CMD_PARAM(BeValue*, callInst);
			BfIRCallingConv callingConv = (BfIRCallingConv)mStream->Read();
			((BeCallInst*)callInst)->mCallingConv = callingConv;
		}
		break;
	case BfIRCmd_SetFuncCallingConv:
		{
			CMD_PARAM(BeFunction*, func);
			BfIRCallingConv callingConv = (BfIRCallingConv)mStream->Read();
			func->mCallingConv = callingConv;
		}
		break;
	case BfIRCmd_SetTailCall:
		{
			CMD_PARAM(BeValue*, callInstVal);
			BeCallInst* callInst = (BeCallInst*)callInstVal;
			callInst->mTailCall = true;
		}
		break;
	case BfIRCmd_SetCallAttribute:
		{
			CMD_PARAM(BeValue*, callInstVal);
			CMD_PARAM(int, paramIdx);
			BfIRAttribute attribute = (BfIRAttribute)mStream->Read();
			BeCallInst* callInst = (BeCallInst*)callInstVal;
			if (attribute == BfIRAttribute_NoReturn)
				callInst->mNoReturn = true;
		}
		break;
	case BfIRCmd_CreateRet:
		{
			CMD_PARAM(BeValue*, val);
#ifdef BE_EXTRA_CHECKS
			auto retType = val->GetType();
			auto funcType = mActiveFunction->GetFuncType();
			BF_ASSERT(retType == funcType->mReturnType);
#endif
			SetResult(curId, mBeModule->CreateRet(val));
		}
		break;
	case BfIRCmd_CreateSetRet:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(int, returnTypeId);
			SetResult(curId, mBeModule->CreateSetRet(val, returnTypeId));
		}
		break;
	case BfIRCmd_CreateRetVoid:
		{
			mBeModule->CreateRetVoid();
		}
		break;
	case BfIRCmd_CreateUnreachable:
		{
			mBeModule->AllocInst<BeUnreachableInst>();
		}
		break;
	case BfIRCmd_Call_AddAttribute:
		{
			CMD_PARAM(BeValue*, callInstVal);
			CMD_PARAM(int, argIdx);
			BfIRAttribute attribute = (BfIRAttribute)mStream->Read();
			BeCallInst* callInst = (BeCallInst*)callInstVal;
			if (argIdx > 0)
			{
				if (attribute == BfIRAttribute_StructRet)
				{
					auto valType = callInst->mArgs[argIdx - 1].mValue->GetType();
					BF_ASSERT(valType->IsPointer());
					callInst->mArgs[argIdx - 1].mStructRet = true;
				}
				else if (attribute == BfIRAttribute_ZExt)
					callInst->mArgs[argIdx - 1].mZExt = true;
				else if (attribute == BfIRAttribute_NoAlias)
					callInst->mArgs[argIdx - 1].mNoAlias = true;
				else if (attribute == BfIRAttribute_NoCapture)
					callInst->mArgs[argIdx - 1].mNoCapture = true;
				else if (attribute == BfIRAttribute_ByVal)
				{
				}
				else
					BF_FATAL("Unhandled");
			}
			else
			{
				if (attribute == BfIRAttribute_NoReturn)
					callInst->mNoReturn = true;
				else
					BF_FATAL("Unhandled");
			}
		}
		break;
	case BfIRCmd_Call_AddAttribute1:
		{
			CMD_PARAM(BeValue*, inst);
			CMD_PARAM(int, argIdx);
			BfIRAttribute attribute = (BfIRAttribute)mStream->Read();
			CMD_PARAM(int, arg);

			BeCallInst* callInst = BeValueDynCast<BeCallInst>(inst);
			if (callInst != NULL)
			{
				if (argIdx > 0)
				{
					if (attribute == BfIRAttribute_Dereferencable)
					{
						callInst->mArgs[argIdx - 1].mDereferenceableSize = arg;
						if (auto func = BeValueDynCast<BeFunction>(callInst->mFunc))
						{
							BF_ASSERT(func->mParams[argIdx - 1].mDereferenceableSize == arg);
						}
					}
					else if (attribute == BfIRAttribute_ByVal)
					{
						callInst->mArgs[argIdx - 1].mByRefSize = arg;
						if (auto func = BeValueDynCast<BeFunction>(callInst->mFunc))
						{
							BF_ASSERT((func->mParams[argIdx - 1].mByValSize == arg) || (func->mParams[argIdx - 1].mByValSize == -1));
						}
					}
					else
						BF_FATAL("Unhandled");
				}
				else
				{
					BF_FATAL("Unhandled");
				}
			}
		}
		break;
	case BfIRCmd_Func_AddAttribute:
		{
			CMD_PARAM(BeFunction*, func);
			CMD_PARAM(int, argIdx);
			BfIRAttribute attribute = (BfIRAttribute)mStream->Read();
			if (argIdx > 0)
			{
				if (attribute == BfIRAttribute_StructRet)
					func->mParams[argIdx - 1].mStructRet = true;
				else if (attribute == BfIRAttribute_NoAlias)
					func->mParams[argIdx - 1].mNoAlias = true;
				else if (attribute == BfIRAttribute_NoCapture)
					func->mParams[argIdx - 1].mNoCapture = true;
				else if (attribute == BfIRAttribute_ZExt)
					func->mParams[argIdx - 1].mZExt = true;
				else
					BF_FATAL("Unhandled");
			}
			else
			{
				if (attribute == BfIRAttribute_VarRet)
					func->mIsVarReturn = true;
				else if (attribute == BFIRAttribute_AlwaysInline)
					func->mAlwaysInline = true;
				else if (attribute == BFIRAttribute_NoUnwind)
					func->mNoUnwind = true;
				else if (attribute == BFIRAttribute_UWTable)
					func->mUWTable = true;
				else if (attribute == BfIRAttribute_NoReturn)
					func->mNoReturn = true;
				else if (attribute == BFIRAttribute_NoFramePointerElim)
					func->mNoFramePointerElim = true;
				else if (attribute == BFIRAttribute_DllExport)
					func->mIsDLLExport = true;
				else if (attribute == BFIRAttribute_DllImport)
					func->mIsDLLImport = true;
				else if (attribute == BFIRAttribute_NoRecurse)
				{
				}
				else if (attribute == BFIRAttribute_Constructor)
				{
				}
				else if (attribute == BFIRAttribute_Destructor)
				{
				}
				else
					BF_FATAL("Unhandled");
			}
		}
		break;
	case BfIRCmd_Func_AddAttribute1:
		{
			CMD_PARAM(BeFunction*, func);
			CMD_PARAM(int, argIdx);
			BfIRAttribute attribute = (BfIRAttribute)mStream->Read();
			CMD_PARAM(int, arg);
			// This is for adding things like Dereferencable, which we don't use

			if (argIdx > 0)
			{
				if (attribute == BfIRAttribute_Dereferencable)
					func->mParams[argIdx - 1].mDereferenceableSize = arg;
				else if (attribute == BfIRAttribute_ByVal)
					func->mParams[argIdx - 1].mByValSize = arg;
				else
					BF_FATAL("Unhandled");
			}
			else
				BF_FATAL("Unhandled");
		}
		break;
	case BfIRCmd_Func_SetParamName:
		{
			CMD_PARAM(BeFunction*, func);
			CMD_PARAM(int, argIdx);
			CMD_PARAM(String, name);
			if (argIdx > 0)
				func->mParams[argIdx - 1].mName = name;
		}
		break;
	case BfIRCmd_Func_DeleteBody:
		{
			CMD_PARAM(BeFunction*, func);
			func->mBlocks.Clear();
		}
		break;
	case BfIRCmd_Func_SafeRename:
		{
			CMD_PARAM(BeFunction*, func);
			func->mName += StrFormat("__RENAME%d", curId);
		}
		break;
	case BfIRCmd_Func_SafeRenameFrom:
		{
			CMD_PARAM(BeFunction*, func);
			CMD_PARAM(String, prevName);
			if (func->mName == prevName)
				func->mName += StrFormat("__RENAME%d", curId);
		}
		break;
	case BfIRCmd_Func_SetLinkage:
		{
			CMD_PARAM(BeFunction*, func);
			BfIRLinkageType linkageType = (BfIRLinkageType)mStream->Read();
			func->mLinkageType = linkageType;
		}
		break;
	case BfIRCmd_SaveDebugLocation:
		{
			mSavedDebugLocs.push_back(mBeModule->GetCurrentDebugLocation());
		}
		break;
	case BfIRCmd_RestoreDebugLocation:
		{
			mBeModule->SetCurrentDebugLocation(mSavedDebugLocs[mSavedDebugLocs.size() - 1]);
			mSavedDebugLocs.pop_back();
		}
		break;
	case BfIRCmd_DupDebugLocation:
		{
			mBeModule->DupCurrentDebugLocation();
		}
		break;
	case BfIRCmd_ClearDebugLocation:
		{
			mBeModule->SetCurrentDebugLocation(NULL);
		}
		break;
	case BfIRCmd_ClearDebugLocationInst:
		{
			CMD_PARAM(BeValue*, instValue);
			auto inst = (BeInst*)instValue;
			inst->mDbgLoc = NULL;
		}
		break;
	case BfIRCmd_ClearDebugLocationInstLast:
		{
			if ((mBeModule->mActiveBlock != NULL) && (!mBeModule->mActiveBlock->mInstructions.IsEmpty()))
			{
				auto inst = mBeModule->mActiveBlock->mInstructions.back();
				inst->mDbgLoc = NULL;
			}
		}
		break;
	case BfIRCmd_UpdateDebugLocation:
		{
			CMD_PARAM(BeValue*, instValue);
			auto inst = (BeInst*)instValue;
			inst->mDbgLoc = mBeModule->mCurDbgLoc;
		}
		break;
	case BfIRCmd_SetCurrentDebugLocation:
		{
			CMD_PARAM(int, line);
			CMD_PARAM(int, column);
			CMD_PARAM(BeMDNode*, diScope);
			CMD_PARAM(BeMDNode*, diInlinedAt);
			BF_ASSERT(diScope != NULL);
			mBeModule->SetCurrentDebugLocation(line - 1, column - 1, diScope, (BeDbgLoc*)diInlinedAt);
		}
		break;
	case BfIRCmd_Nop:
		{
			mBeModule->CreateNop();
		}
		break;
	case BfIRCmd_EnsureInstructionAt:
		{
			mBeModule->AllocInst<BeEnsureInstructionAtInst>();
		}
		break;
	case BfIRCmd_StatementStart:
		{
		}
		break;
	case BfIRCmd_ObjectAccessCheck:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(bool, useAsm);

			auto inst = mBeModule->AllocInst<BeObjectAccessCheckInst>();
			inst->mValue = val;

			SetResult(curId, mBeModule->GetInsertBlock());
		}
		break;
	case BfIRCmd_Comptime_Error:
		{
			CMD_PARAM(int32, error);

			auto inst = mBeModule->AllocInst<BeComptimeError>();
			inst->mError = error;
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_Comptime_GetBfType:
		{
			CMD_PARAM(int32, typeId);
			CMD_PARAM(BeType*, resultType);

			auto inst = mBeModule->AllocInst<BeComptimeGetType>();
			inst->mTypeId = typeId;
			inst->mResultType = resultType;
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_Comptime_GetReflectType:
		{
			CMD_PARAM(int32, typeId);
			CMD_PARAM(BeType*, resultType);

			auto inst = mBeModule->AllocInst<BeComptimeGetReflectType>();
			inst->mTypeId = typeId;
			inst->mResultType = resultType;
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_Comptime_DynamicCastCheck:
		{
			CMD_PARAM(BeValue*, value);
			CMD_PARAM(int32, typeId);
			CMD_PARAM(BeType*, resultType);

			auto inst = mBeModule->AllocInst<BeComptimeDynamicCastCheck>();
			inst->mValue = value;
			inst->mTypeId = typeId;
			inst->mResultType = resultType;
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_Comptime_GetVirtualFunc:
		{
			CMD_PARAM(BeValue*, value);
			CMD_PARAM(int32, virtualTableIdx);
			CMD_PARAM(BeType*, resultType);

			auto inst = mBeModule->AllocInst<BeComptimeGetVirtualFunc>();
			inst->mValue = value;
			inst->mVirtualTableIdx = virtualTableIdx;
			inst->mResultType = resultType;
			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_Comptime_GetInterfaceFunc:
		{
			CMD_PARAM(BeValue*, value);
			CMD_PARAM(int32, ifaceTypeId);
			CMD_PARAM(int32, methodIdx);
			CMD_PARAM(BeType*, resultType);

			auto inst = mBeModule->AllocInst<BeComptimeGetInterfaceFunc>();
			inst->mValue = value;
			inst->mIFaceTypeId = ifaceTypeId;
			inst->mMethodIdx = methodIdx;
			inst->mResultType = resultType;
			SetResult(curId, inst);
		}
		break;

	case BfIRCmd_DbgInit:
		{
			/*mDIBuilder = new BeDIBuilder(*mBeModule);			*/
			mBeModule->mDbgModule = new BeDbgModule();
			mBeModule->mDbgModule->mBeModule = mBeModule;
		}
		break;
	case BfIRCmd_DbgFinalize:
		{
			/*for (auto typeEntryPair : mTypes)
			{
				auto& typeEntry = typeEntryPair.second;
				if (typeEntry.mInstDIType != NULL)
					typeEntry.mInstDIType->resolveCycles();
			}
			mDIBuilder->finalize();*/
		}
		break;
	case BfIRCmd_DbgCreateCompileUnit:
		{
			CMD_PARAM(int, lang);
			CMD_PARAM(String, fileName);
			CMD_PARAM(String, directory);
			CMD_PARAM(String, producer);
			CMD_PARAM(bool, isOptimized);
			CMD_PARAM(String, flags);
			CMD_PARAM(int, runtimeVer);
			CMD_PARAM(bool, linesOnly);

			mBeModule->mDbgModule->mFileName = fileName;
			mBeModule->mDbgModule->mDirectory = directory;
			mBeModule->mDbgModule->mProducer = producer;

			//mDebugging = fileName == "TestToots";

			SetResult(curId, mBeModule->mDbgModule);
		}
		break;
	case BfIRCmd_DbgCreateFile:
		{
			CMD_PARAM(String, fileName);
			CMD_PARAM(String, directory);
			CMD_PARAM(Val128, md5Hash);

			auto dbgFile = mBeModule->mDbgModule->mFiles.Alloc();
			dbgFile->mFileName = fileName;
			dbgFile->mDirectory = directory;
			dbgFile->mMD5Hash = md5Hash;
			dbgFile->mIdx = (int)mBeModule->mDbgModule->mFiles.size() - 1;

			SetResult(curId, dbgFile);
		}
		break;
	case BfIRCmd_DbgGetCurrentLocation:
		{
			SetResult(curId, mBeModule->mCurDbgLoc);
		}
		break;
	case BfIRCmd_DbgSetType:
		{
			CMD_PARAM(int, typeId);
			CMD_PARAM(BeMDNode*, type);
			auto& typeEntry = GetTypeEntry(typeId);
			typeEntry.mDIType = BeValueDynCast<BeDbgType>(type);
			if (typeEntry.mInstDIType == NULL)
				typeEntry.mInstDIType = BeValueDynCast<BeDbgType>(type);
		}
		break;
	case BfIRCmd_DbgSetInstType:
		{
			CMD_PARAM(int, typeId);
			CMD_PARAM(BeMDNode*, type);
			GetTypeEntry(typeId).mInstDIType = BeValueDynCast<BeDbgType>(type);
		}
		break;
	case BfIRCmd_DbgGetType:
		{
			CMD_PARAM(int, typeId);

			if (mBeModule->mCeMachine != NULL)
			{
				auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgTypeId>();
				dbgType->mTypeId = typeId;
				SetResult(curId, dbgType);
			}
			else
				SetResult(curId, GetTypeEntry(typeId).mDIType);
		}
		break;
	case BfIRCmd_DbgGetTypeInst:
		{
			CMD_PARAM(int, typeId);
			auto result = GetTypeEntry(typeId).mInstDIType;
			//BF_ASSERT(result);
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_DbgTrackDITypes:
		{
			CMD_PARAM(int, typeId);
			auto& typeEntry = GetTypeEntry(typeId);

			/*if (typeEntry.mDIType != NULL)
				BeMetadataTracking::track(*(BeMetadata**)&typeEntry.mDIType);
			if (typeEntry.mInstDIType != NULL)
				BeMetadataTracking::track(*(BeMetadata**)&typeEntry.mInstDIType);*/
			//NotImpl();
		}
		break;
	case BfIRCmd_DbgCreateNamespace:
		{
			CMD_PARAM(BeMDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, lineNum);

			auto dbgNamespace = mBeModule->mOwnedValues.Alloc<BeDbgNamespace>();
			dbgNamespace->mScope = scope;
			dbgNamespace->mName = name;
			SetResult(curId, dbgNamespace);
		}
		break;
	case BfIRCmd_DbgCreateImportedModule:
		{
			CMD_PARAM(BeMDNode*, context);
			CMD_PARAM(BeMDNode*, namespaceNode);
			CMD_PARAM(int, lineNum);
			/*SetResult(curId, mDIBuilder->createImportedModule((BeDIScope*)context, (BeDINamespace*)namespaceNode, lineNum));*/
			//NotImpl();
		}
		break;
	case BfIRCmd_DbgCreateBasicType:
		{
			CMD_PARAM(String, name);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(int, encoding);

			auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgBasicType>();
			dbgType->mName = name;
			dbgType->mSize = (int)(sizeInBits / 8);
			dbgType->mAlign = (int)(alignInBits / 8);
			dbgType->mEncoding = encoding;

			SetResult(curId, dbgType);
		}
		break;
	case BfIRCmd_DbgCreateStructType:
		{
			CMD_PARAM(BeMDNode*, context);
			CMD_PARAM(String, name);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(int, flags);
			CMD_PARAM(BeMDNode*, derivedFrom);
			CMD_PARAM(CmdParamVec<BeMDNode*>, members);

			auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgStructType>();
			dbgType->mScope = context;
			dbgType->mName = name;
			dbgType->mSize = (int)(sizeInBits / 8);
			dbgType->mAlign = (int)(alignInBits / 8);
			dbgType->mDerivedFrom = BeValueDynCast<BeDbgType>(derivedFrom);
			dbgType->mDefFile = (BeDbgFile*)file;
			dbgType->mDefLine = lineNum - 1;
			dbgType->mIsFullyDefined = true;
			dbgType->SetMembers(members);

			SetResult(curId, dbgType);
		}
		break;
	case BfIRCmd_DbgCreateEnumerationType:
		{
			CMD_PARAM(BeMDNode*, context);
			CMD_PARAM(String, name);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(CmdParamVec<BeMDNode*>, members);
			CMD_PARAM(BeMDNode*, underlyingType);

			auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgEnumType>();
			dbgType->mScope = context;
			dbgType->mName = name;
			dbgType->mSize = (int)(sizeInBits / 8);
			dbgType->mAlign = (int)(alignInBits / 8);
			dbgType->mIsFullyDefined = true;
			dbgType->mElementType = BeValueDynCast<BeDbgType>(underlyingType);
			for (auto member : members)
			{
				if (auto enumMember = BeValueDynCast<BeDbgEnumMember>(member))
				{
					dbgType->mMembers.push_back(enumMember);
				}
				else
					NotImpl();
			}

			//dbgType->mDefFile = (BeDbgFile*)file;
			//dbgType->mDefLine = line - 1;
			SetResult(curId, dbgType);
		}
		break;
	case BfIRCmd_DbgCreatePointerType:
		{
			CMD_PARAM(BeMDNode*, elementTypeNode);

			BeDbgType* elementType = BeValueDynCast<BeDbgType>(elementTypeNode);
			if (elementType == NULL)
			{
				if (auto dbgTypeId = BeValueDynCast<BeDbgTypeId>(elementTypeNode))
				{
					auto bfElementType = mBeModule->mCeMachine->mCeModule->mContext->mTypes[dbgTypeId->mTypeId];
					auto bfPtrType = mBeModule->mCeMachine->mCeModule->CreatePointerType(bfElementType);

					auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgTypeId>();
					dbgType->mTypeId = bfPtrType->mTypeId;
					SetResult(curId, dbgType);
					break;
				}
			}

			BeDbgType* useType = elementType->FindDerivedType(BeDbgPointerType::TypeId);
			if (useType == NULL)
			{
				auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgPointerType>();
				dbgType->mElement = elementType;
				dbgType->mSize = mPtrSize;
				dbgType->mAlign = mPtrSize;
				elementType->mDerivedTypes.PushFront(dbgType, &mBeModule->mAlloc);
				useType = dbgType;
			}

			SetResult(curId, useType);
		}
		break;
	case BfIRCmd_DbgCreateReferenceType:
		{
			CMD_PARAM(BeMDNode*, elementTypeNode);

			BeDbgType* elementType = BeValueDynCast<BeDbgType>(elementTypeNode);
			if (elementType == NULL)
			{
				if (auto dbgTypeId = BeValueDynCast<BeDbgTypeId>(elementTypeNode))
				{
					auto bfElementType = mBeModule->mCeMachine->mCeModule->mContext->mTypes[dbgTypeId->mTypeId];
					auto bfPtrType = mBeModule->mCeMachine->mCeModule->CreateRefType(bfElementType);

					auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgTypeId>();
					dbgType->mTypeId = bfPtrType->mTypeId;
					SetResult(curId, dbgType);
					break;
				}
			}

			auto useType = mBeModule->mDbgModule->CreateReferenceType(elementType);
			SetResult(curId, useType);
		}
		break;
	case BfIRCmd_DbgCreateConstType:
		{
			CMD_PARAM(BeMDNode*, elementTypeNode);

			BeDbgType* elementType = BeValueDynCast<BeDbgType>(elementTypeNode);
			if (elementType == NULL)
			{
				auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgConstType>();
				dbgType->mElement = elementTypeNode;
				SetResult(curId, dbgType);
				break;
			}

			BeDbgType* useType = elementType->FindDerivedType(BeDbgConstType::TypeId);
			if (useType == NULL)
			{
				auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgConstType>();
				dbgType->mElement = elementType;
				elementType->mDerivedTypes.PushFront(dbgType, &mBeModule->mAlloc);
				useType = dbgType;
			}

			SetResult(curId, useType);
		}
		break;
	case BfIRCmd_DbgCreateArtificialType:
		{
			CMD_PARAM(BeMDNode*, diType);

			//auto dbgType = mBeModule->mOwnedValues.Alloc<BeDbgArtificialType>();
			//dbgType->mElement = (BeDbgType*)diType;

			// Does the artificial thing do anything for us actually?
			auto dbgType = diType;

			SetResult(curId, dbgType);
		}
		break;
	case BfIRCmd_DbgCreateArrayType:
		{
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(BeMDNode*, elementType);
			CMD_PARAM(int64, numElements);

			auto dbgArray = mBeModule->mDbgModule->mTypes.Alloc<BeDbgArrayType>();
			dbgArray->mSize = (int)(sizeInBits / 8);
			dbgArray->mAlign = (int)(alignInBits / 8);
			dbgArray->mElement = BeValueDynCast<BeDbgType>(elementType);
			dbgArray->mNumElements = numElements;

			SetResult(curId, dbgArray);
		}
		break;
	case BfIRCmd_DbgCreateReplaceableCompositeType:
		{
			CMD_PARAM(int, tag);
			CMD_PARAM(String, name);
			CMD_PARAM(BeMDNode*, scope);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, line);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(int, flags);

			if (tag == llvm::dwarf::DW_TAG_structure_type)
			{
				auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgStructType>();
				dbgType->mScope = scope;
				dbgType->mName = name;
				dbgType->mSize = (int)(sizeInBits / 8);
				dbgType->mAlign = (int)(alignInBits / 8);
				dbgType->mDefFile = (BeDbgFile*)file;
				dbgType->mDefLine = line - 1;
				SetResult(curId, dbgType);
			}
			else if (tag == llvm::dwarf::DW_TAG_enumeration_type)
			{
				auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgEnumType>();
				dbgType->mScope = scope;
				dbgType->mName = name;
				dbgType->mSize = (int)(sizeInBits / 8);
				dbgType->mAlign = (int)(alignInBits / 8);
				//dbgType->mDefFile = (BeDbgFile*)file;
				//dbgType->mDefLine = line - 1;
				SetResult(curId, dbgType);
			}
			else
				NotImpl();
		}
		break;
	case BfIRCmd_DbgCreateForwardDecl:
		{
			CMD_PARAM(int, tag);
			CMD_PARAM(String, name);
			CMD_PARAM(BeMDNode*, scope);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, line);

			if (tag == llvm::dwarf::DW_TAG_structure_type)
			{
				auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgStructType>();
				dbgType->mScope = scope;
				dbgType->mName = name;
				dbgType->mDefFile = (BeDbgFile*)file;
				dbgType->mDefLine = line;
				SetResult(curId, dbgType);
			}
			else if (tag == llvm::dwarf::DW_TAG_enumeration_type)
			{
				auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgEnumType>();
				dbgType->mScope = scope;
				dbgType->mName = name;
				SetResult(curId, dbgType);
			}
			else
				NotImpl();
		}
		break;
	case BfIRCmd_DbgCreateSizedForwardDecl:
		{
			CMD_PARAM(int, tag);
			CMD_PARAM(String, name);
			CMD_PARAM(BeMDNode*, scope);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, line);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);

			if (tag == llvm::dwarf::DW_TAG_structure_type)
			{
				auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgStructType>();
				dbgType->mDefFile = (BeDbgFile*)file;
				dbgType->mDefLine = line;
				dbgType->mScope = scope;
				dbgType->mName = name;
				dbgType->mSize = (int)(sizeInBits / 8);
				dbgType->mAlign = (int)(alignInBits / 8);
				SetResult(curId, dbgType);
			}
			else if (tag == llvm::dwarf::DW_TAG_enumeration_type)
			{
				auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgEnumType>();
				dbgType->mScope = scope;
				dbgType->mName = name;
				dbgType->mSize = (int)(sizeInBits / 8);
				dbgType->mAlign = (int)(alignInBits / 8);
				SetResult(curId, dbgType);
			}
			else
				NotImpl();
		}
		break;
	case BeIRCmd_DbgSetTypeSize:
		{
			CMD_PARAM(BeMDNode*, mdType);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);

			if (auto dbgType = BeValueDynCast<BeDbgType>(mdType))
			{
				dbgType->mSize = (int)(sizeInBits / 8);
				dbgType->mAlign = (int)(alignInBits / 8);
			}
		}
		break;
	case BfIRCmd_DbgReplaceAllUses:
		{
			CMD_PARAM(BeMDNode*, diPrevNode);
			CMD_PARAM(BeMDNode*, diNewNode);
			/*diPrevNode->replaceAllUsesWith(diNewNode); 	*/
			NotImpl();
		}
		break;
	case BfIRCmd_DbgDeleteTemporary:
		{
			CMD_PARAM(BeMDNode*, diNode);
			/*BeMDNode::deleteTemporary(diNode);*/
			NotImpl();
		}
		break;
	case BfIRCmd_DbgMakePermanent:
		{
			CMD_PARAM(BeMDNode*, diNode);
			CMD_PARAM(BeMDNode*, diBaseType);
			CMD_PARAM(CmdParamVec<BeMDNode*>, members);

			if (auto dbgType = BeValueDynCast<BeDbgStructType>(diNode))
			{
				dbgType->SetMembers(members);
			}
			else if (auto dbgType = BeValueDynCast<BeDbgEnumType>(diNode))
			{
				dbgType->mElementType = BeValueDynCast<BeDbgType>(diBaseType);
				dbgType->SetMembers(members);
			}
			else
				NotImpl();
			SetResult(curId, diNode);
			break;
		}
	case BfIRCmd_CreateEnumerator:
		{
			CMD_PARAM(String, name);
			CMD_PARAM(int64, val);

			auto enumValue = mBeModule->mOwnedValues.Alloc<BeDbgEnumMember>();
			enumValue->mName = name;
			enumValue->mValue = val;
			SetResult(curId, enumValue);
		}
		break;
	case BfIRCmd_DbgCreateMemberType:
		{
			CMD_PARAM(BeMDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, lineNumber);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(int64, offsetInBits);
			CMD_PARAM(int, flags);
			CMD_PARAM(BeMDNode*, type);

			BF_ASSERT(type != NULL);

			auto dbgMember = mBeModule->mOwnedValues.Alloc<BeDbgStructMember>();
			dbgMember->mName = name;
			dbgMember->mType = BeValueDynCast<BeDbgType>(type);
			dbgMember->mOffset = (int)(offsetInBits / 8);
			dbgMember->mFlags = flags;

			SetResult(curId, dbgMember);
		}
		break;
	case BfIRCmd_DbgStaticCreateMemberType:
		{
			CMD_PARAM(BeMDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, lineNumber);
			CMD_PARAM(BeMDNode*, type);
			CMD_PARAM(int, flags);
			CMD_PARAM(BeConstant*, val);

			BF_ASSERT(type != NULL);

			auto dbgMember = mBeModule->mOwnedValues.Alloc<BeDbgStructMember>();
			dbgMember->mName = name;
			dbgMember->mType = BeValueDynCast<BeDbgType>(type);
			dbgMember->mOffset = -1;
			dbgMember->mStaticValue = val;
			dbgMember->mFlags = flags;
			dbgMember->mIsStatic = true;

			SetResult(curId, dbgMember);
		}
		break;
	case BfIRCmd_DbgCreateInheritance:
		{
			CMD_PARAM(BeMDNode*, type);
			CMD_PARAM(BeMDNode*, baseType);
			CMD_PARAM(int64, baseOffset);
			CMD_PARAM(int, flags);

			auto dbgInheritance = mBeModule->mAlloc.Alloc<BeDbgInheritance>();
			dbgInheritance->mBaseType = BeValueDynCast<BeDbgType>(baseType);

			SetResult(curId, dbgInheritance);
		}
		break;
	case BfIRCmd_DbgCreateMethod:
		{
			CMD_PARAM(BeMDNode*, context);
			CMD_PARAM(String, name);
			CMD_PARAM(String, linkageName);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(BeMDNode*, type);
			CMD_PARAM(bool, isLocalToUnit);
			CMD_PARAM(bool, isDefinition);
			CMD_PARAM(int, vk);
			CMD_PARAM(int, vIndex);
			CMD_PARAM(BeMDNode*, vTableHolder);
			CMD_PARAM(int, flags);
			CMD_PARAM(bool, isOptimized);
			CMD_PARAM(BeValue*, fn);
			CMD_PARAM(CmdParamVec<BeMDNode*>, genericArgs);
			CMD_PARAM(CmdParamVec<BeConstant*>, genericConstValueArgs);

			auto dbgFunc = mBeModule->mOwnedValues.Alloc<BeDbgFunction>();
			dbgFunc->mScope = context;
			dbgFunc->mFile = (BeDbgFile*)file;
			dbgFunc->mLine = lineNum - 1;
			dbgFunc->mType = (BeDbgFunctionType*)type;
			dbgFunc->mName = name;
			dbgFunc->mLinkageName = linkageName;
			dbgFunc->mValue = (BeFunction*)fn;
			dbgFunc->mIsLocalToUnit = isLocalToUnit;
			dbgFunc->mVK = vk;
			dbgFunc->mVIndex = vIndex;
			dbgFunc->mIsStaticMethod = (flags & llvm::DINode::FlagStaticMember) != 0;
			dbgFunc->mFlags = flags;

			for (auto arg : genericArgs)
			{
				BF_ASSERT(arg != NULL);
				dbgFunc->mGenericArgs.Add(BeValueDynCast<BeDbgType>(arg));
			}
			for (auto genericConstValue : genericConstValueArgs)
				dbgFunc->mGenericConstValueArgs.Add(genericConstValue);

			if (dbgFunc->mValue != NULL)
				dbgFunc->mValue->mDbgFunction = dbgFunc;
			dbgFunc->mIdx = (int)mBeModule->mDbgModule->mFuncs.size();
			mBeModule->mDbgModule->mFuncs.push_back(dbgFunc);

			SetResult(curId, dbgFunc);
		}
		break;
	case BfIRCmd_DbgCreateFunction:
		{
			CMD_PARAM(BeMDNode*, context);
			CMD_PARAM(String, name);
			CMD_PARAM(String, linkageName);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(BeMDNode*, type);
			CMD_PARAM(bool, isLocalToUnit);
			CMD_PARAM(bool, isDefinition);
			CMD_PARAM(int, scopeLine);
			CMD_PARAM(int, flags);
			CMD_PARAM(bool, isOptimized);
			CMD_PARAM(BeValue*, fn);

			auto dbgFunc = mBeModule->mOwnedValues.Alloc<BeDbgFunction>();
			dbgFunc->mScope = context;
			dbgFunc->mFile = (BeDbgFile*)file;
			dbgFunc->mLine = lineNum - 1;
			dbgFunc->mType = (BeDbgFunctionType*)type;
			dbgFunc->mName = name;
			dbgFunc->mLinkageName = linkageName;
			dbgFunc->mValue = (BeFunction*)fn;
			dbgFunc->mIsLocalToUnit = isLocalToUnit;
			dbgFunc->mFlags = flags;

			/*if (auto dbgStructType = BeValueDynCast<BeDbgStructType>(context))
			{
				// This will get added to the struct later
			}
			else
			{
			}*/
			if (dbgFunc->mValue != NULL)
				dbgFunc->mValue->mDbgFunction = dbgFunc;
			dbgFunc->mIdx = (int)mBeModule->mDbgModule->mFuncs.size();
			mBeModule->mDbgModule->mFuncs.push_back(dbgFunc);

			SetResult(curId, dbgFunc);
		}
		break;
	case BfIRCmd_DbgCreateParameterVariable:
		{
			CMD_PARAM(BeMDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(int, argNo);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(BeMDNode*, type);
			CMD_PARAM(bool, alwaysPreserve);
			CMD_PARAM(int, flags);

			BF_ASSERT(type != NULL);

			auto dbgFunc = (BeDbgFunction*)scope;

			auto dbgVar = mBeModule->mOwnedValues.Alloc<BeDbgVariable>();
			dbgVar->mName = name;
			dbgVar->mType = type;
			dbgVar->mParamNum = argNo - 1;

			int argIdx = argNo - 1;

			//for (int i = ; i <= argNo - 1; i++)
			while (argIdx >= (int)dbgFunc->mVariables.size())
				dbgFunc->mVariables.push_back(NULL);
			if (dbgFunc->mVariables[argIdx] == NULL)
				dbgFunc->mVariables[argIdx] = dbgVar;
			else
			{
				BF_ASSERT(dbgFunc->mVariables[argIdx]->mParamNum == -1);
				dbgFunc->mVariables.Insert(argIdx, dbgVar);
			}
			//mActiveFunction->mDbgFunction->mVariables.push_back(dbgVar);

			//dbgVar->mValue = mBeModule->GetArgument(argNo - 1);

			SetResult(curId, dbgVar);
		}
		break;
	case BfIRCmd_DbgCreateSubroutineType:
		{
			CMD_PARAM(CmdParamVec<BeMDNode*>, elements);

			auto dbgFuncType = mBeModule->mOwnedValues.Alloc<BeDbgFunctionType>();
			if (!elements.empty())
			{
				dbgFuncType->mReturnType = BeValueDynCast<BeDbgType>(elements[0]);
				for (int i = 1; i < (int)elements.size(); i++)
					dbgFuncType->mParams.push_back(BeValueDynCast<BeDbgType>(elements[i]));
			}

			SetResult(curId, dbgFuncType);
		}
		break;
	case BfIRCmd_DbgCreateAutoVariable:
		{
			CMD_PARAM(BeMDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, lineNo);
			CMD_PARAM(BeMDNode*, type);
			CMD_PARAM(int, initType);

			BF_ASSERT(type != NULL);

			auto dbgVar = mBeModule->mOwnedValues.Alloc<BeDbgVariable>();
			dbgVar->mName = name;
			dbgVar->mType = type;
			dbgVar->mScope = scope;
			dbgVar->mInitType = (BfIRInitType)initType;
			mActiveFunction->mDbgFunction->mVariables.push_back(dbgVar);

			BF_ASSERT(name != "__CRASH_AUTOVARIABLE__");

			SetResult(curId, dbgVar);
		}
		break;
	case BfIRCmd_DbgInsertValueIntrinsic:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(BeMDNode*, varInfo);

			auto dbgVar = BeValueDynCast<BeDbgVariable>(varInfo);
			//dbgVar->mValue = val;
			//dbgVar->mDeclDbgLoc = mBeModule->mCurDbgLoc;

			if (val == NULL)
			{
				val = mBeModule->GetConstant(mBeContext->GetPrimitiveType(BeTypeCode_Int32), (int64)0);
			}

			auto inst = mBeModule->AllocInst<BeDbgDeclareInst>();
			inst->mValue = val;
			inst->mDbgVar = dbgVar;
			inst->mIsValue = true;

			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_DbgInsertDeclare:
		{
			CMD_PARAM(BeValue*, val);
			CMD_PARAM(BeMDNode*, varInfo);
			CMD_PARAM(BeValue*, insertBefore);

			auto dbgVar = BeValueDynCast<BeDbgVariable>(varInfo);
			//dbgVar->mValue = val;
			//dbgVar->mDeclDbgLoc = mBeModule->mCurDbgLoc;

			auto inst = mBeModule->mAlloc.Alloc<BeDbgDeclareInst>();
			inst->mValue = val;
			inst->mDbgVar = dbgVar;
			inst->mIsValue = false;
			if (insertBefore == NULL)
				mBeModule->AddInst(inst);
			else
				NotImpl();

			SetResult(curId, inst);
		}
		break;
	case BfIRCmd_DbgLifetimeEnd:
		{
			CMD_PARAM(BeMDNode*, varInfo);
			auto inst = mBeModule->AllocInst<BeLifetimeEndInst>();
			inst->mPtr = varInfo;
		}
		break;
	case BfIRCmd_DbgCreateGlobalVariable:
		{
			CMD_PARAM(BeMDNode*, context);
			CMD_PARAM(String, name);
			CMD_PARAM(String, linkageName);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(BeMDNode*, type);
			CMD_PARAM(bool, isLocalToUnit);
			CMD_PARAM(BeConstant*, val);
			CMD_PARAM(BeMDNode*, decl);

			auto dbgGlobalVariable = mBeModule->mDbgModule->mGlobalVariables.Alloc();
			dbgGlobalVariable->mContext = context;
			dbgGlobalVariable->mName = name;
			dbgGlobalVariable->mLinkageName = linkageName;
			dbgGlobalVariable->mFile = (BeDbgFile*)file;
			dbgGlobalVariable->mLineNum = lineNum;
			dbgGlobalVariable->mType = type;
			dbgGlobalVariable->mIsLocalToUnit = isLocalToUnit;
			dbgGlobalVariable->mValue = val;
			dbgGlobalVariable->mDecl = decl;

			SetResult(curId, dbgGlobalVariable);
		}
		break;
	case BfIRCmd_DbgCreateLexicalBlock:
		{
			CMD_PARAM(BeMDNode*, scope);
			CMD_PARAM(BeMDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(int, col);

			auto dbgLexicalBlock = mBeModule->mOwnedValues.Alloc<BeDbgLexicalBlock>();
			BF_ASSERT(BeValueDynCast<BeDbgFile>(file) != NULL);
			dbgLexicalBlock->mFile = (BeDbgFile*)file;
			dbgLexicalBlock->mScope = scope;
			dbgLexicalBlock->mId = mBeModule->mCurLexBlockId++;

			SetResult(curId, dbgLexicalBlock);
		}
		break;
	case BfIRCmd_DbgCreateAnnotation:
		{
			CMD_PARAM(BeMDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(BeValue*, value);

			if (auto dbgFunc = BeValueDynCast<BeDbgFunction>(scope))
			{
				auto beType = value->GetType();
				BeDbgType** dbgTypePtr;
				if (mOnDemandTypeMap.TryAdd(beType, NULL, &dbgTypePtr))
				{
					auto dbgType = mBeModule->mDbgModule->mTypes.Alloc<BeDbgBasicType>();
					dbgType->mSize = beType->mSize;
					dbgType->mAlign = beType->mAlign;
					dbgType->mEncoding = llvm::dwarf::DW_ATE_signed;
					*dbgTypePtr = dbgType;
				}

				auto dbgVar = mBeModule->mOwnedValues.Alloc<BeDbgVariable>();
				dbgVar->mName = "#" + name;
				dbgVar->mType = *dbgTypePtr;
				dbgVar->mValue = value;
				dbgVar->mScope = scope;
				dbgFunc->mVariables.Add(dbgVar);

				auto inst = mBeModule->AllocInst<BeDbgDeclareInst>();
				inst->mValue = value;
				inst->mDbgVar = dbgVar;
				inst->mIsValue = true;
			}
			else
				NotImpl();
		}
		break;
	default:
		BF_FATAL("Unhandled");
		break;
	}

#ifdef CODEGEN_TRACK
	gBEMemReporter.EndSection();

	gBEMemReporterSize += mStream->GetReadPos() - curId;
#endif
}

void BeIRCodeGen::SetConfigConst(int idx, int value)
{
	BF_ASSERT(idx == (int)mConfigConsts.size());
	mConfigConsts.Add(value);
}

BeValue* BeIRCodeGen::TryGetBeValue(int id)
{
	auto& result = mResults[id];
	if (result.mKind != BeIRCodeGenEntryKind_Value)
		return NULL;
#ifdef BE_EXTRA_CHECKS
	BF_ASSERT(!result.mBeValue->mLifetimeEnded);
	BF_ASSERT(!result.mBeValue->mWasRemoved);
#endif
	return result.mBeValue;
}

BeValue* BeIRCodeGen::GetBeValue(int id)
{
	auto& result = mResults[id];
	BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Value);
#ifdef BE_EXTRA_CHECKS
	BF_ASSERT(!result.mBeValue->mLifetimeEnded);
	BF_ASSERT(!result.mBeValue->mWasRemoved);
#endif
	return result.mBeValue;
}

BeType* BeIRCodeGen::GetBeType(int id)
{
	auto& result = mResults[id];
	BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Type);
	return result.mBeType;
}

BeBlock* BeIRCodeGen::GetBeBlock(int id)
{
	auto& result = mResults[id];
	BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Block);
	return result.mBeBlock;
}

BeMDNode* BeIRCodeGen::GetBeMetadata(int id)
{
	auto& result = mResults[id];
	BF_ASSERT(result.mKind == BeIRCodeGenEntryKind_Metadata);
	return result.mBeMetadata;
}

BeType* BeIRCodeGen::GetBeTypeById(int id)
{
	return GetTypeEntry(id).mBeType;
}

BeState BeIRCodeGen::GetState()
{
	BeState state;
	state.mActiveFunction = mActiveFunction;
	state.mSavedDebugLocs = mSavedDebugLocs;
	state.mHasDebugLoc = mHasDebugLoc;

	state.mActiveBlock = mBeModule->mActiveBlock;
	state.mInsertPos = mBeModule->mInsertPos;
	state.mCurDbgLoc = mBeModule->mCurDbgLoc;
	state.mPrevDbgLocInline = mBeModule->mPrevDbgLocInline;
	state.mLastDbgLoc = mBeModule->mLastDbgLoc;

	return state;
}

void BeIRCodeGen::SetState(const BeState& state)
{
	mActiveFunction = state.mActiveFunction;
	mBeModule->mActiveFunction = mActiveFunction;
	mSavedDebugLocs = state.mSavedDebugLocs;
	mHasDebugLoc = state.mHasDebugLoc;

	mBeModule->mActiveBlock = state.mActiveBlock;
	mBeModule->mInsertPos = state.mInsertPos;
	mBeModule->mCurDbgLoc = state.mCurDbgLoc;
	mBeModule->mPrevDbgLocInline = state.mPrevDbgLocInline;
	mBeModule->mLastDbgLoc = state.mLastDbgLoc;
}