#include "BfIRCodeGen.h"
#include "BfModule.h"
#include "BeefySysLib/util/BeefPerf.h"
#include "BeefySysLib/util/Hash.h"

#ifdef BF_PLATFORM_WINDOWS
#include <io.h>
#endif

#pragma warning(push)
#pragma warning(disable:4141)
#pragma warning(disable:4146)
#pragma warning(disable:4291)
#pragma warning(disable:4244)
#pragma warning(disable:4267)
#pragma warning(disable:4624)
#pragma warning(disable:4800)
#pragma warning(disable:4996)

#include "llvm/IR/Module.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Attributes.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/TargetParser/Host.h"
//#include "llvm/Support/Dwarf.h"
#include "llvm/IR/DIBuilder.h"

//#include "llvm/ADT/Triple.h"
//#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/LinkAllAsmWriterComponents.h"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
//#include "llvm/MC/SubtargetFeature.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/Pass.h"
//#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils.h"
#include "llvm/Transforms/Scalar/InstSimplifyPass.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormattedStream.h"
//#include "llvm/Support/Host.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/PluginLoader.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
//#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
//#include "llvm/Target/TargetLibraryInfo.h"
#include "llvm/Target/TargetMachine.h"
//#include "llvm/Target/TargetSubtargetInfo.h"
//#include "llvm/Transforms/IPO/PassManagerBuilder.h"
//#include "llvm-c/Transforms/PassManagerBuilder.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Analysis/BasicAliasAnalysis.h"
//#include "llvm/Analysis/CFLAliasAnalysis.h"
//#include "llvm/Analysis/CFLAndersAliasAnalysis.h"
//#include "llvm/Analysis/CFLSteensAliasAnalysis.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/Analysis/ScopedNoAliasAA.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TypeBasedAliasAnalysis.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/ForceFunctionAttrs.h"
#include "llvm/Transforms/IPO/FunctionAttrs.h"
#include "llvm/Transforms/IPO/InferFunctionAttrs.h"
#include "llvm/Transforms/IPO/AlwaysInliner.h"
#include "llvm/Transforms/Instrumentation.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Scalar/GVN.h"
//#include "llvm/Transforms/Vectorize.h"
#include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h"
#include "llvm/Transforms/InstCombine/InstCombine.h"
#include "llvm/Transforms/Scalar/SimpleLoopUnswitch.h"
#include "llvm/Passes/PassBuilder.h"

//#include "llvm/Bitcode/ReaderWriter.h"

#include "llvm/Analysis/Passes.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/Scalar.h"
//#include "llvm/Transforms/Vectorize.h"
#include "llvm/Pass.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/Support/raw_ostream.h"

#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/TargetRegistry.h"

#include "llvm/LTO/LTOBackend.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h"
#include "llvm/Transforms/IPO/AlwaysInliner.h"
#include "llvm/Transforms/IPO.h"

#include "../LLVMUtils.h"

#pragma warning(pop)

void pm(llvm::Module* module);

USING_NS_BF;

#pragma warning(disable:4146)
#pragma warning(disable:4996)

struct BuiltinEntry
{
	const char* mName;
	bool operator<(const StringImpl& rhs) const
	{
		return strcmp(mName, rhs.c_str()) < 0;
	}
};

static const BuiltinEntry gIntrinEntries[] =
{
	{":PLATFORM"},
	{"abs"},
	{"add"},
	{"and"},
	{"atomic_add"},
	{"atomic_and"},
	{"atomic_cmpstore"},
	{"atomic_cmpstore_weak"},
	{"atomic_cmpxchg"},
	{"atomic_fence"},
	{"atomic_load"},
	{"atomic_max"},
	{"atomic_min"},
	{"atomic_nand"},
	{"atomic_or"},
	{"atomic_store"},
	{"atomic_sub"},
	{"atomic_umax"},
	{"atomic_umin"},
	{"atomic_xchg"},
	{"atomic_xor"},
	{"bswap"},
	{"cast"},
	{"cos"},
	{"cpuid"},
	{"debugtrap"},
	{"div"},
	{"eq"},
	{"floor"},
	{"free"},
	{"gt"},
	{"gte"},
	("index"),
	{"log"},
	{"log10"},
	{"log2"},
	{"lt"},
	{"lte"},
	{"malloc"},
	{"max"},
	{"memcpy"},
	{"memmove"},
	{"memset"},
	{"min"},
	{"mod"},
	{"mul"},
	{"neq"},
	{"not"},
	{"or"},
	{"pow"},
	{"powi"},
	{"returnaddress"},
	{"round"},
	{"sar"},
	{"shl"},
	{"shr"},
	{"shuffle"},
	{"sin"},
	{"sqrt"},
	{"sub"},
	{"va_arg"},
	{"va_end"},
	{"va_start"},
	{"xgetbv"},
	{"xor"},
};

#define CMD_PARAM(ty, name) ty name; Read(name);
#define CMD_PARAM_NOTRANS(ty, name) ty name; Read(name, NULL, BfIRSizeAlignKind_NoTransform);
BF_STATIC_ASSERT(BF_ARRAY_COUNT(gIntrinEntries) == BfIRIntrinsic_COUNT);

template <typename T>
class CmdParamVec : public llvm::SmallVector<T, 8>
{};

static int GetLLVMCallingConv(BfIRCallingConv callingConv, BfTargetTriple& targetTriple)
{
	int llvmCallingConv = llvm::CallingConv::C;

	if (targetTriple.GetMachineType() == BfMachineType_AArch64)
	{
		if (callingConv == BfIRCallingConv_CDecl)
			llvmCallingConv = llvm::CallingConv::C;
		else
			llvmCallingConv = llvm::CallingConv::PreserveMost;
	}
	else
	{
		if (callingConv == BfIRCallingConv_ThisCall)
			llvmCallingConv = llvm::CallingConv::X86_ThisCall;
		else if (callingConv == BfIRCallingConv_StdCall)
			llvmCallingConv = llvm::CallingConv::X86_StdCall;
		else if (callingConv == BfIRCallingConv_FastCall)
			llvmCallingConv = llvm::CallingConv::X86_FastCall;
		else if (callingConv == BfIRCallingConv_CDecl)
			llvmCallingConv = llvm::CallingConv::C;
	}

	return llvmCallingConv;
}

static llvm::GlobalValue::LinkageTypes LLVMMapLinkageType(BfIRLinkageType linkageType)
{
	llvm::GlobalValue::LinkageTypes llvmLinkageType;
	if (linkageType == BfIRLinkageType_Internal)
		llvmLinkageType = llvm::GlobalValue::InternalLinkage;
	else
		llvmLinkageType = llvm::GlobalValue::ExternalLinkage;
	return llvmLinkageType;
}

static llvm::Attribute::AttrKind LLVMMapAttribute(BfIRAttribute attr)
{
	switch (attr)
	{
	case BfIRAttribute_NoReturn: return llvm::Attribute::NoReturn;
	case BfIRAttribute_NoAlias: return llvm::Attribute::NoAlias;
	case BfIRAttribute_NoCapture: return llvm::Attribute::NoCapture;
	case BfIRAttribute_StructRet: return llvm::Attribute::StructRet;
	case BfIRAttribute_ZExt: return llvm::Attribute::ZExt;
	case BFIRAttribute_NoUnwind: return llvm::Attribute::NoUnwind;
	case BFIRAttribute_UWTable: return llvm::Attribute::UWTable;
	case BFIRAttribute_AlwaysInline: return llvm::Attribute::AlwaysInline;
	case BFIRAttribute_NoRecurse: return llvm::Attribute::NoRecurse;
	default: break;
	}
	return llvm::Attribute::None;
}

#ifdef BF_PLATFORM_WINDOWS
struct BfTempFile
{
	String mContents;
	String mFilePath;
	CritSect mCritSect;
	FILE* mFP;

	BfTempFile()
	{
		mFP = NULL;
	}

	~BfTempFile()
	{
		if (mFP != NULL)
			fclose(mFP);
		if (!mFilePath.IsEmpty())
			::DeleteFileW(UTF8Decode(mFilePath).c_str());
	}

	bool Create()
	{
		AutoCrit autoCrit(mCritSect);
		if (mFP != NULL)
			return false;

		WCHAR wPath[4096];
		wPath[0] = 0;
		::GetTempPathW(4096, wPath);

		WCHAR wFilePath[4096];
		wFilePath[0] = 0;
		GetTempFileNameW(wPath, L"bftmp", 0, wFilePath);

		mFilePath = UTF8Encode(wFilePath);
		mFP = _wfopen(wFilePath, L"w+D");
		return mFP != NULL;
	}

	String GetContents()
	{
		AutoCrit autoCrit(mCritSect);

		if (mFP != NULL)
		{
			fseek(mFP, 0, SEEK_END);
			int size = (int)ftell(mFP);
			fseek(mFP, 0, SEEK_SET);

			char* str = new char[size];
			int readSize = (int)fread(str, 1, size, mFP);
			mContents.Append(str, readSize);
			delete [] str;
			fclose(mFP);
			mFP = NULL;

			::DeleteFileW(UTF8Decode(mFilePath).c_str());
		}
		return mContents;
	}
};

static BfTempFile gTempFile;

static void AddStdErrCrashInfo()
{
	String tempContents = gTempFile.GetContents();
	if (!tempContents.IsEmpty())
		BfpSystem_AddCrashInfo(tempContents.c_str());
}

#endif

///

BfIRCodeGen::BfIRCodeGen()
{	
	mStream = NULL;
	mBfIRBuilder = NULL;
	mLLVMTargetMachine = NULL;

	mNopInlineAsm = NULL;
	mObjectCheckAsm = NULL;
	mOverflowCheckAsm = NULL;
	mHasDebugLoc = false;
	mAttrSet = NULL;
	mIRBuilder = NULL;
	mDIBuilder = NULL;
	mDICompileUnit = NULL;
	mActiveFunction = NULL;
	mActiveFunctionType = NULL;

	mLLVMContext = new llvm::LLVMContext();
	mLLVMModule = NULL;
	mIsCodeView = false;
	mHadDLLExport = false;
	mConstValIdx = 0;
	mCmdCount = 0;
	mCurLine = -1;

#ifdef BF_PLATFORM_WINDOWS
	if (::GetStdHandle(STD_ERROR_HANDLE) == 0)
	{
		if (gTempFile.Create())
		{
			_dup2(fileno(gTempFile.mFP), 2);
			BfpSystem_AddCrashInfoFunc(AddStdErrCrashInfo);
		}
	}
#endif
}

BfIRCodeGen::~BfIRCodeGen()
{
	mDebugLoc = llvm::DebugLoc();
	mSavedDebugLocs.Clear();
	
	for (auto typeEx : mIRTypeExs)
		delete typeEx;

	for (auto kv : mTypeCodeTypeExMap)
		delete kv.mValue;

	delete mStream;
	delete mIRBuilder;
	delete mDIBuilder;
	delete mLLVMTargetMachine;
	delete mLLVMModule;
	delete mLLVMContext;
}

void BfIRCodeGen::FatalError(const StringImpl &err)
{
	String failStr = "Fatal Error in Module: ";
	failStr += mModuleName;
	failStr += "\n";
	if (mLLVMModule != NULL)
	{
		if (mActiveFunction != NULL)
		{
			failStr += "Function: ";
			failStr += mActiveFunction->getName().str();
			failStr += "\n";
		}

		auto loc = mIRBuilder->getCurrentDebugLocation();
		auto dbgLoc = loc.getAsMDNode();
		if (dbgLoc != NULL)
		{
			std::string str;
			llvm::raw_string_ostream os(str);
			dbgLoc->print(os);
			failStr += "DbgLoc: ";
			failStr += str;
			failStr += "\n";

			llvm::MDNode* scope = loc.getScope();
			if (scope != NULL)
			{
				std::string str;
				llvm::raw_string_ostream os(str);
				scope->print(os);
				failStr += "Scope: ";
				failStr += str;
				failStr += "\n";
			}
		}
	}

	failStr += err;
	BF_FATAL(failStr);
}

void BfIRCodeGen::Fail(const StringImpl& error)
{
	if (mFailed)
		return;

	if (mHasDebugLoc)
	{
		auto dbgLoc = mIRBuilder->getCurrentDebugLocation();
		if (dbgLoc)
		{
			llvm::DIFile* file = NULL;
			if (llvm::DIScope* scope = llvm::dyn_cast<llvm::DIScope>(dbgLoc.getScope()))
			{
				BfIRCodeGenBase::Fail(StrFormat("%s at line %d:%d in %s/%s", error.c_str(), dbgLoc.getLine(), dbgLoc.getCol(), scope->getDirectory().data(), scope->getFilename().data()));
				return;
			}
		}
	}

	BfIRCodeGenBase::Fail(error);
}

void BfIRCodeGen::PrintModule()
{
	Beefy::debug_ostream os;
	mLLVMModule->print(os, NULL, false, true);
	os << "\n";
	os.flush();
}

void BfIRCodeGen::PrintFunction()
{
	Beefy::debug_ostream os;
	mActiveFunction->print(os);
	os << "\n";
	os.flush();
}

void pte(BfIRTypeEx* typeEx, int indent)
{
	Beefy::debug_ostream os;	
	typeEx->mLLVMType->print(os);
	os << "\n";
	os.flush();

	for (int i = 0; i < typeEx->mMembers.mSize; i++)
	{
		for (int i = 0; i < indent; i++)
			os << " ";
		os << i << ". ";
		os.flush();
		pte(typeEx->mMembers[i], indent + 1);
	}
}

void pte(BfIRTypeEx* typeEx)
{
	if (typeEx == NULL)
		return;
	pte(typeEx, 0);
}

void pve(const BfIRTypedValue& typedValue)
{	
	Beefy::debug_ostream os;	
	os << "Value: ";
	typedValue.mValue->print(os);
	os << "\nType: ";
	os.flush();
	pte(typedValue.mTypeEx);
}

void pirb(llvm::IRBuilder<>* irBuilder)
{
	Beefy::debug_ostream os;
	os << "Debug loc: ";
	auto debugLoc = irBuilder->getCurrentDebugLocation();
	if (debugLoc.get() == NULL)
		os << "NULL";
	else
		debugLoc->print(os);	
	os << "\n";
	os.flush();
}

void BfIRCodeGen::FixValues(llvm::StructType* structType, llvm::SmallVector<llvm::Value*, 8>& values)
{
	if (values.size() >= structType->getNumElements())
		return;

	int readIdx = (int)values.size() - 1;
	values.resize(structType->getNumElements());
	for (int i = (int)values.size() - 1; i >= 0; i--)
	{
		if (values[readIdx]->getType() == structType->getElementType(i))
		{
			values[i] = values[readIdx];
			readIdx--;
		}
		else if (structType->getElementType(i)->isArrayTy())
		{
			values[i] = llvm::ConstantAggregateZero::get(structType->getElementType(i));
		}
		else
		{
			BF_FATAL("Malformed structure values");
		}
	}
}

void BfIRCodeGen::FixIndexer(llvm::Value*& val)
{
	if ((int)val->getType()->getScalarSizeInBits() > mPtrSize * 8)
		val = mIRBuilder->CreateIntCast(val, llvm::Type::getInt32Ty(*mLLVMContext), false);
}

BfTypeCode BfIRCodeGen::GetTypeCode(llvm::Type* type, bool isSigned)
{
	if (type->isIntegerTy())
	{
		switch (type->getIntegerBitWidth())
		{
		case 1:
			return BfTypeCode_Boolean;
		case 8:
			return isSigned ? BfTypeCode_Int8 : BfTypeCode_UInt8;
		case 16:
			return isSigned ? BfTypeCode_Int16 : BfTypeCode_UInt16;
		case 32:
			return isSigned ? BfTypeCode_Int32 : BfTypeCode_UInt32;
		case 64:
			return isSigned ? BfTypeCode_Int64 : BfTypeCode_UInt64;
		}
	}

	if (type->isFloatingPointTy())
		return BfTypeCode_Float;
	if (type->isDoubleTy())
		return BfTypeCode_Double;

	return BfTypeCode_None;
}

llvm::Type* BfIRCodeGen::GetLLVMType(BfTypeCode typeCode, bool& isSigned)
{
	if ((typeCode == BfTypeCode_IntPtr) || (typeCode == BfTypeCode_UIntPtr))
	{
		/*isSigned = typeCode == BfTypeCode_IntPtr;
		if (mModule->mSystem->mPtrSize == 4)
			return llvm::Type::getInt32Ty(*mLLVMContext);
		else
			return llvm::Type::getInt64Ty(*mLLVMContext);*/
		BF_FATAL("Unsupported");
	}

	isSigned = false;
	switch (typeCode)
	{
	case BfTypeCode_None:
		return llvm::Type::getVoidTy(*mLLVMContext);
	case BfTypeCode_NullPtr:
		return llvm::PointerType::get(*mLLVMContext, 0);
	case BfTypeCode_Boolean:
		return llvm::Type::getInt1Ty(*mLLVMContext);
	case BfTypeCode_Int8:
		isSigned = true;
		return llvm::Type::getInt8Ty(*mLLVMContext);
	case BfTypeCode_UInt8:
	case BfTypeCode_Char8:
		return llvm::Type::getInt8Ty(*mLLVMContext);
	case BfTypeCode_Int16:
		isSigned = true;
		return llvm::Type::getInt16Ty(*mLLVMContext);
	case BfTypeCode_UInt16:
	case BfTypeCode_Char16:
		return llvm::Type::getInt16Ty(*mLLVMContext);
	case BfTypeCode_Int24:
		isSigned = true;
		return llvm::Type::getIntNTy(*mLLVMContext, 24);
	case BfTypeCode_UInt24:
		return llvm::Type::getIntNTy(*mLLVMContext, 24);
	case BfTypeCode_Int32:
		isSigned = true;
		return llvm::Type::getInt32Ty(*mLLVMContext);
	case BfTypeCode_UInt32:
	case BfTypeCode_Char32:
		return llvm::Type::getInt32Ty(*mLLVMContext);
	case BfTypeCode_Int40:
		isSigned = true;
		return llvm::Type::getIntNTy(*mLLVMContext, 40);
	case BfTypeCode_UInt40:
		return llvm::Type::getIntNTy(*mLLVMContext, 40);
	case BfTypeCode_Int48:
		isSigned = true;
		return llvm::Type::getIntNTy(*mLLVMContext, 48);
	case BfTypeCode_UInt48:
		return llvm::Type::getIntNTy(*mLLVMContext, 48);
	case BfTypeCode_Int56:
		isSigned = true;
		return llvm::Type::getIntNTy(*mLLVMContext, 56);
	case BfTypeCode_UInt56:
		return llvm::Type::getIntNTy(*mLLVMContext, 56);
	case BfTypeCode_Int64:
		isSigned = true;
		return llvm::Type::getInt64Ty(*mLLVMContext);
	case BfTypeCode_UInt64:
		return llvm::Type::getInt64Ty(*mLLVMContext);
	case BfTypeCode_Int128:
		isSigned = true;
		return llvm::Type::getInt128Ty(*mLLVMContext);
	case BfTypeCode_UInt128:
		return llvm::Type::getInt128Ty(*mLLVMContext);
	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:
		return llvm::Type::getFloatTy(*mLLVMContext);
	case BfTypeCode_Double:
		return llvm::Type::getDoubleTy(*mLLVMContext);
	case BfTypeCode_Float2:
		return llvm::FixedVectorType::get(llvm::Type::getFloatTy(*mLLVMContext), 2);
	case BfTypeCode_FloatX2:
		return llvm::ArrayType::get(llvm::Type::getFloatTy(*mLLVMContext), 2);
	case BfTypeCode_FloatX3:
		return llvm::ArrayType::get(llvm::Type::getFloatTy(*mLLVMContext), 3);
	case BfTypeCode_FloatX4:
		return llvm::ArrayType::get(llvm::Type::getFloatTy(*mLLVMContext), 4);
	case BfTypeCode_DoubleX2:
		return llvm::ArrayType::get(llvm::Type::getDoubleTy(*mLLVMContext), 2);
	case BfTypeCode_DoubleX3:
		return llvm::ArrayType::get(llvm::Type::getDoubleTy(*mLLVMContext), 3);
	case BfTypeCode_DoubleX4:
		return llvm::ArrayType::get(llvm::Type::getDoubleTy(*mLLVMContext), 4);
	case BfTypeCode_Int64X2:
		return llvm::ArrayType::get(llvm::Type::getInt64Ty(*mLLVMContext), 2);
	case BfTypeCode_Int64X3:
		return llvm::ArrayType::get(llvm::Type::getInt64Ty(*mLLVMContext), 3);
	case BfTypeCode_Int64X4:
		return llvm::ArrayType::get(llvm::Type::getInt64Ty(*mLLVMContext), 4);
	default: break;
	}
	return NULL;
}

BfIRTypeEx* BfIRCodeGen::GetTypeEx(BfTypeCode typeCode, bool& isSigned)
{
	BfIRTypeEx** valuePtr = NULL;
	if (mTypeCodeTypeExMap.TryAdd(typeCode, NULL, &valuePtr))
	{
		BfIRTypeEx* typeEx = new BfIRTypeEx();
		typeEx->mLLVMType = GetLLVMType(typeCode, isSigned);

		if (typeEx->mLLVMType->isPointerTy())
		{
			// Make void* actually be an i8*
			typeEx->mMembers.Add(GetTypeEx(llvm::Type::getInt8Ty(*mLLVMContext)));
		}

		if (auto arrType = llvm::dyn_cast<llvm::ArrayType>(typeEx->mLLVMType))
		{
			typeEx->mMembers.Add(GetTypeEx(arrType->getElementType()));
		}

		if (auto vectorType = llvm::dyn_cast<llvm::VectorType>(typeEx->mLLVMType))
		{
			typeEx->mMembers.Add(GetTypeEx(vectorType->getElementType()));
		}

		*valuePtr = typeEx;
	}
	else
	{
		isSigned = false;
		switch (typeCode)
		{		
		case BfTypeCode_Int8:			
		case BfTypeCode_Int16:			
		case BfTypeCode_Int24:						
		case BfTypeCode_Int32:			
		case BfTypeCode_Int40:			
		case BfTypeCode_Int48:			
		case BfTypeCode_Int56:			
		case BfTypeCode_Int64:			
		case BfTypeCode_Int128:
			isSigned = true;		
		}		
	}
	return *valuePtr;
}

BfIRTypeEx* BfIRCodeGen::GetTypeEx(llvm::Type* llvmType)
{	
	BfIRTypeEx** valuePtr = NULL;
	if (mLLVMTypeExMap.TryAdd(llvmType, NULL, &valuePtr))
	{
		BfIRTypeEx* typeEx = new BfIRTypeEx();
		mIRTypeExs.Add(typeEx);
		typeEx->mLLVMType = llvmType;		
		*valuePtr = typeEx;
	}	
	return *valuePtr;
}

BfIRTypeEx* BfIRCodeGen::CreateTypeEx(llvm::Type* llvmType)
{
	BfIRTypeEx* typeEx = new BfIRTypeEx();
	mIRTypeExs.Add(typeEx);
	typeEx->mLLVMType = llvmType;
	return typeEx;
}

BfIRTypeEx* BfIRCodeGen::GetPointerTypeEx(BfIRTypeEx* elementType)
{	
	BF_ASSERT(elementType != NULL);
	BfIRTypeEx** valuePtr = NULL;
	if (mPointerTypeExMap.TryAdd(elementType, NULL, &valuePtr))
	{
		BfIRTypeEx* typeEx = new BfIRTypeEx();
		mIRTypeExs.Add(typeEx);
		typeEx->mLLVMType = llvm::PointerType::get(*mLLVMContext, 0);		
		typeEx->mMembers.Add(elementType);
		*valuePtr = typeEx;
	}
	return *valuePtr;
}

BfIRTypeEx* BfIRCodeGen::GetTypeMember(BfIRTypeEx* typeEx, int idx)
{
	if ((idx < 0) || (idx >= typeEx->mMembers.mSize))
	{
		Fail("BfIRTypeEx GetTypeMember OOB");

		bool isSigned;
		return GetTypeEx(BfTypeCode_Int8, isSigned);
	}
	return typeEx->mMembers[idx];
}

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

BfIRTypeEntry* BfIRCodeGen::GetTypeEntry(BfIRTypeEx* type)
{
	int typeId = 0;
	if (!mTypeToTypeIdMap.TryGetValue(type, &typeId))
		return NULL;
	return &GetTypeEntry(typeId);
}

void BfIRCodeGen::SetResult(int id, llvm::Value* value)
{	
	BF_ASSERT(!value->getType()->isAggregateType());
	BF_ASSERT(!value->getType()->isPointerTy());

	BfIRCodeGenEntry entry;
	entry.mKind = BfIRCodeGenEntryKind_LLVMValue;
	entry.mLLVMValue = value;
	mResults.TryAdd(id, entry);
}

void BfIRCodeGen::SetResult(int id, const BfIRTypedValue& value)
{
	BfIRCodeGenEntry entry;
	entry.mKind = BfIRCodeGenEntryKind_TypedValue;
	entry.mTypedValue = value;
	mResults.TryAdd(id, entry);
}

void BfIRCodeGen::SetResultAligned(int id, llvm::Value* value)
{
	BfIRCodeGenEntry entry;
	entry.mKind = BfIRCodeGenEntryKind_LLVMValue_Aligned;
	entry.mLLVMValue = value;
	mResults.TryAdd(id, entry);
}

void BfIRCodeGen::SetResultAligned(int id, const BfIRTypedValue& value)
{
	BfIRCodeGenEntry entry;
	entry.mKind = BfIRCodeGenEntryKind_TypedValue_Aligned;
	entry.mTypedValue = value;
	mResults.TryAdd(id, entry);
}

void BfIRCodeGen::SetResult(int id, llvm::Type* type)
{
	BfIRCodeGenEntry entry;
	entry.mKind = BfIRCodeGenEntryKind_LLVMType;
	entry.mLLVMType = type;
	mResults.TryAdd(id, entry);
}

void BfIRCodeGen::SetResult(int id, BfIRTypeEx* typeEx)
{
	BfIRCodeGenEntry entry;
	entry.mKind = BfIRCodeGenEntryKind_TypeEx;
	entry.mTypeEx = typeEx;
	mResults.TryAdd(id, entry);
}

void BfIRCodeGen::SetResult(int id, llvm::BasicBlock* value)
{
	BfIRCodeGenEntry entry;
	entry.mKind = BfIRCodeGenEntryKind_LLVMBasicBlock;
	entry.mLLVMBlock = value;
	mResults.TryAdd(id, entry);
}

void BfIRCodeGen::SetResult(int id, llvm::MDNode* md)
{
	BfIRCodeGenEntry entry;
	entry.mKind = BfIRCodeGenEntryKind_LLVMMetadata;
	entry.mLLVMMetadata = md;
	mResults.TryAdd(id, entry);
}

void BfIRCodeGen::ProcessBfIRData(const BfSizedArray<uint8>& buffer)
{
    // Diagnostic handlers were unified in LLVM change 5de2d189e6ad, so starting
    // with LLVM 13 this function is gone.
	/*struct InlineAsmErrorHook
	{
		static void StaticHandler(const llvm::SMDiagnostic& diag, void *context, unsigned locCookie)
		{
			if (diag.getKind() == llvm::SourceMgr::DK_Error)
			{
				BfIRCodeGen* irCodeGen = (BfIRCodeGen*)context;

				if (!irCodeGen->mErrorMsg.empty())
					irCodeGen->mErrorMsg += "\n";
				irCodeGen->mErrorMsg += StrFormat("Inline assembly error: \"%s\" : %s", diag.getMessage().data(), diag.getLineContents().data());
			}
		}
	};
	mLLVMContext->setInlineAsmDiagnosticHandler(InlineAsmErrorHook::StaticHandler, this);*/

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

	while (mStream->GetReadPos() < buffer.mSize)
	{
		if (mFailed)
			break;
		HandleNextCmd();
	}

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

int64 BfIRCodeGen::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 BfIRCodeGen::Read(StringImpl& str)
{
	int len = (int)ReadSLEB128();
	str.Append('?', len);
	mStream->Read((void*)str.c_str(), len);
}

void BfIRCodeGen::Read(int& i)
{
	i = (int)ReadSLEB128();
}

void BfIRCodeGen::Read(int64& i)
{
	i = ReadSLEB128();
}

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

void BfIRCodeGen::Read(bool& val)
{
	val = mStream->Read() != 0;
}

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

void BfIRCodeGen::Read(BfIRTypeEntry*& type)
{
	int typeId = (int)ReadSLEB128();
	type = &GetTypeEntry(typeId);
}

void BfIRCodeGen::Read(BfIRTypeEx*& typeEx, BfIRTypeEntry** outTypeEntry)
{
	typeEx = NULL;

	BfIRType::TypeKind typeKind = (BfIRType::TypeKind)mStream->Read();
	if (typeKind == BfIRType::TypeKind::TypeKind_None)
		return;	

	if (typeKind == BfIRType::TypeKind::TypeKind_Stream)
	{
		int streamId = (int)ReadSLEB128();
		if (streamId == -1)
		{	
			typeEx = NULL;
			return;
		}
		auto& result = mResults[streamId];		
		BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_TypeEx);
		typeEx = result.mTypeEx;
		return;
	}

	if (typeKind == BfIRType::TypeKind::TypeKind_SizedArray)
	{
		CMD_PARAM(BfIRTypeEx*, elementType);
		CMD_PARAM(int, length);
		typeEx = new BfIRTypeEx();
		typeEx->mLLVMType = llvm::ArrayType::get(elementType->mLLVMType, length);
		BF_ASSERT(elementType != NULL);
		typeEx->mMembers.Add(elementType);
		mIRTypeExs.Add(typeEx);
		return;
	}

	int typeId = (int)ReadSLEB128();

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

	auto& typeEntry = GetTypeEntry(typeId);
	if (typeKind == BfIRType::TypeKind::TypeKind_TypeId)
		typeEx = typeEntry.mType;
	else if (typeKind == BfIRType::TypeKind::TypeKind_TypeInstId)
		typeEx = typeEntry.mInstType;
	else if (typeKind == BfIRType::TypeKind::TypeKind_TypeInstPtrId)
		typeEx = GetPointerTypeEx(typeEntry.mInstType);
	if (outTypeEntry != NULL)
		*outTypeEntry = &typeEntry;
}

void BfIRCodeGen::Read(llvm::Type*& llvmType, BfIRTypeEntry** outTypeEntry)
{
	BfIRTypeEx* typeEx = NULL;
	Read(typeEx, outTypeEntry);
	if (typeEx != NULL)
	{
		llvmType = typeEx->mLLVMType;
	}
	else
		llvmType = NULL;
}

void BfIRCodeGen::Read(llvm::FunctionType*& llvmType)
{
	int streamId = (int)ReadSLEB128();
	auto& result = mResults[streamId];

	if (result.mKind == BfIRCodeGenEntryKind_TypeEx)
	{		
		llvmType = (llvm::FunctionType*)result.mTypeEx->mLLVMType;
		return;
	}

	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMType);
	llvmType = (llvm::FunctionType*)result.mLLVMType;
}

void BfIRCodeGen::ReadFunctionType(BfIRTypeEx*& typeEx)
{
	int streamId = (int)ReadSLEB128();
	auto& result = mResults[streamId];

	if (result.mKind == BfIRCodeGenEntryKind_TypeEx)
	{
		typeEx = result.mTypeEx;
		return;
	}

	BF_FATAL("Invalid path in ReadFunctionType");

	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMType);
	typeEx = GetTypeEx(result.mLLVMType);
}

void BfIRCodeGen::FixTypedValue(BfIRTypedValue& typedValue)
{
	if ((typedValue.mValue != NULL) && (typedValue.mTypeEx == NULL))
	{
		typedValue.mTypeEx = GetTypeEx(typedValue.mValue->getType());
		BF_ASSERT(!typedValue.mValue->getType()->isStructTy());
		BF_ASSERT(!typedValue.mValue->getType()->isFunctionTy());
	}
}

void BfIRCodeGen::Read(BfIRTypedValue& typedValue, BfIRCodeGenEntry** codeGenEntry, BfIRSizeAlignKind sizeAlignKind)
{
	typedValue.mValue = NULL;
	typedValue.mTypeEx = NULL;

	BfIRParamType paramType = (BfIRParamType)mStream->Read();
	if (paramType == BfIRParamType_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(BfIRTypeEx*, varType);
				CMD_PARAM(bool, isConstant);
				BfIRLinkageType linkageType = (BfIRLinkageType)mStream->Read();
				CMD_PARAM(llvm::Constant*, initializer);
				CMD_PARAM(String, name);
				CMD_PARAM(bool, isTLS);

				llvm::GlobalVariable* globalVariable = mLLVMModule->getGlobalVariable(name.c_str(), true);
				if (globalVariable == NULL)
				{
					globalVariable = mLLVMModule->getGlobalVariable(name.c_str());
					if (globalVariable == NULL)
					{
						globalVariable = new llvm::GlobalVariable(
							*mLLVMModule,
							varType->mLLVMType,
							isConstant,
							LLVMMapLinkageType(linkageType),
							initializer,
							name.c_str(), NULL, isTLS ? llvm::GlobalValue::GeneralDynamicTLSModel : llvm::GlobalValue::NotThreadLocal);
					}
				}
				typedValue.mTypeEx = GetPointerTypeEx(varType);
				typedValue.mValue = globalVariable;
				SetResult(streamId, typedValue);
			}
			else
				typedValue = GetTypedValue(streamId);
			FixTypedValue(typedValue);
			return;
		}
		/*else if (constType == BfConstType_GlobalVar_TypeInst)
		{
			CMD_PARAM(int, streamId);
			if (streamId == -1)
			{
				int streamId = mStream->GetReadPos();

				CMD_PARAM(int, varTypeId);
				auto& typeEntry = GetTypeEntry(varTypeId);
				auto varType = typeEntry.mInstLLVMType;

				CMD_PARAM(bool, isConstant);
				BfIRLinkageType linkageType = (BfIRLinkageType)mStream->Read();
				CMD_PARAM(llvm::Constant*, initializer);
				CMD_PARAM(String, name);
				CMD_PARAM(bool, isTLS);

				auto globalVariable = new llvm::GlobalVariable(
					*mLLVMModule,
					varType,
					isConstant,
					LLVMMapLinkageType(linkageType),
					initializer,
					name, NULL, isTLS ? llvm::GlobalValue::GeneralDynamicTLSModel : llvm::GlobalValue::NotThreadLocal);
				llvmValue = globalVariable;

				SetResult(streamId, globalVariable);
			}
			else
				llvmValue = GetLLVMValue(streamId);
			return;
		}*/
		else if ((constType == BfConstType_BitCast) || (constType == BfConstType_BitCastNull))
		{
			CMD_PARAM(llvm::Constant*, target);
			CMD_PARAM(BfIRTypeEx*, toType);

			typedValue.mTypeEx = toType;
			if ((constType == BfConstType_BitCastNull) && (toType->mLLVMType->isIntegerTy()))
			{
				typedValue.mValue = llvm::ConstantInt::getNullValue(toType->mLLVMType);
			}
			else if (target->getType()->isIntegerTy())
				typedValue.mValue = llvm::ConstantExpr::getIntToPtr(target, toType->mLLVMType);
			else
				typedValue.mValue = llvm::ConstantExpr::getBitCast(target, toType->mLLVMType);
			FixTypedValue(typedValue);
			return;
		}
		else if (constType == BfConstType_GEP32_1)
		{
			CMD_PARAM(BfIRTypedValue, target);
			CMD_PARAM(int, idx0);
			llvm::Value* gepArgs[] = {
				llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), idx0)};
			
			auto compositeType = GetTypeMember(target.mTypeEx, 0);
			auto constant = llvm::dyn_cast<llvm::Constant>(target.mValue);

			typedValue.mTypeEx = target.mTypeEx;
			typedValue.mValue = llvm::ConstantExpr::getInBoundsGetElementPtr(compositeType->mLLVMType, constant, gepArgs);
			FixTypedValue(typedValue);
			return;
		}
		else if (constType == BfConstType_GEP32_2)
		{
			CMD_PARAM(BfIRTypedValue, target);
			CMD_PARAM(int, idx0);
			CMD_PARAM(int, idx1);
			llvm::Value* gepArgs[] = {
				llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), idx0),
				llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), idx1)};

			auto compositeType = GetTypeMember(target.mTypeEx, 0);
			int elemIdx = BF_MAX(BF_MIN(idx1, (int)compositeType->mMembers.mSize - 1), 0);
			auto elemType = GetTypeMember(compositeType, elemIdx);

			auto constant = llvm::dyn_cast<llvm::Constant>(target.mValue);
			typedValue.mValue = llvm::ConstantExpr::getInBoundsGetElementPtr(compositeType->mLLVMType, constant, gepArgs);
			typedValue.mTypeEx = GetPointerTypeEx(elemType);						
			return;
		}
		else if (constType == BfConstType_ExtractValue)
		{
			CMD_PARAM(BfIRTypedValue, target);
			CMD_PARAM(int, idx0);			
			
			auto compositeType = target.mTypeEx;
			int elemIdx = BF_MIN(idx0, (int)compositeType->mMembers.mSize - 1);
			auto elemType = GetTypeMember(compositeType, elemIdx);

			typedValue.mTypeEx = elemType;
			if (auto constant = llvm::dyn_cast<llvm::Constant>(target.mValue))			
				typedValue.mValue = constant->getAggregateElement(llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), idx0));							
			FixTypedValue(typedValue);
			return;
		}
		else if (constType == BfConstType_PtrToInt)
		{
			CMD_PARAM(llvm::Constant*, target);
			BfTypeCode toTypeCode = (BfTypeCode)mStream->Read();

			bool isSigned;
			BfIRTypeEx* toType = GetTypeEx(toTypeCode, isSigned);
			typedValue.mTypeEx = toType;
			typedValue.mValue = llvm::ConstantExpr::getPtrToInt(target, toType->mLLVMType);
			FixTypedValue(typedValue);
			return;
		}
		else if (constType == BfConstType_IntToPtr)
		{
			CMD_PARAM(llvm::Constant*, target);
			CMD_PARAM(BfIRTypeEx*, toType);
			typedValue.mTypeEx = toType;
			typedValue.mValue = llvm::ConstantExpr::getIntToPtr(target, toType->mLLVMType);
			FixTypedValue(typedValue);
			return;
		}
		else if (constType == BfConstType_AggZero)
		{
			BfIRTypeEntry* typeEntry = NULL;
			BfIRTypeEx* type = NULL;
			Read(type, &typeEntry);
			typedValue.mTypeEx = type;
			if ((sizeAlignKind == BfIRSizeAlignKind_Aligned) && (typeEntry != NULL))
				typedValue.mValue = llvm::ConstantAggregateZero::get(GetSizeAlignedType(typeEntry)->mLLVMType);
			else
				typedValue.mValue = llvm::ConstantAggregateZero::get(type->mLLVMType);
			FixTypedValue(typedValue);
			return;
		}
		else if (constType == BfConstType_ArrayZero8)
		{
			CMD_PARAM(int, count);
			auto arrType = llvm::ArrayType::get(llvm::Type::getInt8Ty(*mLLVMContext), count);
			typedValue.mValue = llvm::ConstantAggregateZero::get(arrType);
			FixTypedValue(typedValue);
			return;
		}
		else if (constType == BfConstType_Agg)
		{
			BfIRTypeEntry* typeEntry = NULL;
			BfIRTypeEx* type = NULL;
			Read(type, &typeEntry);
			CmdParamVec<llvm::Constant*> values;
			Read(values, type->mLLVMType->isArrayTy() ? BfIRSizeAlignKind_Aligned : BfIRSizeAlignKind_Original);

			typedValue.mTypeEx = type;

			if (auto arrayType = llvm::dyn_cast<llvm::ArrayType>(type->mLLVMType))
			{
				int fillCount = (int)(arrayType->getNumElements() - values.size());
				if (fillCount > 0)
				{
					auto lastValue = values.back();
					for (int i = 0; i < fillCount; i++)
						values.push_back(lastValue);
				}
				typedValue.mValue = llvm::ConstantArray::get(arrayType, values);
			}
			else if (auto structType = llvm::dyn_cast<llvm::StructType>(type->mLLVMType))
			{
				for (int i = 0; i < (int)values.size(); i++)
				{
					if (values[i]->getType() != structType->getElementType(i))
					{
						auto valArrayType = llvm::dyn_cast<llvm::ArrayType>(values[i]->getType());
						if (valArrayType != NULL)
						{
							if (valArrayType->getNumElements() == 0)
							{
								values[i] = llvm::ConstantAggregateZero::get(structType->getElementType(i));
							}
						}
					}
				}

				if ((sizeAlignKind == BfIRSizeAlignKind_Aligned) && (typeEntry != NULL))
				{
					auto alignedTypeEx = GetSizeAlignedType(typeEntry);
					auto alignedType = llvm::dyn_cast<llvm::StructType>(alignedTypeEx->mLLVMType);
					if (type != alignedTypeEx)
						values.push_back(llvm::ConstantAggregateZero::get(alignedType->getElementType(alignedType->getNumElements() - 1)));
					typedValue.mTypeEx = alignedTypeEx;
					typedValue.mValue = llvm::ConstantStruct::get(alignedType, values);
				}
				else
				{					
					typedValue.mValue = llvm::ConstantStruct::get(structType, values);
				}
			}
			else if (auto vecType = llvm::dyn_cast<llvm::VectorType>(type->mLLVMType))
			{
				typedValue.mValue = llvm::ConstantVector::get(values);
			}
			else
			{
				typedValue.mValue = NULL;
				Fail("Bad type");
			}

			FixTypedValue(typedValue);
			return;
		}
		else if (constType == BfConstType_Undef)
		{
			CMD_PARAM(BfIRTypeEx*, type);
			typedValue.mTypeEx = type;
			typedValue.mValue = llvm::UndefValue::get(type->mLLVMType);			
			return;
		}
		else if (constType == BfConstType_TypeOf)
		{
			CMD_PARAM(BfIRTypeEx*, type);
			typedValue = mReflectDataMap[type];
			BF_ASSERT(typedValue.mValue != NULL);			
			return;
		}
		else if (constType == BfConstType_TypeOf_WithData)
		{
			CMD_PARAM(BfIRTypeEx*, type);
			CMD_PARAM(BfIRTypedValue, value);
			mReflectDataMap[type] = value;
			typedValue = value;			
			return;
		}

		bool isSigned;
		llvm::Type* llvmConstType = GetLLVMType(typeCode, isSigned);

		if (typeCode == BfTypeCode_Float)
		{
			float f;
			mStream->Read(&f, sizeof(float));
			typedValue.mValue = llvm::ConstantFP::get(llvmConstType, f);
		}
		else if (typeCode == BfTypeCode_Double)
		{
			double d;
			mStream->Read(&d, sizeof(double));
			typedValue.mValue = llvm::ConstantFP::get(llvmConstType, d);
		}
		else if (typeCode == BfTypeCode_Boolean)
		{
			CMD_PARAM(bool, boolVal);
			typedValue.mValue = llvm::ConstantInt::get(llvmConstType, boolVal ? 1 : 0);
		}
		else if (typeCode == BfTypeCode_None)
		{
			typedValue.mValue = NULL;
		}
		else if (typeCode == BfTypeCode_NullPtr)
		{
			CMD_PARAM(llvm::Type*, nullType);
			if (nullType != NULL)
				typedValue.mValue = llvm::ConstantPointerNull::get((llvm::PointerType*)nullType);
			else
				typedValue.mValue = llvm::ConstantPointerNull::get((llvm::PointerType*)llvmConstType);
		}
		else if (BfIRBuilder::IsInt(typeCode))
		{
			int64 intVal = ReadSLEB128();
			auto constVal = llvm::ConstantInt::get(llvmConstType, intVal);
			auto constInt = (llvm::ConstantInt*)constVal;
			typedValue.mValue = constInt;
		}
		else
		{
			BF_FATAL("Unhandled");
		}
	}
	else if (paramType == BfIRParamType_Arg)
	{
		int argIdx = mStream->Read();
		if (argIdx >= mActiveFunction->arg_size())
		{
			FatalError(StrFormat("ARG out of bounds %d", argIdx));
		}

		auto typeEx = mActiveFunctionType;

		BF_ASSERT(argIdx < mActiveFunction->arg_size());
		auto argItr = mActiveFunction->arg_begin();
		for (int i = 0; i < argIdx; i++)
			argItr++;
		typedValue.mValue = &(*argItr);
		typedValue.mTypeEx = GetTypeMember(typeEx, argIdx + 1);
	}
	else
	{
		int cmdId = -1;
		if (paramType == BfIRParamType_StreamId_Abs8)
		{
			cmdId = mStream->Read();
		}
		else if (paramType == BfIRParamType_StreamId_Rel)
		{
			cmdId = mCmdCount - (int)ReadSLEB128();
		}
		else
		{
			cmdId = mCmdCount - (paramType - BfIRParamType_StreamId_Back1) - 1;
		}		

		auto& result = mResults[cmdId];

		if ((codeGenEntry != NULL) && (result.mKind != BfIRCodeGenEntryKind_None))		
			*codeGenEntry = &result;					

		if (result.mKind == BfIRCodeGenEntryKind_TypedValue_Aligned)
		{
			typedValue = result.mTypedValue;

			BfIRTypeEx* normalType = NULL;
			if (mAlignedTypeToNormalType.TryGetValue(typedValue.mTypeEx, &normalType)) 			
				typedValue.mTypeEx = normalType; 			

			return;
		}

		if (result.mKind == BfIRCodeGenEntryKind_TypedValue)
		{			
			typedValue = result.mTypedValue;
			return;
		}

		if (result.mKind != BfIRCodeGenEntryKind_LLVMValue)
		{
			if ((codeGenEntry != NULL) && (result.mKind != BfIRCodeGenEntryKind_None))
			{
				*codeGenEntry = &result;
				return;
			}
		}

		if (result.mKind == BfIRCodeGenEntryKind_LLVMValue_Aligned)
		{
			typedValue.mValue = result.mLLVMValue;
			if (sizeAlignKind != BfIRSizeAlignKind_Original)
				return;

			llvm::Type* normalType = NULL;
			//TODO: if (auto ptrType = llvm::dyn_cast<llvm::PointerType>(llvmValue->getType()))
			{
				
// 				if (mAlignedTypeToNormalType.TryGetValue(ptrType->getElementType(), &normalType))
// 				{
// 					llvmValue = mIRBuilder->CreateBitCast(llvmValue, normalType->getPointerTo());
// 					return;
// 				}
			}
		}

		BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMValue);
		typedValue.mValue = result.mLLVMValue;		
	}
	FixTypedValue(typedValue);
}

void BfIRCodeGen::Read(llvm::Value*& llvmValue, BfIRCodeGenEntry** codeGenEntry, BfIRSizeAlignKind sizeAlignKind)
{
	BfIRTypedValue typedValue;
	Read(typedValue, codeGenEntry, sizeAlignKind);
	llvmValue = typedValue.mValue;
}

void BfIRCodeGen::Read(llvm::Constant*& llvmConstant, BfIRSizeAlignKind sizeAlignKind)
{
	llvm::Value* value;
	Read(value, NULL, sizeAlignKind);
	if (value == NULL)
	{
		llvmConstant = NULL;
	}
	else
	{
		BF_ASSERT(llvm::isa<llvm::Constant>(value));
		llvmConstant = (llvm::Constant*)value;
	}
}

void BfIRCodeGen::Read(llvm::Function*& llvmFunc)
{
	int streamId = (int)ReadSLEB128();
	if (streamId == -1)
	{
		llvmFunc = NULL;
		return;
	}
	auto& result = mResults[streamId];	
	if (result.mKind == BfIRCodeGenEntryKind_TypedValue)
	{
		llvmFunc = (llvm::Function*)result.mTypedValue.mValue;
		return;
	}

	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMValue);
	BF_ASSERT(llvm::isa<llvm::Function>(result.mLLVMValue));
	llvmFunc = (llvm::Function*)result.mLLVMValue;
}

void BfIRCodeGen::ReadFunction(BfIRTypedValue& typedValue)
{
	int streamId = (int)ReadSLEB128();
	if (streamId == -1)
	{
		typedValue.mValue = NULL;
		typedValue.mTypeEx = NULL;
		return;
	}
	auto& result = mResults[streamId];
	if (result.mKind == BfIRCodeGenEntryKind_TypedValue)
	{
		typedValue = result.mTypedValue;
		return;
	}

	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMValue);
	BF_ASSERT(llvm::isa<llvm::Function>(result.mLLVMValue));
	typedValue.mValue = result.mLLVMValue;
	FixTypedValue(typedValue);	
}

void BfIRCodeGen::Read(llvm::BasicBlock*& llvmBlock)
{
	int streamId = (int)ReadSLEB128();
	auto& result = mResults[streamId];
	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMBasicBlock);
	llvmBlock = (llvm::BasicBlock*)result.mLLVMType;
}

void BfIRCodeGen::Read(llvm::MDNode*& llvmMD)
{
	int streamId = (int)ReadSLEB128();
	if (streamId == -1)
	{
		llvmMD = NULL;
		return;
	}
	auto& result = mResults[streamId];
	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMMetadata);
	llvmMD = result.mLLVMMetadata;
}

void BfIRCodeGen::Read(llvm::Metadata*& llvmMD)
{
	int streamId = (int)ReadSLEB128();
	if (streamId == -1)
	{
		llvmMD = NULL;
		return;
	}
	auto& result = mResults[streamId];
	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMMetadata);
	llvmMD = result.mLLVMMetadata;
}

void BfIRCodeGen::AddNop()
{
	if ((mTargetTriple.GetMachineType() != BfMachineType_x86) && (mTargetTriple.GetMachineType() != BfMachineType_x64))
		return;

	if (mNopInlineAsm == NULL)
	{
		llvm::SmallVector<llvm::Type*, 8> paramTypes;
		llvm::FunctionType* funcType = llvm::FunctionType::get(llvm::Type::getVoidTy(*mLLVMContext), paramTypes, false);
		mNopInlineAsm = llvm::InlineAsm::get(funcType,
			"nop", "", true, false, llvm::InlineAsm::AD_ATT);
	}

	llvm::CallInst* callInst = mIRBuilder->CreateCall(mNopInlineAsm);
	
	callInst->addFnAttr(llvm::Attribute::NoUnwind);
}

llvm::Value* BfIRCodeGen::TryToVector(const BfIRTypedValue& value)
{
	auto valueType = value.mTypeEx->mLLVMType;
	if (llvm::isa<llvm::VectorType>(valueType))
		return value.mValue;
	
 	if (auto ptrType = llvm::dyn_cast<llvm::PointerType>(valueType))
 	{
 		auto ptrElemType = GetTypeMember(value.mTypeEx, 0);
 		if (auto arrType = llvm::dyn_cast<llvm::ArrayType>(ptrElemType->mLLVMType))
 		{
 			auto vecType = llvm::FixedVectorType::get(arrType->getArrayElementType(), (uint)arrType->getArrayNumElements());
 			auto vecPtrType = vecType->getPointerTo();
 			
 			auto ptrVal0 = mIRBuilder->CreateBitCast(value.mValue, vecPtrType);
 			return mIRBuilder->CreateAlignedLoad(vecType, ptrVal0, llvm::MaybeAlign(1));
 		}
 
 		if (auto vecType = llvm::dyn_cast<llvm::VectorType>(ptrElemType->mLLVMType))
 		{
 			return mIRBuilder->CreateAlignedLoad(vecType, value.mValue, llvm::MaybeAlign(1));
 		}
 	}

	return NULL;
}

bool BfIRCodeGen::TryMemCpy(const BfIRTypedValue& ptr, llvm::Value* val)
{
	auto valType = val->getType();

	auto dataLayout = llvm::DataLayout(mLLVMModule);
	int arrayBytes = (int)dataLayout.getTypeSizeInBits(valType) / 8;

	// LLVM has perf issues with large aggregates - it treats each element as a unique value,
	//  which is great for optimizing small data but is a perf killer for large data.
	if (arrayBytes < 256)
		return false;

	auto int8Ty = llvm::Type::getInt8Ty(*mLLVMContext);
	auto int32Ty = llvm::Type::getInt32Ty(*mLLVMContext);
	auto int8PtrTy = int8Ty->getPointerTo();

	if (auto loadInst = llvm::dyn_cast<llvm::LoadInst>(val))
	{
		mIRBuilder->CreateMemCpy(
			mIRBuilder->CreateBitCast(ptr.mValue, int8PtrTy),
			llvm::MaybeAlign(1),
			mIRBuilder->CreateBitCast(loadInst->getPointerOperand(), int8PtrTy),
			llvm::MaybeAlign(1),
			llvm::ConstantInt::get(int32Ty, arrayBytes));
		return true;
	}

	auto constVal = llvm::dyn_cast<llvm::Constant>(val);
	if (constVal == NULL)
		return false;

	if (llvm::isa<llvm::ConstantAggregateZero>(constVal))
	{
		mIRBuilder->CreateMemSet(
			mIRBuilder->CreateBitCast(ptr.mValue, int8PtrTy),
			llvm::ConstantInt::get(int8Ty, 0),
			llvm::ConstantInt::get(int32Ty, arrayBytes),
			llvm::MaybeAlign(1));
		return true;
	}

	auto globalVariable = new llvm::GlobalVariable(
		*mLLVMModule,
		valType,
		true,
		llvm::GlobalValue::InternalLinkage,
		constVal,
		StrFormat("__ConstVal__%d", mConstValIdx++).c_str(),
		NULL,
		llvm::GlobalValue::NotThreadLocal);

	mIRBuilder->CreateMemCpy(
		mIRBuilder->CreateBitCast(ptr.mValue, int8PtrTy),
		llvm::MaybeAlign(1),
		mIRBuilder->CreateBitCast(globalVariable, int8PtrTy),
		llvm::MaybeAlign(1),
		llvm::ConstantInt::get(int32Ty, arrayBytes));

	return true;
}

bool BfIRCodeGen::TryVectorCpy(const BfIRTypedValue& ptr, llvm::Value* val)
{	
 	if (GetTypeMember(ptr.mTypeEx, 0)->mLLVMType == val->getType())
 		return false;

	if (!llvm::isa<llvm::VectorType>(val->getType()))
	{
		return false;
	}

	auto usePtr = mIRBuilder->CreateBitCast(ptr.mValue, val->getType()->getPointerTo());
	mIRBuilder->CreateAlignedStore(val, usePtr, llvm::MaybeAlign(1));

	return true;
}

llvm::Type* BfIRCodeGen::GetLLVMPointerElementType(BfIRTypeEx* typeEx)
{
	BF_ASSERT(typeEx != NULL);
	BF_ASSERT(typeEx->mLLVMType->isPointerTy());
	return GetTypeMember(typeEx, 0)->mLLVMType;
}

BfIRTypeEx* BfIRCodeGen::GetSizeAlignedType(BfIRTypeEntry* typeEntry)
{
	if ((typeEntry->mAlignType == NULL) && ((typeEntry->mSize & (typeEntry->mAlign - 1)) != 0))
	{
		auto structType = llvm::dyn_cast<llvm::StructType>(typeEntry->mType->mLLVMType);
		if (structType != NULL)
		{
			//TODO: Fill out properly

			BF_ASSERT(structType->isPacked());

			auto alignTypeEx = new BfIRTypeEx();
			mIRTypeExs.Add(alignTypeEx);

			auto alignType = llvm::StructType::create(*mLLVMContext, (structType->getName().str() + "_ALIGNED").c_str());
			llvm::SmallVector<llvm::Type*, 8> members;
			for (int elemIdx = 0; elemIdx < (int)structType->getNumElements(); elemIdx++)
			{
				members.push_back(structType->getElementType(elemIdx));
			}
			int alignSize = BF_ALIGN(typeEntry->mSize, typeEntry->mAlign);
			int fillSize = alignSize - typeEntry->mSize;
			members.push_back(llvm::ArrayType::get(llvm::Type::getInt8Ty(*mLLVMContext), fillSize));
			alignType->setBody(members, structType->isPacked());
					
			alignTypeEx->mLLVMType = alignType;
			typeEntry->mAlignType = alignTypeEx;
			mAlignedTypeToNormalType[alignTypeEx] = typeEntry->mType;
		}
	}

	if (typeEntry->mAlignType != NULL)
		return typeEntry->mAlignType;

	return typeEntry->mType;
}

BfIRTypedValue BfIRCodeGen::GetAlignedPtr(const BfIRTypedValue& val)
{	
	BfIRTypedValue result = val;

	if (auto ptrType = llvm::dyn_cast<llvm::PointerType>(val.mTypeEx->mLLVMType))
	{
		auto elemType = GetTypeMember(val.mTypeEx, 0);
		auto typeEntry = GetTypeEntry(elemType);
		if (typeEntry != NULL)
		{
			auto alignedType = GetSizeAlignedType(typeEntry);
			if (alignedType != elemType)
				result.mTypeEx = GetPointerTypeEx(alignedType);
		}
	}

	return result;
}


llvm::Value* BfIRCodeGen::DoCheckedIntrinsic(llvm::Intrinsic::ID intrin, llvm::Value* lhs, llvm::Value* rhs, bool useAsm)
{
	if ((mTargetTriple.GetMachineType() != BfMachineType_x86) && (mTargetTriple.GetMachineType() != BfMachineType_x64))
		useAsm = false;

	CmdParamVec<llvm::Type*> useParams;
	useParams.push_back(lhs->getType());
	auto func = llvm::Intrinsic::getDeclaration(mLLVMModule, intrin, useParams);

	CmdParamVec<llvm::Value*> args;
	args.push_back(lhs);
	args.push_back(rhs);
	llvm::FunctionType* funcType = func->getFunctionType();

	auto aggResult = mIRBuilder->CreateCall(funcType, func, args);
	auto valResult = mIRBuilder->CreateExtractValue(aggResult, 0);
	auto failResult = mIRBuilder->CreateExtractValue(aggResult, 1);

	if (!useAsm)
	{
		mLockedBlocks.Add(mIRBuilder->GetInsertBlock());

		auto failBB = llvm::BasicBlock::Create(*mLLVMContext, "access.fail");
		auto passBB = llvm::BasicBlock::Create(*mLLVMContext, "access.pass");

		mIRBuilder->CreateCondBr(failResult, failBB, passBB);

		mActiveFunction->insert(mActiveFunction->end(), failBB);
		mIRBuilder->SetInsertPoint(failBB);

		auto trapDecl = llvm::Intrinsic::getDeclaration(mLLVMModule, llvm::Intrinsic::trap);
		auto callInst = mIRBuilder->CreateCall(trapDecl);
		callInst->addFnAttr(llvm::Attribute::NoReturn);
		mIRBuilder->CreateBr(passBB);
		
		mActiveFunction->insert(mActiveFunction->end(), passBB);
		mIRBuilder->SetInsertPoint(passBB);
	}
	else
	{
		if (mOverflowCheckAsm == NULL)
		{
			std::vector<llvm::Type*> paramTypes;
			paramTypes.push_back(llvm::Type::getInt8Ty(*mLLVMContext));
			auto funcType = llvm::FunctionType::get(llvm::Type::getVoidTy(*mLLVMContext), paramTypes, false);

			String asmStr =
				"testb $$1, $0\n"
				"jz 1f\n"
				"int $$3\n"
				"1:";

			mOverflowCheckAsm = llvm::InlineAsm::get(funcType,
				asmStr.c_str(), "r,~{dirflag},~{fpsr},~{flags}", true,
				false, llvm::InlineAsm::AD_ATT);
		}

		llvm::SmallVector<llvm::Value*, 1> llvmArgs;
		llvmArgs.push_back(mIRBuilder->CreateIntCast(failResult, llvm::Type::getInt8Ty(*mLLVMContext), false));
		llvm::CallInst* callInst = mIRBuilder->CreateCall(mOverflowCheckAsm, llvmArgs);
		callInst->addFnAttr(llvm::Attribute::NoUnwind);
	}
	return valResult;
}

void BfIRCodeGen::CreateMemSet(llvm::Value* addr, llvm::Value* val, llvm::Value* size, int alignment, bool isVolatile)
{
	auto sizeConst = llvm::dyn_cast<llvm::ConstantInt>(size);
	auto valConst = llvm::dyn_cast<llvm::ConstantInt>(val);
	if ((!mIsOptimized) && (sizeConst != NULL) && (valConst != NULL))
	{
		int64 sizeVal = sizeConst->getSExtValue();
		uint8 setVal = (uint8)valConst->getSExtValue();
		if (sizeVal <= 128)
		{
			//llvm::Value* intVal = mIRBuilder->CreatePtrToInt(addr, llvm::Type::getInt32Ty(*mLLVMContext))

			int curOffset = 0;
			int sizeLeft = (int)sizeVal;
			llvm::Value* headVal;

			if (mPtrSize >= 8)
			{
				headVal = NULL;
				auto intTy = llvm::Type::getInt64Ty(*mLLVMContext);
				auto constVal = llvm::ConstantInt::get(intTy,
					((int64)setVal << 56) | ((int64)setVal << 48) | ((int64)setVal << 40) | ((int64)setVal << 32) |
					((int64)setVal << 24) | ((int64)setVal << 16) | ((int64)setVal << 8) | ((int64)setVal));
				while (sizeLeft >= 8)
				{
					if (headVal == NULL)
						headVal = mIRBuilder->CreateBitCast(addr, intTy->getPointerTo());
					llvm::Value* ptrVal = headVal;
					if (curOffset != 0)
						ptrVal = mIRBuilder->CreateConstInBoundsGEP1_32(intTy, headVal, curOffset / 8);
					mIRBuilder->CreateStore(constVal, ptrVal, isVolatile);

					curOffset += 8;
					sizeLeft -= 8;
				}
			}

			if (sizeLeft >= 4)
			{
				headVal = NULL;
				auto intTy = llvm::Type::getInt32Ty(*mLLVMContext);
				auto constVal = llvm::ConstantInt::get(intTy, ((int)setVal << 24) | ((int)setVal << 16) | ((int)setVal << 8) | ((int)setVal));
				while (sizeLeft >= 4)
				{
					if (headVal == NULL)
						headVal = mIRBuilder->CreateBitCast(addr, intTy->getPointerTo());
					llvm::Value* ptrVal = headVal;
					if (curOffset != 0)
						ptrVal = mIRBuilder->CreateConstInBoundsGEP1_32(intTy, headVal, curOffset / 4);
					mIRBuilder->CreateStore(constVal, ptrVal, isVolatile);

					curOffset += 4;
					sizeLeft -= 4;
				}
			}

			if (sizeLeft >= 2)
			{
				headVal = NULL;
				auto intTy = llvm::Type::getInt16Ty(*mLLVMContext);
				auto constVal = llvm::ConstantInt::get(intTy, ((int)setVal << 8) | ((int)setVal));
				while (sizeLeft >= 2)
				{
					if (headVal == NULL)
						headVal = mIRBuilder->CreateBitCast(addr, intTy->getPointerTo());
					llvm::Value* ptrVal = headVal;
					if (curOffset != 0)
						ptrVal = mIRBuilder->CreateConstInBoundsGEP1_32(intTy, headVal, curOffset / 2);
					mIRBuilder->CreateStore(constVal, ptrVal, isVolatile);

					curOffset += 2;
					sizeLeft -= 2;
				}
			}

			if (sizeLeft >= 1)
			{
				headVal = NULL;
				auto intTy = llvm::Type::getInt8Ty(*mLLVMContext);
				auto constVal = llvm::ConstantInt::get(intTy, ((int)setVal));
				while (sizeLeft >= 1)
				{
					if (headVal == NULL)
						headVal = mIRBuilder->CreateBitCast(addr, intTy->getPointerTo());
					llvm::Value* ptrVal = headVal;
					if (curOffset != 0)
						ptrVal = mIRBuilder->CreateConstInBoundsGEP1_32(intTy, headVal, curOffset / 1);
					mIRBuilder->CreateStore(constVal, ptrVal, isVolatile);

					curOffset += 1;
					sizeLeft -= 1;
				}
			}

			return;
		}
	}

	mIRBuilder->CreateMemSet(addr, val, size, llvm::MaybeAlign(alignment), isVolatile);
}

void BfIRCodeGen::InitTarget()
{
	llvm::SMDiagnostic Err;
	llvm::Triple theTriple = llvm::Triple(mLLVMModule->getTargetTriple());
	llvm::CodeGenOptLevel optLvl = llvm::CodeGenOptLevel::None;

	String cpuName = mTargetCPU;
	String arch = "";

	// Get the target specific parser.
	std::string Error;
	const llvm::Target *theTarget = llvm::TargetRegistry::lookupTarget(arch.c_str(), theTriple, Error);
	if (!theTarget)
	{
		Fail(StrFormat("Failed to create LLVM Target: %s", Error.c_str()));
		return;
	}

	llvm::TargetOptions Options = llvm::TargetOptions(); // InitTargetOptionsFromCodeGenFlags();

	String featuresStr;

	if (mCodeGenOptions.mOptLevel == BfOptLevel_O1)
	{
		//optLvl = CodeGenOpt::Less;
	}
	else if (mCodeGenOptions.mOptLevel == BfOptLevel_O2)
		optLvl = llvm::CodeGenOptLevel::Default;
	else if (mCodeGenOptions.mOptLevel == BfOptLevel_O3)
		optLvl = llvm::CodeGenOptLevel::Aggressive;

	if (theTriple.isWasm())
		featuresStr = "+atomics,+bulk-memory,+mutable-globals,+sign-ext";
	else if (mCodeGenOptions.mSIMDSetting == BfSIMDSetting_SSE)
		featuresStr = "+sse";
	else if (mCodeGenOptions.mSIMDSetting == BfSIMDSetting_SSE2)
		featuresStr = "+sse2";
	else if (mCodeGenOptions.mSIMDSetting == BfSIMDSetting_SSE3)
		featuresStr = "+sse3";
	else if (mCodeGenOptions.mSIMDSetting == BfSIMDSetting_SSE4)
		featuresStr = "+sse4";
	else if (mCodeGenOptions.mSIMDSetting == BfSIMDSetting_SSE41)
		featuresStr = "+sse4.1";
	else if (mCodeGenOptions.mSIMDSetting == BfSIMDSetting_AVX)
		featuresStr = "+avx";
	else if (mCodeGenOptions.mSIMDSetting == BfSIMDSetting_AVX2)
		featuresStr = "+avx2";

	std::optional<llvm::Reloc::Model> relocModel;
	llvm::CodeModel::Model cmModel = llvm::CodeModel::Small;

	switch (mCodeGenOptions.mRelocType)
	{
	case BfRelocType_Static:
		relocModel = llvm::Reloc::Model::DynamicNoPIC;
		break;
	case BfRelocType_PIC:
		relocModel = llvm::Reloc::Model::PIC_;
		break;
	case BfRelocType_DynamicNoPIC:
		relocModel = llvm::Reloc::Model::DynamicNoPIC;
		break;
	case BfRelocType_ROPI:
		relocModel = llvm::Reloc::Model::ROPI;
		break;
	case BfRelocType_RWPI:
		relocModel = llvm::Reloc::Model::RWPI;
		break;
	case BfRelocType_ROPI_RWPI:
		relocModel = llvm::Reloc::Model::ROPI_RWPI;
		break;
	default: break;
	}

	switch (mCodeGenOptions.mPICLevel)
	{
	case BfPICLevel_Not:
		mLLVMModule->setPICLevel(llvm::PICLevel::Level::NotPIC);
		break;
	case BfPICLevel_Small:
		mLLVMModule->setPICLevel(llvm::PICLevel::Level::SmallPIC);
		break;
	case BfPICLevel_Big:
		mLLVMModule->setPICLevel(llvm::PICLevel::Level::BigPIC);
		break;
	default: break;
	}

	mLLVMTargetMachine =
		theTarget->createTargetMachine(theTriple.getTriple(), cpuName.c_str(), featuresStr.c_str(),
			Options, relocModel, cmModel, optLvl);

	mLLVMModule->setDataLayout(mLLVMTargetMachine->createDataLayout());
}

void BfIRCodeGen::HandleNextCmd()
{
	if (mFailed)
		return;

	int curId = mCmdCount;

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

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

			BF_ASSERT(mLLVMModule == NULL);
            mModuleName = moduleName;
			mPtrSize = ptrSize;
			mIsOptimized = isOptimized;
			mLLVMModule = new llvm::Module(moduleName.c_str(), *mLLVMContext);
			mIRBuilder = new llvm::IRBuilder<>(*mLLVMContext);

            //OutputDebugStrF("-------- Starting Module %s --------\n", moduleName.c_str());
		}
		break;
	case BfIRCmd_Module_SetTargetTriple:
		{
			CMD_PARAM(String, targetTriple);
			CMD_PARAM(String, targetCPU);

			mTargetTriple.Set(targetTriple);
			mTargetCPU = targetCPU;						

            if (targetTriple.IsEmpty())
                mLLVMModule->setTargetTriple(llvm::sys::getDefaultTargetTriple());
            else
                mLLVMModule->setTargetTriple(targetTriple.c_str());

			InitTarget();
		}
		break;
	case BfIRCmd_Module_AddModuleFlag:
		{
			CMD_PARAM(String, flag);
			CMD_PARAM(int, val);
			mLLVMModule->addModuleFlag(llvm::Module::Warning, flag.c_str(), val);

			if (flag == "CodeView")
				mIsCodeView = true;
		}
		break;
	case BfIRCmd_WriteIR:
		{
			CMD_PARAM(String, fileName);
			std::error_code ec;
			llvm::raw_fd_ostream outStream(fileName.c_str(), ec, llvm::sys::fs::OpenFlags::OF_Text);
			if (ec)
			{
				Fail("Failed writing IR '" + fileName + "': " + ec.message());
			}
			else
				mLLVMModule->print(outStream, NULL);
		}
		break;
	case BfIRCmd_Abort:
		Fail("Stream aborted");
		break;
	case BfIRCmd_SetType:
		{
			CMD_PARAM(int, typeId);
			
			CMD_PARAM(BfIRTypeEx*, type);
			//llvm::Type* type;
			//llvm::Type* elementType;

			auto& typeEntry = GetTypeEntry(typeId);
			typeEntry.mType = type;
			if (typeEntry.mInstType == NULL)
				typeEntry.mInstType = type;
			mTypeToTypeIdMap[type] = typeId;
		}
		break;
	case BfIRCmd_SetInstType:
		{
			CMD_PARAM(int, typeId);
			CMD_PARAM(BfIRTypeEx*, type);
			GetTypeEntry(typeId).mInstType = type;
		}
		break;
	case BfIRCmd_PrimitiveType:
		{
			BfTypeCode typeCode = (BfTypeCode)mStream->Read();
			bool isSigned;
			SetResult(curId, GetLLVMType(typeCode, isSigned));
		}
		break;
	case BfIRCmd_CreateAnonymousStruct:
		{
			CMD_PARAM(CmdParamVec<BfIRTypeEx*>, members);
			
			CmdParamVec<llvm::Type*> llvmMembers;
			for (auto& memberType : members)			
				llvmMembers.push_back(memberType->mLLVMType);

			auto structType = llvm::StructType::get(*mLLVMContext, llvmMembers);
			auto typeEx = CreateTypeEx(structType);
			for (auto& memberType : members)
			{
				BF_ASSERT(memberType != NULL);
				typeEx->mMembers.Add(memberType);
			}

			SetResult(curId, typeEx);
		}
		break;
	case BfIRCmd_CreateStruct:
		{
			CMD_PARAM(String, typeName);

			auto structType = llvm::StructType::create(*mLLVMContext, typeName.c_str());
			auto typeEx = CreateTypeEx(structType);			
			SetResult(curId, typeEx);
		}
		break;
	case BfIRCmd_StructSetBody:
		{
			BfIRTypeEx* typeEx = NULL;
			BfIRTypeEntry* typeEntry = NULL;

			Read(typeEx, &typeEntry);
			CMD_PARAM(CmdParamVec<BfIRTypeEx*>, members);
			CMD_PARAM(int, instSize);
			CMD_PARAM(int, instAlign);
			CMD_PARAM(bool, isPacked);

			typeEx->mMembers.clear();
			auto type = typeEx->mLLVMType;

			CmdParamVec<llvm::Type*> llvmMembers;
			for (auto& memberType : members)
			{
				BF_ASSERT(memberType != NULL);
				typeEx->mMembers.Add(memberType);
				llvmMembers.push_back(memberType->mLLVMType);
			}

			BF_ASSERT(llvm::isa<llvm::StructType>(type));
			auto structType = (llvm::StructType*)type;
			if (structType->isOpaque())
				structType->setBody(llvmMembers, isPacked);
			if (typeEntry != NULL)
			{
				typeEntry->mSize = instSize;
				typeEntry->mAlign = instAlign;
			}
		}
		break;
	case  BfIRCmd_Type:
		{
			CMD_PARAM(BfIRTypeEntry*, typeEntry);
			auto type = typeEntry->mType;
			SetResult(curId, type);
		}
		break;
	case  BfIRCmd_TypeInst:
		{
			CMD_PARAM(BfIRTypeEntry*, typeEntry);
			SetResult(curId, typeEntry->mInstType);
		}
		break;
	case BfIRCmd_TypeInstPtr:
		{
			CMD_PARAM(BfIRTypeEntry*, typeEntry);
			SetResult(curId, GetPointerTypeEx(typeEntry->mInstType));
		}
		break;
	case BfIRCmd_GetType:
		{
			CMD_PARAM(BfIRTypedValue, typedValue);
			BF_ASSERT(typedValue.mTypeEx != NULL);
			SetResult(curId, typedValue.mTypeEx);
		}
		break;
	case BfIRCmd_GetPointerToFuncType:
		{
			BfIRTypeEx* funcType = NULL;
			ReadFunctionType(funcType);			
			SetResult(curId, GetPointerTypeEx(funcType));
		}
		break;
	case BfIRCmd_GetPointerToType:
		{
			CMD_PARAM(BfIRTypeEx*, type);
			SetResult(curId, GetPointerTypeEx(type));
		}
		break;
	case BfIRCmd_GetSizedArrayType:
		{
			BfIRTypeEx* elementType = NULL;
			BfIRTypeEntry* elementTypeEntry = NULL;
			Read(elementType, &elementTypeEntry);

			auto typeEx = new BfIRTypeEx();			
			typeEx->mMembers.Add(elementType);
			mIRTypeExs.Add(typeEx);
			
			CMD_PARAM(int, length);
			if (elementTypeEntry != NULL)
				typeEx->mLLVMType = llvm::ArrayType::get(GetSizeAlignedType(elementTypeEntry)->mLLVMType, length);			
			else
				typeEx->mLLVMType = llvm::ArrayType::get(elementType->mLLVMType, length);

			SetResult(curId, typeEx);
		}
		break;
	case BfIRCmd_GetVectorType:
		{
			CMD_PARAM(BfIRTypeEx*, elementType);
			CMD_PARAM(int, length);

			auto llvmType = elementType->mLLVMType;
			if (llvmType->getScalarSizeInBits() == 1)
			{
				llvmType = llvm::Type::getInt8Ty(*mLLVMContext);
			}

			auto typeEx = new BfIRTypeEx();
			mIRTypeExs.Add(typeEx);
			typeEx->mLLVMType = llvm::FixedVectorType::get(llvmType, length);
			typeEx->mMembers.Add(elementType);
			SetResult(curId, typeEx);
		}
		break;
	case BfIRCmd_CreateConstAgg:
		{
			CMD_PARAM(BfIRTypeEx*, type);
			CMD_PARAM(CmdParamVec<llvm::Value*>, values)
			llvm::SmallVector<llvm::Constant*, 8> copyValues;

			if (auto arrayType = llvm::dyn_cast<llvm::ArrayType>(type->mLLVMType))
			{
				for (auto val : values)
				{
					auto constValue = llvm::dyn_cast<llvm::Constant>(val);
					BF_ASSERT(constValue != NULL);
					copyValues.push_back(constValue);
				}

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

				BfIRTypedValue result;
				result.mTypeEx = type;
				result.mValue = llvm::ConstantArray::get(arrayType, copyValues);
				SetResult(curId, result);
			}
			else if (auto structType = llvm::dyn_cast<llvm::StructType>(type->mLLVMType))
			{
				FixValues(structType, values);
				for (auto val : values)
				{
					auto constValue = llvm::dyn_cast<llvm::Constant>(val);
					BF_ASSERT(constValue != NULL);
					copyValues.push_back(constValue);
				}
				BfIRTypedValue result;
				result.mTypeEx = type;
				result.mValue = llvm::ConstantStruct::get(structType, copyValues);
				SetResult(curId, result);
			}
			else
				Fail("Bad type");
		}
		break;
	case BfIRCmd_CreateConstStructZero:
		{
			CMD_PARAM(BfIRTypeEx*, type);
			BfIRTypedValue result;
			result.mTypeEx = type;
			result.mValue = llvm::ConstantAggregateZero::get(type->mLLVMType);
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_CreateConstString:
		{
			CMD_PARAM(String, str);

			BfIRTypedValue result;
			result.mValue = llvm::ConstantDataArray::getString(*mLLVMContext, llvm::StringRef(str.c_str(), str.length()));
			result.mTypeEx = GetTypeEx(result.mValue->getType());
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_ConfigConst:
		{
			CMD_PARAM(int, constIdx);
			BfTypeCode typeCode = (BfTypeCode)mStream->Read();
			if (typeCode == BfTypeCode_IntPtr)
				typeCode = (mPtrSize == 4) ? BfTypeCode_Int32 : BfTypeCode_Int64;
			llvm::Constant* constVal = (typeCode == BfTypeCode_Int32) ?
				mConfigConsts32[constIdx] :
				mConfigConsts64[constIdx];
			SetResult(curId, constVal);
		}
		break;
	case BfIRCmd_SetName:
		{
			CMD_PARAM_NOTRANS(llvm::Value*, val);
			CMD_PARAM(String, name);
			if (!val->getType()->isVoidTy())
				val->setName(name.c_str());
		}
		break;
	case BfIRCmd_CreateUndefValue:
		{
			CMD_PARAM(BfIRTypeEx*, type);
			BfIRTypedValue result;
			result.mTypeEx = type;
			result.mValue = llvm::UndefValue::get(type->mLLVMType);
			SetResult(curId, result);			
		}
		break;
	case BfIRCmd_NumericCast:
		{
			CMD_PARAM(llvm::Value*, val);
			CMD_PARAM(bool, valIsSigned);
			BfTypeCode typeCode = (BfTypeCode)mStream->Read();
			BfTypeCode valTypeCode = GetTypeCode(val->getType(), valIsSigned);
			bool toSigned;
			auto toLLVMType = GetLLVMType(typeCode, toSigned);

			llvm::Value* retVal = NULL;

			if (BfIRBuilder::IsInt(typeCode))
			{
				// Int -> Int
				if ((BfIRBuilder::IsInt(valTypeCode)) || (valTypeCode == BfTypeCode_Boolean))
				{
					retVal = mIRBuilder->CreateIntCast(val, toLLVMType, toSigned && valIsSigned);
				}
				else // Float -> Int
				{
					if (BfIRBuilder::IsSigned(typeCode))
						retVal = mIRBuilder->CreateFPToSI(val, toLLVMType);
					else
						retVal = mIRBuilder->CreateFPToUI(val, toLLVMType);
				}
			}
			else
			{
				// Int -> Float
				if ((BfIRBuilder::IsInt(valTypeCode)) || (valTypeCode == BfTypeCode_Boolean))
				{
					if (BfIRBuilder::IsSigned(valTypeCode))
						retVal = mIRBuilder->CreateSIToFP(val, toLLVMType);
					else
						retVal = mIRBuilder->CreateUIToFP(val, toLLVMType);
				}
				else // Float -> Float
				{
					retVal = mIRBuilder->CreateFPCast(val, toLLVMType);
				}
			}
			SetResult(curId, retVal);
		}
		break;
	case BfIRCmd_CmpEQ:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFCmpOEQ(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateICmpEQ(lhs, rhs));
		}
		break;
	case BfIRCmd_CmpNE:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFCmpUNE(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateICmpNE(lhs, rhs));
		}
		break;
	case BfIRCmd_CmpSLT:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFCmpOLT(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateICmpSLT(lhs, rhs));
		}
		break;
	case BfIRCmd_CmpULT:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFCmpOLT(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateICmpULT(lhs, rhs));
		}
		break;
	case BfIRCmd_CmpSLE:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFCmpOLE(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateICmpSLE(lhs, rhs));
		}
		break;
	case BfIRCmd_CmpULE:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFCmpOLE(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateICmpULE(lhs, rhs));
		}
		break;
	case BfIRCmd_CmpSGT:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFCmpOGT(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateICmpSGT(lhs, rhs));
		}
		break;
	case BfIRCmd_CmpUGT:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFCmpOGT(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateICmpUGT(lhs, rhs));
		}
		break;
	case BfIRCmd_CmpSGE:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFCmpOGE(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateICmpSGE(lhs, rhs));
		}
		break;
	case BfIRCmd_CmpUGE:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFCmpOGE(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateICmpUGE(lhs, rhs));
		}
		break;

	case BfIRCmd_Add:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			CMD_PARAM(int8, overflowCheckKind);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFAdd(lhs, rhs));
			else if ((overflowCheckKind & (BfOverflowCheckKind_Signed | BfOverflowCheckKind_Unsigned)) != 0)
				SetResult(curId, DoCheckedIntrinsic(((overflowCheckKind & BfOverflowCheckKind_Signed) != 0) ? llvm::Intrinsic::sadd_with_overflow : llvm::Intrinsic::uadd_with_overflow,
					lhs, rhs, (overflowCheckKind & BfOverflowCheckKind_Flag_UseAsm) != 0));
			else
				SetResult(curId, mIRBuilder->CreateAdd(lhs, rhs));
		}
		break;
	case BfIRCmd_Sub:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			CMD_PARAM(int8, overflowCheckKind);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFSub(lhs, rhs));
			else if ((overflowCheckKind & (BfOverflowCheckKind_Signed | BfOverflowCheckKind_Unsigned)) != 0)
				SetResult(curId, DoCheckedIntrinsic(((overflowCheckKind & BfOverflowCheckKind_Signed) != 0) ? llvm::Intrinsic::ssub_with_overflow : llvm::Intrinsic::usub_with_overflow,
					lhs, rhs, (overflowCheckKind & BfOverflowCheckKind_Flag_UseAsm) != 0));
			else
				SetResult(curId, mIRBuilder->CreateSub(lhs, rhs));
		}
		break;
	case BfIRCmd_Mul:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			CMD_PARAM(int8, overflowCheckKind);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFMul(lhs, rhs));
			else if ((overflowCheckKind & (BfOverflowCheckKind_Signed | BfOverflowCheckKind_Unsigned)) != 0)
				SetResult(curId, DoCheckedIntrinsic(((overflowCheckKind & BfOverflowCheckKind_Signed) != 0) ? llvm::Intrinsic::smul_with_overflow : llvm::Intrinsic::umul_with_overflow,
					lhs, rhs, (overflowCheckKind & BfOverflowCheckKind_Flag_UseAsm) != 0));
			else
				SetResult(curId, mIRBuilder->CreateMul(lhs, rhs));
		}
		break;
	case BfIRCmd_SDiv:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFDiv(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateSDiv(lhs, rhs));
		}
		break;
	case BfIRCmd_UDiv:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			SetResult(curId, mIRBuilder->CreateUDiv(lhs, rhs));
		}
		break;
	case BfIRCmd_SRem:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			if (lhs->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFRem(lhs, rhs));
			else
				SetResult(curId, mIRBuilder->CreateSRem(lhs, rhs));
		}
		break;
	case BfIRCmd_URem:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			SetResult(curId, mIRBuilder->CreateURem(lhs, rhs));
		}
		break;
	case BfIRCmd_And:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			SetResult(curId, mIRBuilder->CreateAnd(lhs, rhs));
		}
		break;
	case BfIRCmd_Or:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			SetResult(curId, mIRBuilder->CreateOr(lhs, rhs));
		}
		break;
	case BfIRCmd_Xor:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			SetResult(curId, mIRBuilder->CreateXor(lhs, rhs));
		}
		break;
	case BfIRCmd_Shl:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			SetResult(curId, mIRBuilder->CreateShl(lhs, rhs));
		}
		break;
	case BfIRCmd_AShr:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			SetResult(curId, mIRBuilder->CreateAShr(lhs, rhs));
		}
		break;
	case BfIRCmd_LShr:
		{
			CMD_PARAM(llvm::Value*, lhs);
			CMD_PARAM(llvm::Value*, rhs);
			SetResult(curId, mIRBuilder->CreateLShr(lhs, rhs));
		}
		break;
	case BfIRCmd_Neg:
		{
			CMD_PARAM(llvm::Value*, val);
			if (val->getType()->isFloatingPointTy())
				SetResult(curId, mIRBuilder->CreateFNeg(val));
			else
				SetResult(curId, mIRBuilder->CreateNeg(val));
		}
		break;
	case BfIRCmd_Not:
		{
			CMD_PARAM(llvm::Value*, val);
			SetResult(curId, mIRBuilder->CreateNot(val));
		}
		break;
	case BfIRCmd_BitCast:
		{
			CMD_PARAM(BfIRTypedValue, val);
			CMD_PARAM(BfIRTypeEx*, toType);

			BfIRTypedValue result;		
			result.mTypeEx = toType;

			auto fromType = val.mValue->getType();
			if ((!fromType->isPointerTy()) || (!toType->mLLVMType->isPointerTy()))
			{
				if (fromType->isIntegerTy())				
					result.mValue = mIRBuilder->CreateIntToPtr(val.mValue, toType->mLLVMType);									
				else
					result.mValue = mIRBuilder->CreatePtrToInt(val.mValue, toType->mLLVMType);				
			}
			else
				result.mValue = mIRBuilder->CreateBitCast(val.mValue, toType->mLLVMType);
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_PtrToInt:
		{
			CMD_PARAM(llvm::Value*, val);
			auto typeCode = (BfTypeCode)mStream->Read();
			bool isSigned;			

			BfIRTypedValue result;
			result.mTypeEx = GetTypeEx(typeCode, isSigned);
			result.mValue = mIRBuilder->CreatePtrToInt(val, result.mTypeEx->mLLVMType);
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_IntToPtr:
		{
			CMD_PARAM(llvm::Value*, val);
			CMD_PARAM(BfIRTypeEx*, toType);

			BfIRTypedValue result;
			result.mTypeEx = toType;
			result.mValue = mIRBuilder->CreateIntToPtr(val, toType->mLLVMType);
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_InboundsGEP1_32:
		{
			CMD_PARAM(BfIRTypedValue, val);
			CMD_PARAM(int, idx0);
			
			BfIRTypedValue result;
			result.mTypeEx = val.mTypeEx;
			auto alignedPtr = GetAlignedPtr(val);

			auto compositeType = GetTypeMember(alignedPtr.mTypeEx, 0);
			result.mValue = mIRBuilder->CreateConstInBoundsGEP1_32(compositeType->mLLVMType, alignedPtr.mValue, idx0);			
			SetResult(curId, result);			
		}
		break;
	case BfIRCmd_InboundsGEP2_32:
		{
			CMD_PARAM(BfIRTypedValue, val);
			CMD_PARAM(int, idx0);
			CMD_PARAM(int, idx1);
			
			auto compositeType = GetTypeMember(val.mTypeEx, 0);
			int elemIdx = BF_MIN(idx1, (int)compositeType->mMembers.mSize - 1);
			BfIRTypeEx* elemType = GetTypeMember(compositeType, elemIdx);

			BfIRTypedValue result;
			result.mValue = mIRBuilder->CreateConstInBoundsGEP2_32(compositeType->mLLVMType, val.mValue, idx0, idx1);
			result.mTypeEx = GetPointerTypeEx(elemType);

			SetResult(curId, result);
		}
		break;
	case BfIRCmd_InBoundsGEP1:
		{
			CMD_PARAM(BfIRTypedValue, val);
			CMD_PARAM(llvm::Value*, idx0);

			BfIRTypedValue result;			
			auto alignedPtr = GetAlignedPtr(val);			
			auto compositeType = GetTypeMember(alignedPtr.mTypeEx, 0);

			FixIndexer(idx0);
			result.mValue = mIRBuilder->CreateInBoundsGEP(compositeType->mLLVMType, alignedPtr.mValue, idx0);
			result.mTypeEx = val.mTypeEx;
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_InBoundsGEP2:
		{
			CMD_PARAM(BfIRTypedValue, val);
			CMD_PARAM(llvm::Value*, idx0);
			CMD_PARAM(llvm::Value*, idx1);
			FixIndexer(idx0);
			FixIndexer(idx1);
			llvm::Value* indices[2] = { idx0, idx1 };

			int elemIdx = 0;
			if (auto constInt = llvm::dyn_cast<llvm::ConstantInt>(idx1))							
				elemIdx = BF_MIN((int)constInt->getSExtValue(), (int)val.mTypeEx->mMembers.mSize - 1);

			auto compositeType = GetTypeMember(val.mTypeEx, 0);
			BfIRTypeEx* elemType = GetTypeMember(compositeType, elemIdx);

			BfIRTypedValue result;			
			result.mValue = mIRBuilder->CreateInBoundsGEP(compositeType->mLLVMType, val.mValue, llvm::makeArrayRef(indices));
			result.mTypeEx = GetPointerTypeEx(elemType);
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_IsNull:
		{
			CMD_PARAM(llvm::Value*, val);
			SetResult(curId, mIRBuilder->CreateIsNull(val));
		}
		break;
	case BfIRCmd_IsNotNull:
		{
			CMD_PARAM(llvm::Value*, val);
			SetResult(curId, mIRBuilder->CreateIsNotNull(val));
		}
		break;
	case BfIRCmd_ExtractValue:
		{
			CMD_PARAM(BfIRTypedValue, val);
			CMD_PARAM(int, idx);
			
			auto compositeType = val.mTypeEx;
			int elemIdx = BF_MIN(idx, (int)compositeType->mMembers.mSize - 1);
			auto elemType = GetTypeMember(compositeType, elemIdx);

			BfIRTypedValue result;
			result.mTypeEx = elemType;
			result.mValue = mIRBuilder->CreateExtractValue(val.mValue, llvm::makeArrayRef((unsigned)idx));
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_InsertValue:
		{
			CMD_PARAM(BfIRTypedValue, agg);
			CMD_PARAM(BfIRTypedValue, val);
			CMD_PARAM(int, idx);

			BfIRTypedValue result;
			result.mTypeEx = agg.mTypeEx;
			result.mValue = mIRBuilder->CreateInsertValue(agg.mValue, val.mValue, llvm::makeArrayRef((unsigned)idx));
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_Alloca:
		{
			CMD_PARAM(BfIRTypeEx*, type);
			if (type->mLLVMType->isStructTy())
			{
				BF_ASSERT(!((llvm::StructType*)type->mLLVMType)->isOpaque());
			}

			BfIRTypedValue result;
			result.mTypeEx = GetPointerTypeEx(type);
			result.mValue = mIRBuilder->CreateAlloca(type->mLLVMType);
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_AllocaArray:
		{
			CMD_PARAM(BfIRTypeEx*, type);
			CMD_PARAM(llvm::Value*, arraySize);

			auto origType = type;
			auto typeEntry = GetTypeEntry(type);
			if (typeEntry != NULL)
				type = GetSizeAlignedType(typeEntry);

			BfIRTypedValue typedValue;
			typedValue.mTypeEx = GetPointerTypeEx(type);
			
			if (origType != type)
			{
				typedValue.mValue = mIRBuilder->CreateAlloca(type->mLLVMType, arraySize);
				SetResultAligned(curId, typedValue);
			}
			else
			{
				typedValue.mValue = mIRBuilder->CreateAlloca(type->mLLVMType, arraySize);
				SetResult(curId, typedValue);
			}
		}
		break;
	case BfIRCmd_SetAllocaAlignment:
		{
			CMD_PARAM_NOTRANS(llvm::Value*, val);
			CMD_PARAM(int, alignment);
			auto inst = llvm::dyn_cast<llvm::AllocaInst>(val);
			inst->setAlignment(llvm::Align(alignment));
		}
		break;
	case BfIRCmd_SetAllocaNoChkStkHint:
		{
			CMD_PARAM_NOTRANS(llvm::Value*, val);
			// LLVM does not support this
		}
		break;
	case BfIRCmd_LifetimeStart:
		{
			CMD_PARAM_NOTRANS(llvm::Value*, val);
			SetResult(curId, mIRBuilder->CreateLifetimeStart(val));
		}
		break;
	case BfIRCmd_LifetimeEnd:
		{
			CMD_PARAM_NOTRANS(llvm::Value*, val);
			SetResult(curId, mIRBuilder->CreateLifetimeEnd(val));
		}
		break;
	case BfIRCmd_LifetimeSoftEnd:
		{
			CMD_PARAM_NOTRANS(llvm::Value*, val);
		}
		break;
	case BfIRCmd_LifetimeExtend:
		{
			CMD_PARAM_NOTRANS(llvm::Value*, val);
		}
		break;
	case BfIRCmd_Load:
		{
			CMD_PARAM(BfIRTypedValue, typedValue);
			BF_ASSERT(typedValue.mTypeEx != NULL);
			CMD_PARAM(bool, isVolatile);

			BfIRTypedValue result;
			result.mTypeEx = GetTypeMember(typedValue.mTypeEx, 0);
			result.mValue = mIRBuilder->CreateLoad(result.mTypeEx->mLLVMType, typedValue.mValue, isVolatile);
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_AlignedLoad:
		{
			CMD_PARAM(BfIRTypedValue, typedValue);
			BF_ASSERT(typedValue.mTypeEx != NULL);
			CMD_PARAM(int, alignment);
			CMD_PARAM(bool, isVolatile);
			
			BfIRTypedValue result;
			result.mTypeEx = GetTypeMember(typedValue.mTypeEx, 0);
			result.mValue = mIRBuilder->CreateAlignedLoad(result.mTypeEx->mLLVMType, typedValue.mValue, llvm::MaybeAlign(alignment), isVolatile);
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_Store:
		{
			CMD_PARAM(BfIRTypedValue, val);
			CMD_PARAM(BfIRTypedValue, ptr);
			CMD_PARAM(bool, isVolatile);

			if ((!TryMemCpy(ptr, val.mValue)) &&
				(!TryVectorCpy(ptr, val.mValue)))
				SetResult(curId, mIRBuilder->CreateStore(val.mValue, ptr.mValue, isVolatile));
		}
		break;
	case BfIRCmd_AlignedStore:
		{
			CMD_PARAM(BfIRTypedValue, val);
			CMD_PARAM(BfIRTypedValue, ptr);
			CMD_PARAM(int, alignment);
			CMD_PARAM(bool, isVolatile);
			if ((!TryMemCpy(ptr, val.mValue)) &&
				(!TryVectorCpy(ptr, val.mValue)))
				SetResult(curId, mIRBuilder->CreateAlignedStore(val.mValue, ptr.mValue, llvm::MaybeAlign(alignment), isVolatile));
		}
		break;
	case BfIRCmd_MemSet:
		{
			CMD_PARAM(llvm::Value*, addr);
			CMD_PARAM(llvm::Value*, val);
			CMD_PARAM(llvm::Value*, size);
			CMD_PARAM(int, alignment);

			CreateMemSet(addr, val, size, alignment);
		}
		break;
	case BfIRCmd_Fence:
		{
			BfIRFenceType fenceType = (BfIRFenceType)mStream->Read();
			if (fenceType == BfIRFenceType_AcquireRelease)
				mIRBuilder->CreateFence(llvm::AtomicOrdering::AcquireRelease);
		}
		break;
	case BfIRCmd_StackSave:
		{
			//auto intrin = llvm::Intrinsic::getDeclaration(mLLVMModule, llvm::Intrinsic::stacksave);
			//CreateStackSave

			//auto callInst = mIRBuilder->CreateCall(intrin);

			BfIRTypedValue result;
			result.mValue = mIRBuilder->CreateStackSave();
			result.mTypeEx = GetTypeEx(result.mValue->getType());
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_StackRestore:
		{
			CMD_PARAM(llvm::Value*, stackVal);
			//auto intrin = llvm::Intrinsic::getDeclaration(mLLVMModule, llvm::Intrinsic::stackrestore);
			//auto callInst = mIRBuilder->CreateCall(intrin, llvm::SmallVector<llvm::Value*, 1> {stackVal });
			auto callInst = mIRBuilder->CreateStackRestore(stackVal);
			SetResult(curId, callInst);
		}
		break;
	case BfIRCmd_GlobalVariable:
		{
			CMD_PARAM(BfIRTypeEx*, varType);
			CMD_PARAM(bool, isConstant);
			BfIRLinkageType linkageType = (BfIRLinkageType)mStream->Read();
			CMD_PARAM(String, name);
			CMD_PARAM(bool, isTLS);
			CMD_PARAM(llvm::Constant*, initializer);

			auto globalVariable = mLLVMModule->getGlobalVariable(name.c_str());
			if (globalVariable == NULL)
			{
				globalVariable = new llvm::GlobalVariable(
					*mLLVMModule,
					varType->mLLVMType,
					isConstant,
					LLVMMapLinkageType(linkageType),
					initializer,
					name.c_str(), NULL, isTLS ? llvm::GlobalValue::GeneralDynamicTLSModel : llvm::GlobalValue::NotThreadLocal);
			}

			BfIRTypedValue result;
			result.mValue = globalVariable;
			result.mTypeEx = GetPointerTypeEx(varType);
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_GlobalVar_SetUnnamedAddr:
		{
			CMD_PARAM(llvm::Value*, val);
			CMD_PARAM(bool, unnamedAddr);
			((llvm::GlobalVariable*)val)->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
		}
		break;
	case BfIRCmd_GlobalVar_SetInitializer:
		{
			CMD_PARAM(llvm::Value*, val);
			CMD_PARAM(llvm::Constant*, initializer);
			((llvm::GlobalVariable*)val)->setInitializer(initializer);
		}
		break;
	case BfIRCmd_GlobalVar_SetAlignment:
		{
			CMD_PARAM(llvm::Value*, val);
			CMD_PARAM(int, alignment);
			((llvm::GlobalVariable*)val)->setAlignment(llvm::Align(alignment));
		}
		break;
	case BfIRCmd_GlobalVar_SetStorageKind:
		{
			CMD_PARAM(llvm::Value*, val);
			CMD_PARAM(int, storageKind);
			((llvm::GlobalVariable*)val)->setDLLStorageClass((llvm::GlobalValue::DLLStorageClassTypes)storageKind);
		}
		break;
	case BfIRCmd_GlobalStringPtr:
		{
			CMD_PARAM(String, str);

			BfIRTypedValue result;
			result.mValue = mIRBuilder->CreateGlobalStringPtr(llvm::StringRef(str.c_str(), str.length()));
			result.mTypeEx = GetTypeEx(result.mValue->getType());
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_SetReflectTypeData:
		{
			CMD_PARAM(BfIRTypeEx*, type);
			CMD_PARAM(BfIRTypedValue, value);
			mReflectDataMap[type] = value;
		}
		break;
	case BfIRCmd_CreateBlock:
		{
			CMD_PARAM(String, name);
			CMD_PARAM(bool, addNow);
			auto block = llvm::BasicBlock::Create(*mLLVMContext, name.c_str());
			if (addNow)
				mActiveFunction->insert(mActiveFunction->end(), block);
			SetResult(curId, block);
		}
		break;
	case BfIRCmd_MaybeChainNewBlock:
		{
			CMD_PARAM(String, name);
			auto newBlock = mIRBuilder->GetInsertBlock();
			if (!newBlock->empty())
			{
				auto bb = llvm::BasicBlock::Create(*mLLVMContext, name.c_str());
				mIRBuilder->CreateBr(bb);
				mActiveFunction->insert(mActiveFunction->end(), bb);
				mIRBuilder->SetInsertPoint(bb);
				newBlock = bb;
			}
			SetResult(curId, newBlock);
		}
		break;
	case BfIRCmd_AddBlock:
		{
			CMD_PARAM(llvm::BasicBlock*, block);
			mActiveFunction->insert(mActiveFunction->end(), block);
		}
		break;
	case BfIRCmd_DropBlocks:
		{
			//TODO: Not even needed
// 			CMD_PARAM(llvm::BasicBlock*, startingBlock);
// 			auto& basicBlockList = mActiveFunction->getBasicBlockList();
// 			int postExitBlockIdx = -1;
// 
// 			auto itr = basicBlockList.rbegin();
// 			int blockIdx = (int)basicBlockList.size() - 1;
// 			while (itr != basicBlockList.rend())
// 			{
// 				auto& block = *itr++;
// 				block.dropAllReferences();
// 				if (&block == startingBlock)
// 				{
// 					postExitBlockIdx = blockIdx;
// 					break;
// 				}
// 				blockIdx--;
// 			}
// 
// 			while ((int)basicBlockList.size() > postExitBlockIdx)
// 			{
// 				auto& block = basicBlockList.back();
// 				block.eraseFromParent();
// 			}
		}
		break;
	case BfIRCmd_MergeBlockDown:
		{
			CMD_PARAM(llvm::BasicBlock*, fromBlock);
			CMD_PARAM(llvm::BasicBlock*, intoBlock);
			//llvm::BasicBlock::InstListType& fromInstList = fromBlock->getInstList();
			//llvm::BasicBlock::InstListType& intoInstList = intoBlock->getInstList();
			//intoInstList.splice(intoInstList.begin(), fromInstList, fromInstList.begin(), fromInstList.end());
			intoBlock->splice(intoBlock->begin(), fromBlock);
			fromBlock->eraseFromParent();
		}
		break;
	case BfIRCmd_GetInsertBlock:
		{
			SetResult(curId, mIRBuilder->GetInsertBlock());
		}
		break;
	case BfIRCmd_SetInsertPoint:
		{
			CMD_PARAM(llvm::BasicBlock*, block);
			if (mLockedBlocks.Contains(block))
				Fail("Attempt to modify locked block");
			mIRBuilder->SetInsertPoint(block);
		}
		break;
	case BfIRCmd_SetInsertPointAtStart:
		{
			CMD_PARAM(llvm::BasicBlock*, block);

			auto itr = block->begin();
			if (itr != block->end())
			{
				// Some instructructions MUST be at start of the block
				auto instType = itr->getType();
				if (llvm::PHINode::classof(&*itr))
					++itr;
			}

			mIRBuilder->SetInsertPoint(block, itr);
			// SetInsertPoint can clear the debug loc so reset it here
			mIRBuilder->SetCurrentDebugLocation(mDebugLoc);			
		}
		break;
	case BfIRCmd_EraseFromParent:
		{
			CMD_PARAM(llvm::BasicBlock*, block);
			block->eraseFromParent();
		}
		break;
	case BfIRCmd_DeleteBlock:
		{
			CMD_PARAM(llvm::BasicBlock*, block);
			delete block;
		}
		break;
	case BfIRCmd_EraseInstFromParent:
		{
			CMD_PARAM(llvm::Value*, instVal);
			BF_ASSERT(llvm::isa<llvm::Instruction>(instVal));
			((llvm::Instruction*)instVal)->eraseFromParent();
		}
		break;
	case BfIRCmd_CreateBr:
	case BfIRCmd_CreateBr_NoCollapse:
		{
			CMD_PARAM(llvm::BasicBlock*, block);
			mIRBuilder->CreateBr(block);
		}
		break;
	case BfIRCmd_CreateBr_Fake:
		{
			CMD_PARAM(llvm::BasicBlock*, block);
			// Do nothing
		}
		break;
	case BfIRCmd_CreateCondBr:
		{
			CMD_PARAM(llvm::Value*, condVal);
			CMD_PARAM(llvm::BasicBlock*, trueBlock);
			CMD_PARAM(llvm::BasicBlock*, falseBlock);
			mIRBuilder->CreateCondBr(condVal, trueBlock, falseBlock);
		}
		break;
	case BfIRCmd_MoveBlockToEnd:
		{
			CMD_PARAM(llvm::BasicBlock*, block);
			block->moveAfter(&block->getParent()->back());
		}
		break;
	case BfIRCmd_CreateSwitch:
		{
			CMD_PARAM(llvm::Value*, val);
			CMD_PARAM(llvm::BasicBlock*, dest);
			CMD_PARAM(int, numCases);
			SetResult(curId, mIRBuilder->CreateSwitch(val, dest, numCases));
		}
		break;
	case BfIRCmd_AddSwitchCase:
		{
			CMD_PARAM(llvm::Value*, switchVal);
			CMD_PARAM(llvm::Value*, caseVal);
			CMD_PARAM(llvm::BasicBlock*, caseBlock);
			BF_ASSERT(llvm::isa<llvm::SwitchInst>(switchVal));
			BF_ASSERT(llvm::isa<llvm::ConstantInt>(caseVal));
			((llvm::SwitchInst*)switchVal)->addCase((llvm::ConstantInt*)caseVal, caseBlock);
		}
		break;
	case BfIRCmd_SetSwitchDefaultDest:
		{
			CMD_PARAM(llvm::Value*, switchVal);
			CMD_PARAM(llvm::BasicBlock*, caseBlock);
			((llvm::SwitchInst*)switchVal)->setDefaultDest(caseBlock);
		}
		break;
	case BfIRCmd_CreatePhi:
		{
			CMD_PARAM(BfIRTypeEx*, type);
			CMD_PARAM(int, incomingCount);			
			BfIRTypedValue result;			
			result.mTypeEx = type;
			result.mValue = mIRBuilder->CreatePHI(type->mLLVMType, incomingCount);
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_AddPhiIncoming:
		{
			CMD_PARAM(llvm::Value*, phiValue);
			CMD_PARAM(llvm::Value*, value);
			CMD_PARAM(llvm::BasicBlock*, comingFrom);
			BF_ASSERT(llvm::isa<llvm::PHINode>(phiValue));
			((llvm::PHINode*)phiValue)->addIncoming(value, comingFrom);
		}
		break;
	case BfIRCmd_GetIntrinsic:
		{
			CMD_PARAM(String, intrinName);
			CMD_PARAM(int, intrinId);
			CMD_PARAM(BfIRTypeEx*, returnType);
			CMD_PARAM(CmdParamVec<BfIRTypeEx*>, paramTypes);

			llvm::Function* func = NULL;

			struct _Intrinsics
			{
				llvm::Intrinsic::ID mID;
				int mArg0;
				int mArg1;
				int mArg2;
			};

			static _Intrinsics intrinsics[] =
			{
				{ (llvm::Intrinsic::ID)-1, -1}, // PLATFORM,
				{ llvm::Intrinsic::fabs, 0, -1},
				{ (llvm::Intrinsic::ID)-2, -1}, // add,
				{ (llvm::Intrinsic::ID)-2, -1}, // and,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicAdd,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicAnd,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicCmpStore,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicCmpStore_Weak,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicCmpXChg,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicFence,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicLoad,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicMax,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicMin,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicNAnd,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicOr,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicStore,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicSub,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicUMax,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicUMin,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicXChg,
				{ (llvm::Intrinsic::ID)-2, -1}, // AtomicXor,
				{ llvm::Intrinsic::bswap, -1},
				{ (llvm::Intrinsic::ID)-2, -1}, // cast,
				{ llvm::Intrinsic::cos, 0, -1},
				{ (llvm::Intrinsic::ID)-2, -1}, // cpuid
				{ llvm::Intrinsic::debugtrap, -1}, // debugtrap,
				{ (llvm::Intrinsic::ID)-2, -1}, // div
				{ (llvm::Intrinsic::ID)-2, -1}, // eq
				{ llvm::Intrinsic::floor, 0, -1},
				{ (llvm::Intrinsic::ID)-2, -1}, // free
				{ (llvm::Intrinsic::ID)-2, -1}, // gt
				{ (llvm::Intrinsic::ID)-2, -1}, // gte
				{ (llvm::Intrinsic::ID)-2, -1}, // index
				{ llvm::Intrinsic::log, 0, -1},
				{ llvm::Intrinsic::log10, 0, -1},
				{ llvm::Intrinsic::log2, 0, -1},
				{ (llvm::Intrinsic::ID)-2, -1}, // lt
				{ (llvm::Intrinsic::ID)-2, -1}, // lte
				{ (llvm::Intrinsic::ID)-2}, // malloc
				{ (llvm::Intrinsic::ID)-2, -1}, // max
				{ llvm::Intrinsic::memcpy, 0, 1, 2},
				{ llvm::Intrinsic::memmove, 0, 2},
				{ llvm::Intrinsic::memset, 0, 2},
				{ (llvm::Intrinsic::ID)-2, -1}, // min
				{ (llvm::Intrinsic::ID)-2, -1}, // mod
				{ (llvm::Intrinsic::ID)-2, -1}, // mul
				{ (llvm::Intrinsic::ID)-2, -1}, // neq
				{ (llvm::Intrinsic::ID)-2, -1}, // not
				{ (llvm::Intrinsic::ID)-2, -1}, // or
				{ llvm::Intrinsic::pow, 0, -1},
				{ llvm::Intrinsic::powi, 0, -1},
				{ llvm::Intrinsic::returnaddress, -1},
				{ llvm::Intrinsic::round, 0, -1},
				{ (llvm::Intrinsic::ID)-2, -1}, // sar
				{ (llvm::Intrinsic::ID)-2, -1}, // shl
				{ (llvm::Intrinsic::ID)-2, -1}, // shr
				{ (llvm::Intrinsic::ID)-2, -1}, // shuffle
				{ llvm::Intrinsic::sin, 0, -1},
				{ llvm::Intrinsic::sqrt, 0, -1},
				{ (llvm::Intrinsic::ID)-2, -1}, // sub,
				{ (llvm::Intrinsic::ID)-2, -1}, // va_arg,
				{ llvm::Intrinsic::vaend, -1}, // va_end,
				{ llvm::Intrinsic::vastart, -1}, // va_start,
				{ (llvm::Intrinsic::ID)-2, -1}, // xgetbv
				{ (llvm::Intrinsic::ID)-2, -1}, // xor
			};
			BF_STATIC_ASSERT(BF_ARRAY_COUNT(intrinsics) == BfIRIntrinsic_COUNT);

			CmdParamVec<llvm::Type*> useParams;
			if (intrinsics[intrinId].mArg0 != -1)
			{
				useParams.push_back(paramTypes[0]->mLLVMType);
				if (intrinsics[intrinId].mArg1 != -1)
				{
					useParams.push_back(paramTypes[1]->mLLVMType);
					if (intrinsics[intrinId].mArg2 != -1)
					{
						useParams.push_back(paramTypes[2]->mLLVMType);
					}
				}
			}

			bool isFakeIntrinsic = (int)intrinsics[intrinId].mID == -2;
			if (isFakeIntrinsic)
			{
				auto intrinsicData = mIntrinsicData.Alloc();
				intrinsicData->mName = intrinName;
				intrinsicData->mIntrinsic = (BfIRIntrinsic)intrinId;
				intrinsicData->mReturnType = returnType;

				BfIRCodeGenEntry entry;
				entry.mKind = BfIRCodeGenEntryKind_IntrinsicData;
				entry.mIntrinsicData = intrinsicData;
				mResults.TryAdd(curId, entry);
				break;
			}

			if (intrinId == BfIRIntrinsic__PLATFORM)
			{
				int colonPos = (int)intrinName.IndexOf(':');
				String platName = intrinName.Substring(0, colonPos);
				String platIntrinName = intrinName.Substring(colonPos + 1);

				if (platName.IsEmpty())
				{
					auto intrinsicData = mIntrinsicData.Alloc();
					intrinsicData->mName = platIntrinName;
					intrinsicData->mIntrinsic = BfIRIntrinsic__PLATFORM;
					intrinsicData->mReturnType = returnType;

					BfIRCodeGenEntry entry;
					entry.mKind = BfIRCodeGenEntryKind_IntrinsicData;
					entry.mIntrinsicData = intrinsicData;
					mResults.TryAdd(curId, entry);
					break;
				}

				llvm::Intrinsic::ID intrin = llvm::Intrinsic::getIntrinsicForClangBuiltin(platName.c_str(), platIntrinName.c_str());
				if ((int)intrin <= 0)
					FatalError(StrFormat("Unable to find intrinsic '%s'", intrinName.c_str()));
				else
					func = llvm::Intrinsic::getDeclaration(mLLVMModule, intrinsics[intrinId].mID, useParams);
			}
			else
			{
				BF_ASSERT(intrinsics[intrinId].mID != (llvm::Intrinsic::ID)-1);
				func = llvm::Intrinsic::getDeclaration(mLLVMModule, intrinsics[intrinId].mID, useParams);
			}
			mIntrinsicReverseMap[func] = intrinId;

			auto funcTypeEx = CreateTypeEx(func->getFunctionType());
			funcTypeEx->mMembers.Add(returnType);
			for (auto typeEx : paramTypes)
				funcTypeEx->mMembers.Add(typeEx);

			BfIRTypedValue result;
			result.mTypeEx = GetPointerTypeEx(funcTypeEx);
			result.mValue = func;
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_CreateFunctionType:
		{
			CMD_PARAM(BfIRTypeEx*, resultType);
			CMD_PARAM(CmdParamVec<BfIRTypeEx*>, paramTypes);
			CMD_PARAM(bool, isVarArg);
			
			CmdParamVec<llvm::Type*> llvmTypes;
			for (auto typeEx : paramTypes)
			{
				if (typeEx->mLLVMType->isPointerTy())
				{
					BF_ASSERT(!typeEx->mMembers.IsEmpty());
				}

				llvmTypes.push_back(typeEx->mLLVMType);
			}

			auto funcType = llvm::FunctionType::get(resultType->mLLVMType, llvmTypes, isVarArg);

			auto typeEx = CreateTypeEx(funcType);
			if (typeEx->mMembers.IsEmpty())
			{
				typeEx->mMembers.Add(resultType);
				for (auto paramType : paramTypes)			
					typeEx->mMembers.Add(paramType);
			}

			SetResult(curId, typeEx);
		}
		break;
	case BfIRCmd_CreateFunction:
		{
			BfIRTypeEx* type = NULL;
			ReadFunctionType(type);

			BfIRLinkageType linkageType = (BfIRLinkageType)mStream->Read();
			CMD_PARAM(String, name);

			BfIRTypedValue result;
			result.mTypeEx = GetPointerTypeEx(type);

			auto func = mLLVMModule->getFunction(name.c_str());
			if ((func == NULL) || (func->getFunctionType() != type->mLLVMType))
				func = llvm::Function::Create((llvm::FunctionType*)type->mLLVMType, LLVMMapLinkageType(linkageType), name.c_str(), mLLVMModule);
			result.mValue = func;
			SetResult(curId, result);
		}
		break;
	case BfIRCmd_SetFunctionName:
		{
			CMD_PARAM(llvm::Value*, func);
			CMD_PARAM(String, name);
			llvm::Function* llvmFunc = llvm::dyn_cast<llvm::Function>(func);
			llvmFunc->setName(name.c_str());
		}
		break;
	case BfIRCmd_EnsureFunctionPatchable:
		{
			int minPatchSize = 5;

			int guessInstBytes = 1; // ret
			guessInstBytes += mActiveFunction->getFunctionType()->getNumParams() * 4;

			if (guessInstBytes < 5)
			{
				for (auto& block : *mActiveFunction)
				{
					for (auto& inst : block)
					{
						if (auto loadInst = llvm::dyn_cast<llvm::LoadInst>(&inst))
							guessInstBytes += 2;
						else if (auto storeInst = llvm::dyn_cast<llvm::StoreInst>(&inst))
							guessInstBytes += 2;
						else if (auto callInst = llvm::dyn_cast<llvm::CallInst>(&inst))
						{
							auto calledValue = callInst->getCalledOperand();

							if (calledValue == mNopInlineAsm)
								guessInstBytes += 1;
							else if (auto func = llvm::dyn_cast<llvm::Function>(calledValue))
							{
								if (!func->isIntrinsic())
									guessInstBytes += 4;
							}
							else
								guessInstBytes += 4;
						}

						if (guessInstBytes >= minPatchSize)
							break;
					}
				}
			}

			for (int i = guessInstBytes; i < minPatchSize; i++)
				AddNop();
		}
		break;
	case BfIRCmd_RemapBindFunction:
		{
			CMD_PARAM(BfIRTypedValue, 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

			llvm::Function* llvmFunc = llvm::dyn_cast<llvm::Function>(func.mValue);

			if (llvmFunc != NULL)
			{
				// I don't know why we mixed in HSPreserveIdx - that causes bound address to change after reloading, basically totally breaking
				//  the whole point of this.
				//String funcName = StrFormat("bf_hs_preserve@%d@%s", mModule->mCompiler->mHSPreserveIdx++, func->getName());
				String funcName = StrFormat("bf_hs_preserve@%s_%s", llvmFunc->getName().data(), mLLVMModule->getName().data());
				llvm::GlobalVariable* globalVariable = mLLVMModule->getGlobalVariable(funcName.c_str());
				if (globalVariable == NULL)
				{
					globalVariable = new llvm::GlobalVariable(*mLLVMModule, llvmFunc->getType(), true, llvm::GlobalValue::ExternalLinkage, (llvm::Constant*)llvmFunc, funcName.c_str());
				}

				BfIRTypedValue result;
				result.mTypeEx = func.mTypeEx;
				result.mValue = mIRBuilder->CreateLoad(result.mTypeEx->mLLVMType, globalVariable);
				SetResult(curId, result);
			}
			else
				SetResult(curId, func);
		}
		break;
	case BfIRCmd_SetActiveFunction:
		{
			BfIRTypedValue func;
			ReadFunction(func);
			mActiveFunction = (llvm::Function*)func.mValue;
			if (mActiveFunction == NULL)
				mActiveFunctionType = NULL;
			else
				mActiveFunctionType = GetTypeMember(func.mTypeEx, 0);
		}
		break;
	case BfIRCmd_CreateCall:
		{
			BfIRTypedValue func;
			BfIRCodeGenEntry* codeGenEntry = NULL;
			Read(func, &codeGenEntry);
			CMD_PARAM(CmdParamVec<BfIRTypedValue>, args);			

			if ((func.mValue == NULL) && (codeGenEntry != NULL) && (codeGenEntry->mKind == BfIRCodeGenEntryKind_IntrinsicData))
			{
				auto intrinsicData = codeGenEntry->mIntrinsicData;

				switch (intrinsicData->mIntrinsic)
				{
				case BfIRIntrinsic__PLATFORM:
				{
					if (intrinsicData->mName == "add_string_to_section")
					{
						llvm::StringRef strContent[2];
						llvm::ConstantDataArray* dataArray;

						for (int i = 0; i < 2; i++)
						{
							if (const llvm::ConstantExpr* ce = llvm::dyn_cast<llvm::ConstantExpr>(args[i].mValue))
							{
								llvm::Value* firstOperand = ce->getOperand(0);
								if (llvm::GlobalVariable* gv = llvm::dyn_cast<llvm::GlobalVariable>(firstOperand))
								{
									if (gv->getType()->isPointerTy())
									{
										if (dataArray = llvm::dyn_cast<llvm::ConstantDataArray>(gv->getInitializer()))
											strContent[i] = dataArray->getAsString();
									}
								}
							}
							else
								FatalError("Value is not ConstantExpr");
						}

						static int symbolCount = 0;
						symbolCount++;

						auto charType = llvm::IntegerType::get(*mLLVMContext, 8);
						std::vector<llvm::Constant*> chars(strContent[0].size());
						for (unsigned int i = 0; i < strContent[0].size(); i++)
						{
							chars[i] = llvm::ConstantInt::get(charType, strContent[0][i]);;
						}

						chars.push_back(llvm::ConstantInt::get(charType, 0));
						auto stringType = llvm::ArrayType::get(charType, chars.size());

						std::string symbolName = strContent[1].str() + "_" + std::to_string(symbolCount);
						llvm::StringRef resultStringRef(symbolName);

						auto globalVar = (llvm::GlobalVariable*)mLLVMModule->getOrInsertGlobal(symbolName, stringType);
						globalVar->setSection(strContent[1]);
						globalVar->setInitializer(llvm::ConstantArray::get(stringType, chars));
						globalVar->setConstant(true);
						globalVar->setLinkage(llvm::GlobalValue::LinkageTypes::ExternalLinkage);
						globalVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);

						SetResult(curId, llvm::ConstantExpr::getBitCast(globalVar, charType->getPointerTo()));
						break;
					}

					FatalError(StrFormat("Unable to find intrinsic '%s'", intrinsicData->mName.c_str()));
					break;
				}
				case BfIRIntrinsic_Add:
				case BfIRIntrinsic_And:
				case BfIRIntrinsic_Div:
				case BfIRIntrinsic_Eq:
				case BfIRIntrinsic_Gt:
				case BfIRIntrinsic_GtE:
				case BfIRIntrinsic_Lt:
				case BfIRIntrinsic_LtE:
				case BfIRIntrinsic_Mod:
				case BfIRIntrinsic_Mul:
				case BfIRIntrinsic_Neq:
				case BfIRIntrinsic_Or:
				case BfIRIntrinsic_Sub:
				case BfIRIntrinsic_Xor:
					{
						auto val0 = TryToVector(args[0]);
						if (val0 != NULL)
						{
							auto vecType = llvm::dyn_cast<llvm::VectorType>(val0->getType());
							auto elemType = vecType->getElementType();
							bool isFP = elemType->isFloatingPointTy();

							llvm::Value* val1;
							if (args.size() < 2)
							{
								llvm::Value* val;
								if (isFP)
									val = llvm::ConstantFP::get(elemType, 1);
								else
									val = llvm::ConstantInt::get(elemType, 1);
								val1 = mIRBuilder->CreateInsertElement(llvm::UndefValue::get(vecType), val, (uint64)0);
								val1 = mIRBuilder->CreateInsertElement(val1, val, (uint64)1);
								val1 = mIRBuilder->CreateInsertElement(val1, val, (uint64)2);
								val1 = mIRBuilder->CreateInsertElement(val1, val, (uint64)3);
							}
							else if (args[1].mValue->getType()->isPointerTy())
							{
								auto ptrVal1 = mIRBuilder->CreateBitCast(args[1].mValue, vecType->getPointerTo());
								val1 = mIRBuilder->CreateAlignedLoad(vecType, ptrVal1, llvm::MaybeAlign(1));
							}
							else if (args[1].mValue->getType()->isVectorTy())
							{
								val1 = args[1].mValue;
							}
							else
							{
								val1 = mIRBuilder->CreateInsertElement(llvm::UndefValue::get(vecType), args[1].mValue, (uint64)0);
								val1 = mIRBuilder->CreateInsertElement(val1, args[1].mValue, (uint64)1);
								val1 = mIRBuilder->CreateInsertElement(val1, args[1].mValue, (uint64)2);
								val1 = mIRBuilder->CreateInsertElement(val1, args[1].mValue, (uint64)3);
							}

							if (isFP)
							{
								llvm::Value* result = NULL;
								switch (intrinsicData->mIntrinsic)
								{
								case BfIRIntrinsic_Add:
									result = mIRBuilder->CreateFAdd(val0, val1);
									break;
								case BfIRIntrinsic_Div:
									result = mIRBuilder->CreateFDiv(val0, val1);
									break;
								case BfIRIntrinsic_Eq:
									result = mIRBuilder->CreateFCmpOEQ(val0, val1);
									break;
								case BfIRIntrinsic_Gt:
									result = mIRBuilder->CreateFCmpOGT(val0, val1);
									break;
								case BfIRIntrinsic_GtE:
									result = mIRBuilder->CreateFCmpOGE(val0, val1);
									break;
								case BfIRIntrinsic_Lt:
									result = mIRBuilder->CreateFCmpOLT(val0, val1);
									break;
								case BfIRIntrinsic_LtE:
									result = mIRBuilder->CreateFCmpOLE(val0, val1);
									break;
								case BfIRIntrinsic_Mod:
									result = mIRBuilder->CreateFRem(val0, val1);
									break;
								case BfIRIntrinsic_Mul:
									result = mIRBuilder->CreateFMul(val0, val1);
									break;
								case BfIRIntrinsic_Neq:
									result = mIRBuilder->CreateFCmpONE(val0, val1);
									break;
								case BfIRIntrinsic_Sub:
									result = mIRBuilder->CreateFSub(val0, val1);
									break;
								default:
									FatalError("Intrinsic argument error");
								}

								if (result != NULL)
								{
									if (auto vecType = llvm::dyn_cast<llvm::FixedVectorType>(result->getType()))
									{
										if (auto intType = llvm::dyn_cast<llvm::IntegerType>(vecType->getElementType()))
										{
											if (intType->getBitWidth() == 1)
											{
												auto toType = llvm::FixedVectorType::get(llvm::IntegerType::get(*mLLVMContext, 8), vecType->getNumElements());
												result = mIRBuilder->CreateZExt(result, toType);
											}
										}
									}

									SetResult(curId, result);
								}
							}
							else
							{
								llvm::Value* result = NULL;
								switch (intrinsicData->mIntrinsic)
								{
								case BfIRIntrinsic_And:
									result = mIRBuilder->CreateAnd(val0, val1);
									break;
								case BfIRIntrinsic_Add:
									result = mIRBuilder->CreateAdd(val0, val1);
									break;
								case BfIRIntrinsic_Div:
									result = mIRBuilder->CreateSDiv(val0, val1);
									break;
								case BfIRIntrinsic_Eq:
									result = mIRBuilder->CreateICmpEQ(val0, val1);
									break;
								case BfIRIntrinsic_Gt:
									result = mIRBuilder->CreateICmpSGT(val0, val1);
									break;
								case BfIRIntrinsic_GtE:
									result = mIRBuilder->CreateICmpSGE(val0, val1);
									break;
								case BfIRIntrinsic_Lt:
									result = mIRBuilder->CreateICmpSLT(val0, val1);
									break;
								case BfIRIntrinsic_LtE:
									result = mIRBuilder->CreateICmpSLE(val0, val1);
									break;
								case BfIRIntrinsic_Mod:
									result = mIRBuilder->CreateSRem(val0, val1);
									break;
								case BfIRIntrinsic_Mul:
									result = mIRBuilder->CreateMul(val0, val1);
									break;
								case BfIRIntrinsic_Neq:
									result = mIRBuilder->CreateICmpNE(val0, val1);
									break;
								case BfIRIntrinsic_Or:
									result = mIRBuilder->CreateOr(val0, val1);
									break;
								case BfIRIntrinsic_Sub:
									result = mIRBuilder->CreateSub(val0, val1);
									break;
								case BfIRIntrinsic_Xor:
									result = mIRBuilder->CreateXor(val0, val1);
									break;
								default:
									FatalError("Intrinsic argument error");
								}

								if (result != NULL)
								{
									if (auto vecType = llvm::dyn_cast<llvm::FixedVectorType>(result->getType()))
									{
										if (auto intType = llvm::dyn_cast<llvm::IntegerType>(vecType->getElementType()))
										{
											if (intType->getBitWidth() == 1)
											{
												auto toType = llvm::FixedVectorType::get(llvm::IntegerType::get(*mLLVMContext, 8), vecType->getNumElements());
												result = mIRBuilder->CreateZExt(result, toType);
											}
										}
									}

									SetResult(curId, result);
								}
							}
						}
						else if (auto ptrType = llvm::dyn_cast<llvm::PointerType>(args[1].mTypeEx->mLLVMType))
						{
							//auto ptrElemType = ptrType->getElementType();
							auto ptrElemType = GetLLVMPointerElementType(args[1].mTypeEx);
							if (auto arrType = llvm::dyn_cast<llvm::ArrayType>(ptrElemType))
							{
								auto vecType = llvm::FixedVectorType::get(arrType->getArrayElementType(), (uint)arrType->getArrayNumElements());
								auto vecPtrType = vecType->getPointerTo();

								llvm::Value* val0;
								val0 = mIRBuilder->CreateInsertElement(llvm::UndefValue::get(vecType), args[0].mValue, (uint64)0);
								val0 = mIRBuilder->CreateInsertElement(val0, args[0].mValue, (uint64)1);
								val0 = mIRBuilder->CreateInsertElement(val0, args[0].mValue, (uint64)2);
								val0 = mIRBuilder->CreateInsertElement(val0, args[0].mValue, (uint64)3);

								auto ptrVal1 = mIRBuilder->CreateBitCast(args[1].mValue, vecPtrType);
								auto val1 = mIRBuilder->CreateAlignedLoad(vecType, ptrVal1, llvm::MaybeAlign(1));

								switch (intrinsicData->mIntrinsic)
								{
								case BfIRIntrinsic_Div:
									SetResult(curId, mIRBuilder->CreateFDiv(val0, val1));
									break;
								case BfIRIntrinsic_Mod:
									SetResult(curId, mIRBuilder->CreateFRem(val0, val1));
									break;
								default:
									FatalError("Intrinsic argument error");
								}
							}
						}
						else
						{
							FatalError("Intrinsic argument error");
						}
					}
					break;
				case BfIRIntrinsic_Min:
				case BfIRIntrinsic_Max:
					{
						// Get arguments as vectors
						auto val0 = TryToVector(args[0]);
						if (val0 == NULL)
							FatalError("Intrinsic argument error");

						auto val1 = TryToVector(args[1]);
						if (val1 == NULL)
							FatalError("Intrinsic argument error");

						// Make sure both argument types are the same
						auto vecType = llvm::dyn_cast<llvm::VectorType>(val0->getType());
						if (vecType != llvm::dyn_cast<llvm::VectorType>(val1->getType()))
							FatalError("Intrinsic argument error");

						// Make sure the type is not scalable
						if (vecType->getElementCount().isScalable())
							FatalError("Intrinsic argument error");

						// Make sure the element type is either float or double
						auto elemType = vecType->getElementType();
						if (!elemType->isFloatTy() && !elemType->isDoubleTy())
							FatalError("Intrinsic argument error");

						// Get some properties for easier access
						bool isFloat = elemType->isFloatTy();
						bool isMin = intrinsicData->mIntrinsic == BfIRIntrinsic_Min;
						auto elemCount = vecType->getElementCount().getFixedValue();

						// Get the intrinsic function
						const char* funcName;

						if (isFloat)
						{
							if (elemCount == 4)
							{
								funcName = isMin ? "llvm.x86.sse.min.ps" : "llvm.x86.sse.max.ps";
								SetActiveFunctionSimdType(BfIRSimdType_SSE);
							}
							else if (elemCount == 8)
							{
								funcName = isMin ? "llvm.x86.avx.min.ps.256" : "llvm.x86.avx.max.ps.256";
								SetActiveFunctionSimdType(BfIRSimdType_AVX2);
							}
							else if (elemCount == 16)
							{
								funcName = isMin ? "llvm.x86.avx512.min.ps.512" : "llvm.x86.avx512.max.ps.512";
								SetActiveFunctionSimdType(BfIRSimdType_AVX512);
							}
							else
								FatalError("Intrinsic argument error");
						}
						else
						{
							if (elemCount == 2)
							{
								funcName = isMin ? "llvm.x86.sse.min.pd" : "llvm.x86.sse.max.pd";
								SetActiveFunctionSimdType(BfIRSimdType_SSE);
							}
							else if (elemCount == 4)
							{
								funcName = isMin ? "llvm.x86.avx.min.pd.256" : "llvm.x86.avx.max.pd.256";
								SetActiveFunctionSimdType(BfIRSimdType_AVX2);
							}
							else if (elemCount == 8)
							{
								funcName = isMin ? "llvm.x86.avx512.min.pd.512" : "llvm.x86.avx512.max.pd.512";
								SetActiveFunctionSimdType(BfIRSimdType_AVX512);
							}
							else
								FatalError("Intrinsic argument error");
						}

						auto func = mLLVMModule->getOrInsertFunction(funcName, vecType, vecType, vecType);

						// Call intrinsic
						llvm::SmallVector<llvm::Value*, 2> args;
						args.push_back(val0);
						args.push_back(val1);

						SetResult(curId, mIRBuilder->CreateCall(func, args));
					}
					break;
				case BfIRIntrinsic_Cpuid:
					{
						llvm::Type* elemType = llvm::Type::getInt32Ty(*mLLVMContext);

						// Check argument errors
						if (args.size() != 6 || !args[0].mValue->getType()->isIntegerTy(32) || !args[1].mValue->getType()->isIntegerTy(32))
							FatalError("Intrinsic argument error");

// 						for (int i = 2; i < 6; i++)
// 						{
// 							llvm::Type* type = args[i]->getType();
// 
// 							if (!type->isPointerTy() || !GetPointerElementType(args[1])->isIntegerTy(32))
// 								FatalError("Intrinsic argument error");
// 						}

						// Get asm return type
						llvm::SmallVector<llvm::Type*, 4> asmReturnTypes;
						asmReturnTypes.push_back(elemType);
						asmReturnTypes.push_back(elemType);
						asmReturnTypes.push_back(elemType);
						asmReturnTypes.push_back(elemType);

						llvm::Type* returnType = llvm::StructType::get(*mLLVMContext, asmReturnTypes);

						// Get asm function
						llvm::SmallVector<llvm::Type*, 2> funcParams;
						funcParams.push_back(elemType);
						funcParams.push_back(elemType);

						llvm::FunctionType* funcType = llvm::FunctionType::get(returnType, funcParams, false);
						llvm::InlineAsm* func = llvm::InlineAsm::get(funcType, "xchgq %rbx,${1:q}\ncpuid\nxchgq %rbx,${1:q}", "={ax},=r,={cx},={dx},0,2,~{dirflag},~{fpsr},~{flags}", false);

						// Call asm function
						llvm::SmallVector<llvm::Value*, 2> funcArgs;
						funcArgs.push_back(args[0].mValue);
						funcArgs.push_back(args[1].mValue);

						llvm::Value* asmResult = mIRBuilder->CreateCall(func, funcArgs);

						// Store results
						mIRBuilder->CreateStore(mIRBuilder->CreateExtractValue(asmResult, 0), args[2].mValue);
						mIRBuilder->CreateStore(mIRBuilder->CreateExtractValue(asmResult, 1), args[3].mValue);
						mIRBuilder->CreateStore(mIRBuilder->CreateExtractValue(asmResult, 2), args[4].mValue);
						mIRBuilder->CreateStore(mIRBuilder->CreateExtractValue(asmResult, 3), args[5].mValue);
					}
					break;
				case BfIRIntrinsic_Xgetbv:
					{
						if (args.size() != 1 || !args[0].mValue->getType()->isIntegerTy(32))
							FatalError("Intrinsic argument error");

						auto func = mLLVMModule->getOrInsertFunction("llvm.x86.xgetbv", llvm::Type::getInt64Ty(*mLLVMContext), llvm::Type::getInt32Ty(*mLLVMContext));
						SetResult(curId, mIRBuilder->CreateCall(func, args[0].mValue));
					}
					break;
				case BfIRIntrinsic_Not:
					{
						auto val0 = TryToVector(args[0]);
						SetResult(curId, mIRBuilder->CreateNot(val0));
					}
					break;
				case BfIRIntrinsic_Shuffle:
					{
						llvm::SmallVector<int, 8> intMask;
						for (int i = 7; i < (int)intrinsicData->mName.length(); i++)
							intMask.push_back((int)(intrinsicData->mName[i] - '0'));

						auto val0 = TryToVector(args[0]);

						if (val0 != NULL)
						{
							SetResult(curId, mIRBuilder->CreateShuffleVector(val0, val0, intMask));
						}
						else
						{
							FatalError("Intrinsic argument error");
						}
					}
					break;
				case BfIRIntrinsic_Index:
					{
						llvm::Value* gepArgs[] = {
							llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), 0),
							args[1].mValue };
						auto gep = mIRBuilder->CreateInBoundsGEP(GetLLVMPointerElementType(args[0].mTypeEx), args[0].mValue, llvm::makeArrayRef(gepArgs));
						if (args.size() >= 3)
							mIRBuilder->CreateStore(args[2].mValue, gep);
						else
						{
							BfIRTypedValue result;
							result.mTypeEx = GetTypeMember(args[0].mTypeEx, 0);
							result.mValue = mIRBuilder->CreateLoad(result.mTypeEx->mLLVMType, gep);
							SetResult(curId, result);
						}
					}
					break;

				case BfIRIntrinsic_AtomicCmpStore:
				case BfIRIntrinsic_AtomicCmpStore_Weak:
				case BfIRIntrinsic_AtomicCmpXChg:
					{
						auto memoryKindConst = llvm::dyn_cast<llvm::ConstantInt>(args[3].mValue);
						if (memoryKindConst == NULL)
						{
							FatalError("Non-constant success ordering on Atomic_CmpXChg");
							break;
						}
						auto memoryKind = (BfIRAtomicOrdering)memoryKindConst->getSExtValue();

						auto successOrdering = llvm::AtomicOrdering::Unordered;
						auto failOrdering = llvm::AtomicOrdering::Unordered;
						switch (memoryKind & BfIRAtomicOrdering_ORDERMASK)
						{
						case BfIRAtomicOrdering_Acquire:
							successOrdering = llvm::AtomicOrdering::Acquire;
							failOrdering = llvm::AtomicOrdering::Acquire;
							break;
						case BfIRAtomicOrdering_AcqRel:
							successOrdering = llvm::AtomicOrdering::AcquireRelease;
							failOrdering = llvm::AtomicOrdering::Acquire;
							break;
						case BfIRAtomicOrdering_Relaxed:
							successOrdering = llvm::AtomicOrdering::Monotonic;
							failOrdering = llvm::AtomicOrdering::Monotonic;
							break;
						case BfIRAtomicOrdering_Release:
							successOrdering = llvm::AtomicOrdering::Release;
							failOrdering = llvm::AtomicOrdering::Monotonic;
							break;
						case BfIRAtomicOrdering_SeqCst:
							successOrdering = llvm::AtomicOrdering::SequentiallyConsistent;
							failOrdering = llvm::AtomicOrdering::SequentiallyConsistent;
							break;
						default:
							Fail("Invalid success ordering on Atomic_CmpXChg");
							break;
						}

						if (args.size() >= 5)
						{
							auto memoryKindConst = llvm::dyn_cast<llvm::ConstantInt>(args[4].mValue);
							if (memoryKindConst == NULL)
							{
								FatalError("Non-constant fail ordering on Atomic_CmpXChg");
								break;
							}
							auto memoryKind = (BfIRAtomicOrdering)memoryKindConst->getSExtValue();
							switch (memoryKind & BfIRAtomicOrdering_ORDERMASK)
							{
							case BfIRAtomicOrdering_Acquire:
								failOrdering = llvm::AtomicOrdering::Acquire;
								break;
							case BfIRAtomicOrdering_Relaxed:
								failOrdering = llvm::AtomicOrdering::Monotonic;
								break;
							case BfIRAtomicOrdering_SeqCst:
								failOrdering = llvm::AtomicOrdering::SequentiallyConsistent;
								break;
							default:
								FatalError("Invalid fail ordering on Atomic_CmpXChg");
								break;
							}
						}

						auto inst = mIRBuilder->CreateAtomicCmpXchg(args[0].mValue, args[1].mValue, args[2].mValue, llvm::MaybeAlign(), successOrdering, failOrdering);
						if (intrinsicData->mIntrinsic == BfIRIntrinsic_AtomicCmpStore_Weak)
							inst->setWeak(true);
						if ((memoryKind & BfIRAtomicOrdering_Volatile) != 0)
							inst->setVolatile(true);
						if (intrinsicData->mIntrinsic == BfIRIntrinsic_AtomicCmpXChg)
						{
							auto prevVal = mIRBuilder->CreateExtractValue(inst, 0);
							SetResult(curId, prevVal);
						}
						else
						{
							auto successVal = mIRBuilder->CreateExtractValue(inst, 1);
							SetResult(curId, successVal);
						}
					}
					break;
				case BfIRIntrinsic_AtomicFence:
					{
						if (args.size() == 0)
						{
							if ((mTargetTriple.GetMachineType() != BfMachineType_x86) && (mTargetTriple.GetMachineType() != BfMachineType_x64))
							{
								Fail("Unable to create compiler barrier on this platform");
							}
							else
							{
								// Compiler barrier

								llvm::SmallVector<llvm::Type*, 8> paramTypes;
								llvm::FunctionType* funcType = llvm::FunctionType::get(llvm::Type::getVoidTy(*mLLVMContext), paramTypes, false);
								auto fenceFunc = llvm::InlineAsm::get(funcType,
									"", "~{memory},~{dirflag},~{fpsr},~{flags}", true, false, llvm::InlineAsm::AD_ATT);
								mIRBuilder->CreateCall(fenceFunc);
							}
							break;
						}

						auto memoryKindConst = llvm::dyn_cast<llvm::ConstantInt>(args[0].mValue);
						if (memoryKindConst == NULL)
						{
							FatalError("Non-constant success ordering on AtomicFence");
							break;
						}

						auto memoryKind = (BfIRAtomicOrdering)memoryKindConst->getSExtValue();
						auto ordering = llvm::AtomicOrdering::SequentiallyConsistent;
						switch (memoryKind & BfIRAtomicOrdering_ORDERMASK)
						{
						case BfIRAtomicOrdering_Acquire:
							ordering = llvm::AtomicOrdering::Acquire;
							break;
						case BfIRAtomicOrdering_AcqRel:
							ordering = llvm::AtomicOrdering::AcquireRelease;
							break;
						case BfIRAtomicOrdering_Release:
							ordering = llvm::AtomicOrdering::Release;
							break;
						case BfIRAtomicOrdering_SeqCst:
							ordering = llvm::AtomicOrdering::SequentiallyConsistent;
							break;
						default:
							Fail("Invalid ordering on atomic operation");
							break;
						}

						mIRBuilder->CreateFence(ordering);
					}
					break;
				case BfIRIntrinsic_AtomicLoad:
					{
						auto memoryKindConst = llvm::dyn_cast<llvm::ConstantInt>(args[1].mValue);
						if (memoryKindConst == NULL)
						{
							FatalError("Non-constant success ordering on AtomicLoad");
							break;
						}

						BfIRTypedValue result;
						result.mTypeEx = GetTypeMember(args[0].mTypeEx, 0);

						auto memoryKind = (BfIRAtomicOrdering)memoryKindConst->getSExtValue();
						auto ptrType = llvm::dyn_cast<llvm::PointerType>(args[0].mValue->getType());
						auto loadInst = mIRBuilder->CreateAlignedLoad(result.mTypeEx->mLLVMType, args[0].mValue, llvm::MaybeAlign((uint)GetLLVMPointerElementType(args[0].mTypeEx)->getPrimitiveSizeInBits() / 8));
						switch (memoryKind & BfIRAtomicOrdering_ORDERMASK)
						{
						case BfIRAtomicOrdering_Acquire:
							loadInst->setAtomic(llvm::AtomicOrdering::Acquire);
							break;
						case BfIRAtomicOrdering_Relaxed:
							loadInst->setAtomic(llvm::AtomicOrdering::Monotonic);
							break;
						case BfIRAtomicOrdering_SeqCst:
							loadInst->setAtomic(llvm::AtomicOrdering::SequentiallyConsistent);
							break;
						default:
							BF_FATAL("BadAtomic");
						}
						if ((memoryKind & BfIRAtomicOrdering_Volatile) != 0)
							loadInst->setVolatile(true);
						result.mValue = loadInst;

						SetResult(curId, result);
					}
					break;
				case BfIRIntrinsic_AtomicStore:
					{
						auto memoryKindConst = llvm::dyn_cast<llvm::ConstantInt>(args[2].mValue);
						if (memoryKindConst == NULL)
						{
							FatalError("Non-constant success ordering on AtomicLoad");
							break;
						}
						auto memoryKind = (BfIRAtomicOrdering)memoryKindConst->getSExtValue();

						auto storeInst = mIRBuilder->CreateAlignedStore(args[1].mValue, args[0].mValue, llvm::MaybeAlign((uint)args[1].mValue->getType()->getPrimitiveSizeInBits() / 8));
						switch (memoryKind & BfIRAtomicOrdering_ORDERMASK)
						{
						case BfIRAtomicOrdering_Relaxed:
							storeInst->setAtomic(llvm::AtomicOrdering::Monotonic);
							break;
						case BfIRAtomicOrdering_Release:
							storeInst->setAtomic(llvm::AtomicOrdering::Release);
							break;
						case BfIRAtomicOrdering_SeqCst:
							storeInst->setAtomic(llvm::AtomicOrdering::SequentiallyConsistent);
							break;
						}
						if ((memoryKind & BfIRAtomicOrdering_Volatile) != 0)
							storeInst->setVolatile(true);
						SetResult(curId, storeInst);
					}
					break;
				case BfIRIntrinsic_AtomicAdd:
				case BfIRIntrinsic_AtomicAnd:
				case BfIRIntrinsic_AtomicMax:
				case BfIRIntrinsic_AtomicMin:
				case BfIRIntrinsic_AtomicNAnd:
				case BfIRIntrinsic_AtomicOr:
				case BfIRIntrinsic_AtomicSub:
				case BfIRIntrinsic_AtomicUMax:
				case BfIRIntrinsic_AtomicUMin:
				case BfIRIntrinsic_AtomicXChg:
				case BfIRIntrinsic_AtomicXor:
					{
						bool isFloat = args[1].mValue->getType()->isFloatingPointTy();

						auto op = llvm::AtomicRMWInst::BinOp::Add;
						switch (intrinsicData->mIntrinsic)
						{
						case BfIRIntrinsic_AtomicAdd:
							op = llvm::AtomicRMWInst::BinOp::Add;
							break;
						case BfIRIntrinsic_AtomicAnd:
							op = llvm::AtomicRMWInst::BinOp::And;
							break;
						case BfIRIntrinsic_AtomicMax:
							op = llvm::AtomicRMWInst::BinOp::Max;
							break;
						case BfIRIntrinsic_AtomicMin:
							op = llvm::AtomicRMWInst::BinOp::Min;
							break;
						case BfIRIntrinsic_AtomicNAnd:
							op = llvm::AtomicRMWInst::BinOp::Nand;
							break;
						case BfIRIntrinsic_AtomicOr:
							op = llvm::AtomicRMWInst::BinOp::Or;
							break;
						case BfIRIntrinsic_AtomicSub:
							op = llvm::AtomicRMWInst::BinOp::Sub;
							break;
						case BfIRIntrinsic_AtomicUMax:
							op = llvm::AtomicRMWInst::BinOp::UMax;
							break;
						case BfIRIntrinsic_AtomicUMin:
							op = llvm::AtomicRMWInst::BinOp::UMin;
							break;
						case BfIRIntrinsic_AtomicXChg:
							op = llvm::AtomicRMWInst::BinOp::Xchg;
							break;
						case BfIRIntrinsic_AtomicXor:
							op = llvm::AtomicRMWInst::BinOp::Xor;
							break;
						default: break;
						}

						auto memoryKindConst = llvm::dyn_cast<llvm::ConstantInt>(args[2].mValue);
						if (memoryKindConst == NULL)
						{
							FatalError("Non-constant ordering on atomic operation");
							break;
						}

						auto memoryKind = (BfIRAtomicOrdering)memoryKindConst->getSExtValue();
						auto ordering = llvm::AtomicOrdering::Unordered;
						switch (memoryKind & BfIRAtomicOrdering_ORDERMASK)
						{
						case BfIRAtomicOrdering_Acquire:
							ordering = llvm::AtomicOrdering::Acquire;
							break;
						case BfIRAtomicOrdering_AcqRel:
							ordering = llvm::AtomicOrdering::AcquireRelease;
							break;
						case BfIRAtomicOrdering_Relaxed:
							ordering = llvm::AtomicOrdering::Monotonic;
							break;
						case BfIRAtomicOrdering_Release:
							ordering = llvm::AtomicOrdering::Release;
							break;
						case BfIRAtomicOrdering_SeqCst:
							ordering = llvm::AtomicOrdering::SequentiallyConsistent;
							break;
						default:
							Fail("Invalid ordering on atomic operation");
							break;
						}

						auto atomicRMW = mIRBuilder->CreateAtomicRMW(op, args[0].mValue, args[1].mValue, llvm::MaybeAlign(), ordering);
						if ((memoryKind & BfIRAtomicOrdering_Volatile) != 0)
							atomicRMW->setVolatile(true);
						llvm::Value* result = atomicRMW;
						if ((memoryKind & BfIRAtomicOrdering_ReturnModified) != 0)
						{
							switch (intrinsicData->mIntrinsic)
							{
							case BfIRIntrinsic_AtomicAdd:
								if (isFloat)
									result = mIRBuilder->CreateFAdd(atomicRMW, args[1].mValue);
								else
									result = mIRBuilder->CreateAdd(atomicRMW, args[1].mValue);
								break;
							case BfIRIntrinsic_AtomicAnd:
								result = mIRBuilder->CreateAnd(atomicRMW, args[1].mValue);
								break;
							case BfIRIntrinsic_AtomicMax:
							case BfIRIntrinsic_AtomicMin:
							case BfIRIntrinsic_AtomicUMax:
							case BfIRIntrinsic_AtomicUMin:
								{
									llvm::Value* cmpVal = NULL;
									switch (intrinsicData->mIntrinsic)
									{
									case BfIRIntrinsic_AtomicMax:
										if (isFloat)
											cmpVal = mIRBuilder->CreateFCmpOGE(atomicRMW, args[1].mValue);
										else
											cmpVal = mIRBuilder->CreateICmpSGE(atomicRMW, args[1].mValue);
										break;
									case BfIRIntrinsic_AtomicMin:
										if (isFloat)
											cmpVal = mIRBuilder->CreateFCmpOLE(atomicRMW, args[1].mValue);
										else
											cmpVal = mIRBuilder->CreateICmpSLE(atomicRMW, args[1].mValue);
										break;
									case BfIRIntrinsic_AtomicUMax:
										cmpVal = mIRBuilder->CreateICmpUGE(atomicRMW, args[1].mValue);
										break;
									case BfIRIntrinsic_AtomicUMin:
										cmpVal = mIRBuilder->CreateICmpULE(atomicRMW, args[1].mValue);
										break;
									default: break;
									}
									result = mIRBuilder->CreateSelect(cmpVal, atomicRMW, args[1].mValue);
								}
								break;
							case BfIRIntrinsic_AtomicNAnd:
								result = mIRBuilder->CreateAnd(atomicRMW, args[1].mValue);
								result = mIRBuilder->CreateNot(result);
								break;
							case BfIRIntrinsic_AtomicOr:
								result = mIRBuilder->CreateOr(atomicRMW, args[1].mValue);
								break;
							case BfIRIntrinsic_AtomicSub:
								if (isFloat)
									result = mIRBuilder->CreateFSub(atomicRMW, args[1].mValue);
								else
									result = mIRBuilder->CreateSub(atomicRMW, args[1].mValue);
								break;
							case BfIRIntrinsic_AtomicXor:
								result = mIRBuilder->CreateXor(atomicRMW, args[1].mValue);
								break;
							case BfIRIntrinsic_AtomicXChg:
								result = args[1].mValue;
								break;
							default: break;
							}
						}
						SetResult(curId, result);
					}
					break;
				case BfIRIntrinsic_Cast:
					{
						BfIRTypedValue result;
						result.mTypeEx = intrinsicData->mReturnType;

						auto arg0Type = args[0].mValue->getType();
						if (arg0Type->isPointerTy())
						{
							if (intrinsicData->mReturnType->mLLVMType->isPointerTy())
							{
								result.mValue = mIRBuilder->CreateBitCast(args[0].mValue, intrinsicData->mReturnType->mLLVMType);
							}
							else
							{
								auto castedRes = mIRBuilder->CreateBitCast(args[0].mValue, intrinsicData->mReturnType->mLLVMType->getPointerTo());
								result.mValue = mIRBuilder->CreateAlignedLoad(intrinsicData->mReturnType->mLLVMType, castedRes, llvm::MaybeAlign(1));
							}
						}
						else if ((arg0Type->isVectorTy()) && (intrinsicData->mReturnType->mLLVMType->isVectorTy()))
						{
							result.mValue = mIRBuilder->CreateBitCast(args[0].mValue, intrinsicData->mReturnType->mLLVMType);
						}
						else
							FatalError("Invalid cast intrinsic values");

						SetResult(curId, result);
					}
					break;
				case BfIRIntrinsic_VAArg:
					{
						auto constInt = llvm::dyn_cast<llvm::ConstantInt>(args[2].mValue);
						auto argType = GetLLVMTypeById((int)constInt->getSExtValue());
						auto vaArgVal = mIRBuilder->CreateVAArg(args[0].mValue, argType);

						auto resultPtr = mIRBuilder->CreateBitCast(args[1].mValue, argType->getPointerTo());
						mIRBuilder->CreateStore(vaArgVal, resultPtr);
					}
					break;
				default:
					FatalError("Unhandled intrinsic");
				}
				break;
			}

			if (auto funcPtr = llvm::dyn_cast<llvm::Function>(func.mValue))
			{
// 				if (funcPtr->getName() == "__FAILCALL")
// 				{
// 					FatalError("__FAILCALL");
// 				}

				int intrinId = -1;
				if (mIntrinsicReverseMap.TryGetValue(funcPtr, &intrinId))
				{
					if (intrinId == BfIRIntrinsic_MemSet)
					{
						int align = 1;

						BF_ASSERT(args.size() == 5);
						auto alignConst = llvm::dyn_cast<llvm::ConstantInt>(args[3].mValue);
						if (alignConst != NULL)
							align = (int)alignConst->getSExtValue();
						bool isVolatile = false;
						auto volatileConst = llvm::dyn_cast<llvm::ConstantInt>(args[4].mValue);
						if ((volatileConst != NULL) && (volatileConst->getSExtValue() != 0))
							isVolatile = true;
						CreateMemSet(args[0].mValue, args[1].mValue, args[2].mValue, align, isVolatile);
						break;
					}
					else if ((intrinId == BfIRIntrinsic_MemCpy) || (intrinId == BfIRIntrinsic_MemMove))
					{
						int align = 1;

						BF_ASSERT(args.size() == 5);
						auto alignConst = llvm::dyn_cast<llvm::ConstantInt>(args[3].mValue);
						if (alignConst != NULL)
							align = (int)alignConst->getSExtValue();
						bool isVolatile = false;
						auto volatileConst = llvm::dyn_cast<llvm::ConstantInt>(args[4].mValue);
						if ((volatileConst != NULL) && (volatileConst->getSExtValue() != 0))
							isVolatile = true;
						if (intrinId == BfIRIntrinsic_MemCpy)
							mIRBuilder->CreateMemCpy(args[0].mValue, llvm::MaybeAlign(align), args[1].mValue, llvm::MaybeAlign(align), args[2].mValue, isVolatile);
						else
							mIRBuilder->CreateMemMove(args[0].mValue, llvm::MaybeAlign(align), args[1].mValue, llvm::MaybeAlign(align), args[2].mValue, isVolatile);
						break;
					}
				}
			}

            llvm::Value* val0 = NULL;
            llvm::Value* val1 = NULL;
            if (args.size() > 0)
            {
                val0 = args[0].mValue;
            }
            if (args.size() > 1)
            {
                val1 = args[1].mValue;
            }

			llvm::FunctionType* funcType = NULL;
			if (auto ptrType = llvm::dyn_cast<llvm::PointerType>(func.mValue->getType()))
				funcType = llvm::dyn_cast<llvm::FunctionType>(GetLLVMPointerElementType(func.mTypeEx));

			CmdParamVec<llvm::Value*> llvmArgs;
			for (auto& arg : args)
				llvmArgs.push_back(arg.mValue);

			auto funcTypeEx = GetTypeMember(func.mTypeEx, 0);
			auto returnTypeEx = GetTypeMember(funcTypeEx, 0);
			
			BfIRTypedValue result;
			result.mTypeEx = returnTypeEx;
			result.mValue = mIRBuilder->CreateCall(funcType, func.mValue, llvmArgs);
			SetResult(curId, result);

			mLastFuncCalled.mValue = result.mValue;
			mLastFuncCalled.mTypeEx = funcTypeEx;
		}
		break;
	case BfIRCmd_SetCallCallingConv:
		{
			CMD_PARAM(llvm::Value*, callInst);
			BfIRCallingConv callingConv = (BfIRCallingConv)mStream->Read();
			BF_ASSERT(llvm::isa<llvm::CallInst>(callInst));
			((llvm::CallInst*)callInst)->setCallingConv(GetLLVMCallingConv(callingConv, mTargetTriple));
		}
		break;
	case BfIRCmd_SetFuncCallingConv:
		{
			CMD_PARAM(llvm::Function*, func);
			BfIRCallingConv callingConv = (BfIRCallingConv)mStream->Read();
			((llvm::Function*)func)->setCallingConv(GetLLVMCallingConv(callingConv, mTargetTriple));
		}
		break;
	case BfIRCmd_SetTailCall:
		{
			CMD_PARAM(llvm::Value*, callInst);
			BF_ASSERT(llvm::isa<llvm::CallInst>(callInst));
			((llvm::CallInst*)callInst)->setTailCall();
		}
		break;
	case BfIRCmd_SetCallAttribute:
		{
			CMD_PARAM(llvm::Value*, callInst);
			CMD_PARAM(int, paramIdx);
			BfIRAttribute attribute = (BfIRAttribute)mStream->Read();
			BF_ASSERT(llvm::isa<llvm::CallInst>(callInst));
			llvm::Attribute::AttrKind attr = llvm::Attribute::None;
			if (attribute == BfIRAttribute_NoReturn)
				attr = llvm::Attribute::NoReturn;
			((llvm::CallInst*)callInst)->addParamAttr(paramIdx, attr);
		}
		break;
	case BfIRCmd_CreateRet:
		{
			CMD_PARAM(llvm::Value*, val);
			SetResult(curId, mIRBuilder->CreateRet(val));
		}
		break;
	case BfIRCmd_CreateRetVoid:
		{
			mIRBuilder->CreateRetVoid();
		}
		break;
	case BfIRCmd_CreateUnreachable:
		{
			mIRBuilder->CreateUnreachable();
		}
		break;
	case BfIRCmd_Call_AddAttribute:
		{
			CMD_PARAM(BfIRTypedValue, inst);
			CMD_PARAM(int, argIdx);

			BF_ASSERT(inst.mValue == mLastFuncCalled.mValue);

			BfIRAttribute attribute = (BfIRAttribute)mStream->Read();			
			auto attr = LLVMMapAttribute(attribute);
			auto callInst = llvm::dyn_cast<llvm::CallInst>(inst.mValue);
			BfIRTypeEx* funcType = mLastFuncCalled.mTypeEx;

			if (attr == llvm::Attribute::StructRet)
			{
				auto elemPtrType = GetTypeMember(funcType, argIdx);
				auto elemType = GetTypeMember(elemPtrType, 0);
				llvm::Attribute sret = llvm::Attribute::getWithStructRetType(*mLLVMContext, elemType->mLLVMType);
 				((llvm::CallInst*)callInst)->addParamAttr(argIdx - 1, sret);
			}
			else
			{
				if (argIdx == -1)
					((llvm::CallInst*)callInst)->addFnAttr(attr);
				else if (argIdx == 0)
					((llvm::CallInst*)callInst)->addRetAttr(attr);
				else
					((llvm::CallInst*)callInst)->addParamAttr(argIdx - 1, attr);
			}
		}
		break;
	case BfIRCmd_Call_AddAttribute1:
		{
			CMD_PARAM(BfIRTypedValue, inst);
			CMD_PARAM(int, argIdx);
			BfIRAttribute attribute = (BfIRAttribute)mStream->Read();
			CMD_PARAM(int, arg);

			BF_ASSERT(inst.mValue == mLastFuncCalled.mValue);

			auto callInst = llvm::dyn_cast<llvm::CallInst>(inst.mValue);
			if (callInst != NULL)
			{
				BfIRTypeEx* funcType = mLastFuncCalled.mTypeEx;

				if (attribute == BfIRAttribute_Dereferencable)
				{					
					((llvm::CallInst*)callInst)->addDereferenceableParamAttr(argIdx - 1, arg);
				}
				else if (attribute == BfIRAttribute_ByVal)
				{
					auto elemPtrType = GetTypeMember(funcType, argIdx);
					auto elemType = GetTypeMember(elemPtrType, 0);
					llvm::Attribute byValAttr = llvm::Attribute::getWithByValType(*mLLVMContext, elemType->mLLVMType);
					llvm::Attribute alignAttr = llvm::Attribute::getWithAlignment(*mLLVMContext, llvm::Align(arg));
					((llvm::CallInst*)callInst)->addParamAttr(argIdx - 1, byValAttr);
					((llvm::CallInst*)callInst)->addParamAttr(argIdx - 1, alignAttr);
				}
			}
		}
		break;
	case BfIRCmd_Func_AddAttribute:
		{
			BfIRTypedValue typedValue;
			ReadFunction(typedValue);
			CMD_PARAM(int, argIdx);			

			auto func = llvm::dyn_cast<llvm::Function>(typedValue.mValue);
			auto funcType = GetTypeMember(typedValue.mTypeEx, 0);

			BfIRAttribute attribute = (BfIRAttribute)mStream->Read();
			if (attribute == BFIRAttribute_DllImport)
			{
				func->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass);
			}
			else if (attribute == BFIRAttribute_DllExport)
			{
				func->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass);
				mHadDLLExport = true;
			}
			else if (attribute == BFIRAttribute_NoFramePointerElim)
			{
				func->addFnAttr("no-frame-pointer-elim", "true");
			}
			else if ((attribute == BFIRAttribute_Constructor) || (attribute == BFIRAttribute_Destructor))
			{
				CmdParamVec<llvm::Type*> members;
				members.push_back(llvm::Type::getInt32Ty(*mLLVMContext));
				members.push_back(func->getType());
				members.push_back(llvm::PointerType::get(*mLLVMContext, 0));
				llvm::StructType* structType = llvm::StructType::get(*mLLVMContext, members);

				CmdParamVec<llvm::Constant*> structVals;
				structVals.push_back(llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), 0x7FFFFF00));
				structVals.push_back(func);
				structVals.push_back(llvm::ConstantPointerNull::get(llvm::PointerType::get(*mLLVMContext, 0)));
				auto constStruct = llvm::ConstantStruct::get(structType, structVals);

				CmdParamVec<llvm::Constant*> structArrVals;
				structArrVals.push_back(constStruct);

				auto arrTy = llvm::ArrayType::get(structType, 1);
				auto constArr = llvm::ConstantArray::get(arrTy, structArrVals);

				auto globalVariable = new llvm::GlobalVariable(
					*mLLVMModule,
					arrTy,
					false,
					llvm::GlobalValue::AppendingLinkage,
					constArr,
					(attribute == BFIRAttribute_Constructor) ? "llvm.global_ctors" : "llvm.global_dtors",
					NULL, llvm::GlobalValue::NotThreadLocal);
			}
			else
			{
				auto attr = LLVMMapAttribute(attribute);
				if (attr == llvm::Attribute::StructRet)
				{					
					auto elemPtrType = GetTypeMember(funcType, argIdx);
					auto elemType = GetTypeMember(elemPtrType, 0);
					llvm::Attribute sret = llvm::Attribute::getWithStructRetType(*mLLVMContext, elemType->mLLVMType);
					func->addParamAttr(argIdx - 1, sret);
				}
				else if (attr != llvm::Attribute::None)
				{
					if (argIdx < 0)
					{
						switch (attr)
						{
						case llvm::Attribute::UWTable:
							{
								llvm::AttrBuilder attrBuilder(*mLLVMContext);
								attrBuilder.addUWTableAttr(llvm::UWTableKind::Default);
								func->addFnAttrs(attrBuilder);
							}
							break;
						default:
							func->addFnAttr(attr);
						}						
					}
					else if (argIdx == 0)
						func->addRetAttr(attr);
					else
						func->addParamAttr(argIdx - 1, attr);
				}
			}
		}
		break;
	case BfIRCmd_Func_AddAttribute1:
		{
			BfIRTypedValue typedValue;
			ReadFunction(typedValue);
			CMD_PARAM(int, argIdx);

			auto func = llvm::dyn_cast<llvm::Function>(typedValue.mValue);
			auto funcType = GetTypeMember(typedValue.mTypeEx, 0);
		
			BfIRAttribute attribute = (BfIRAttribute)mStream->Read();
			CMD_PARAM(int, arg);
			if (attribute == BfIRAttribute_Dereferencable)
			{
				((llvm::Function*)func)->addDereferenceableParamAttr(argIdx - 1, arg);
			}
			else if (attribute == BfIRAttribute_ByVal)
			{
				auto elemPtrType = GetTypeMember(funcType, argIdx);
				auto elemType = GetTypeMember(elemPtrType, 0);

				auto funcType = func->getFunctionType();
				llvm::Attribute byValAttr = llvm::Attribute::getWithByValType(*mLLVMContext, elemType->mLLVMType);
				llvm::Attribute alignAttr = llvm::Attribute::getWithAlignment(*mLLVMContext, llvm::Align(arg));
				func->addParamAttr(argIdx - 1, byValAttr);
				func->addParamAttr(argIdx - 1, alignAttr);
			}
		}
		break;
	case BfIRCmd_Func_SetParamName:
		{
			CMD_PARAM(llvm::Function*, func);
			CMD_PARAM(int, argIdx);
			CMD_PARAM(String, name);

			if (argIdx > func->arg_size())
			{
				Fail("BfIRCmd_Func_SetParamName argIdx error");
				break;
			}

			auto argItr = func->arg_begin();
			for (int i = 1; i < argIdx; i++)
				++argItr;
			argItr->setName(name.c_str());
		}
		break;
	case BfIRCmd_Func_DeleteBody:
		{
			CMD_PARAM(llvm::Function*, func);
			BF_ASSERT(llvm::isa<llvm::Function>(func));
			((llvm::Function*)func)->deleteBody();
		}
		break;
	case BfIRCmd_Func_SafeRename:
		{
			CMD_PARAM(llvm::Function*, func);
			func->setName(llvm::Twine((Beefy::String(func->getName().data()) + StrFormat("__RENAME%d", curId)).c_str()));
		}
		break;
	case BfIRCmd_Func_SafeRenameFrom:
		{
			CMD_PARAM(llvm::Function*, func);
			CMD_PARAM(String, prevName);
			if (String(func->getName().data()) == prevName)
				func->setName(llvm::Twine((Beefy::String(func->getName().data()) + StrFormat("__RENAME%d", curId)).c_str()));
		}
		break;
	case BfIRCmd_Func_SetLinkage:
		{
			CMD_PARAM(llvm::Function*, func);
			BfIRLinkageType linkageType = (BfIRLinkageType)mStream->Read();
			((llvm::Function*)func)->setLinkage(LLVMMapLinkageType(linkageType));
		}
		break;
	case BfIRCmd_SaveDebugLocation:
		{
			mSavedDebugLocs.push_back(mIRBuilder->getCurrentDebugLocation());
		}
		break;
	case BfIRCmd_RestoreDebugLocation:
		{
			mDebugLoc = mSavedDebugLocs[mSavedDebugLocs.size() - 1];
			mIRBuilder->SetCurrentDebugLocation(mDebugLoc);
			mSavedDebugLocs.pop_back();
		}
		break;
	case BfIRCmd_DupDebugLocation:
		break;
	case BfIRCmd_ClearDebugLocation:
		{
			mDebugLoc = llvm::DebugLoc();
			mIRBuilder->SetCurrentDebugLocation(llvm::DebugLoc());
		}
		break;
	case BfIRCmd_ClearDebugLocationInst:
		{
			CMD_PARAM_NOTRANS(llvm::Value*, instValue);
			BF_ASSERT(llvm::isa<llvm::Instruction>(instValue));

			if (llvm::dyn_cast<llvm::DbgDeclareInst>(instValue))
			{
				printf("BfIRCmd_ClearDebugLocationInst on DbgDeclareInst in %s\n", mModuleName.c_str());
			}
			else
			{
				((llvm::Instruction*)instValue)->setDebugLoc(llvm::DebugLoc());
			}
		}
		break;
	case BfIRCmd_ClearDebugLocationInstLast:
		{
			llvm::BasicBlock* bb = mIRBuilder->GetInsertBlock();
			if (bb != NULL)
			{
				if (!bb->empty())
				{
					auto& inst = bb->back();
					if (llvm::dyn_cast<llvm::DbgDeclareInst>(&inst))
					{
						printf("BfIRCmd_ClearDebugLocationInstLast on DbgDeclareInst\n");
					}
					else
					{
						inst.setDebugLoc(llvm::DebugLoc());
					}					
				}
			}
		}
		break;
	case BfIRCmd_UpdateDebugLocation:
		{
			CMD_PARAM_NOTRANS(llvm::Value*, instValue);
			BF_ASSERT(llvm::isa<llvm::Instruction>(instValue));
			if ((llvm::dyn_cast<llvm::DbgDeclareInst>(instValue)) && (!mIRBuilder->getCurrentDebugLocation()))
			{
				printf("BfIRCmd_UpdateDebugLocation NULL on DbgDeclareInst\n");
			}
			else
			{
				((llvm::Instruction*)instValue)->setDebugLoc(mIRBuilder->getCurrentDebugLocation());
			}
		}
		break;
	case BfIRCmd_SetCurrentDebugLocation:
		{
			CMD_PARAM(int, line);
			CMD_PARAM(int, column);
			CMD_PARAM(llvm::MDNode*, diScope);
			CMD_PARAM(llvm::MDNode*, diInlinedAt);
			if (line == 0)
				column = 0;
			mCurLine = line;
			mDebugLoc = llvm::DILocation::get(*mLLVMContext, line, column, diScope, diInlinedAt);

#ifdef _DEBUG
			llvm::DILocation* DL = mDebugLoc;
			if (DL != NULL)
			{
				llvm::Metadata* Parent = DL->getRawScope();
				llvm::DILocalScope* Scope = DL->getInlinedAtScope();
				llvm::DISubprogram* SP = Scope->getSubprogram();
				if (SP != NULL)
				{
					BF_ASSERT(SP->describes(mActiveFunction));
				}
			}
#endif
		}
		break;
	case BfIRCmd_Nop:
	case BfIRCmd_EnsureInstructionAt:
		AddNop();
		break;
	case BfIRCmd_StatementStart:
		// We only commit the debug loc for statement starts
		mIRBuilder->SetCurrentDebugLocation(mDebugLoc);
		mHasDebugLoc = true;
		break;
	case BfIRCmd_ObjectAccessCheck:
		{
			CMD_PARAM(llvm::Value*, val);
			CMD_PARAM(bool, useAsm);
			auto curLLVMFunc = mActiveFunction;
			auto irBuilder = mIRBuilder;

			if ((mTargetTriple.GetMachineType() != BfMachineType_x86) && (mTargetTriple.GetMachineType() != BfMachineType_x64))
				useAsm = false;

			if (!useAsm)
			{
				mLockedBlocks.Add(irBuilder->GetInsertBlock());

				// This is generates slower code than the inline asm in debug mode, but can optimize well in release
				auto int8Ty = llvm::Type::getInt8Ty(*mLLVMContext);
				auto int8Ptr = irBuilder->CreateBitCast(val, int8Ty->getPointerTo());
				auto int8Val = irBuilder->CreateLoad(int8Ty, int8Ptr);
				auto cmpResult = irBuilder->CreateICmpUGE(int8Val, llvm::ConstantInt::get(int8Ty, 0x80));

				auto failBB = llvm::BasicBlock::Create(*mLLVMContext, "access.fail");
				auto passBB = llvm::BasicBlock::Create(*mLLVMContext, "access.pass");

				irBuilder->CreateCondBr(cmpResult, failBB, passBB);

				curLLVMFunc->insert(curLLVMFunc->end(), failBB);
				irBuilder->SetInsertPoint(failBB);

				auto trapDecl = llvm::Intrinsic::getDeclaration(mLLVMModule, llvm::Intrinsic::trap);
				auto callInst = irBuilder->CreateCall(trapDecl);
				callInst->addFnAttr(llvm::Attribute::NoReturn);
				irBuilder->CreateBr(passBB);

				curLLVMFunc->insert(curLLVMFunc->end(), passBB);
				irBuilder->SetInsertPoint(passBB);

				SetResult(curId, passBB);
			}
			else
			{
				llvm::Type* voidPtrType = llvm::PointerType::get(*mLLVMContext, 0);
				if (mObjectCheckAsm == NULL)
				{
					std::vector<llvm::Type*> paramTypes;
					paramTypes.push_back(voidPtrType);
					auto funcType = llvm::FunctionType::get(llvm::Type::getVoidTy(*mLLVMContext), paramTypes, false);

					String asmStr =
						"cmpb $$128, ($0)\n"
						"jb 1f\n"
						"int $$3\n"
						"1:";

					mObjectCheckAsm = llvm::InlineAsm::get(funcType,
						asmStr.c_str(), "r,~{dirflag},~{fpsr},~{flags}", true,
						false, llvm::InlineAsm::AD_ATT);
				}

				llvm::SmallVector<llvm::Value*, 1> llvmArgs;
				llvmArgs.push_back(mIRBuilder->CreateBitCast(val, voidPtrType));

				llvm::CallInst* callInst = irBuilder->CreateCall(mObjectCheckAsm, llvmArgs);
				callInst->addFnAttr(llvm::Attribute::NoUnwind);

				SetResult(curId, mIRBuilder->GetInsertBlock());
			}
		}
		break;
	case BfIRCmd_DbgInit:
		{
			mDIBuilder = new llvm::DIBuilder(*mLLVMModule);
		}
		break;
	case BfIRCmd_DbgFinalize:
		{
			for (auto& typeEntryPair : mTypes)
			{
				auto& typeEntry = typeEntryPair.mValue;
				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);

			auto diFile = mDIBuilder->createFile(fileName.c_str(), directory.c_str());
			mDICompileUnit = mDIBuilder->createCompileUnit(lang, diFile, producer.c_str(), isOptimized, flags.c_str(), runtimeVer, "", linesOnly ? llvm::DICompileUnit::LineTablesOnly : llvm::DICompileUnit::FullDebug);
			SetResult(curId, mDICompileUnit);
		}
		break;
	case BfIRCmd_DbgCreateFile:
		{
			CMD_PARAM(String, fileName);
			CMD_PARAM(String, directory);
			CMD_PARAM(Val128, md5Hash);

			char hashStr[64];
			for (int i = 0; i < 16; i++)
				sprintf(&hashStr[i * 2], "%.2x", ((uint8*)&md5Hash)[i]);

			SetResult(curId, mDIBuilder->createFile(fileName.c_str(), directory.c_str(),
				llvm::DIFile::ChecksumInfo<llvm::StringRef>(llvm::DIFile::CSK_MD5, hashStr)));
		}
		break;
	case BfIRCmd_ConstValueI64:
		{
			CMD_PARAM(int64, val);
			SetResult(curId, mDIBuilder->createConstantValueExpression((uint64)val));
		}
		break;
	case BfIRCmd_DbgGetCurrentLocation:
		{
			auto debugLoc = mIRBuilder->getCurrentDebugLocation();
			if (!debugLoc)
				debugLoc = mDebugLoc;
			SetResult(curId, debugLoc);
		}
		break;
	case BfIRCmd_DbgSetType:
		{
			CMD_PARAM(int, typeId);
			CMD_PARAM(llvm::MDNode*, type);
			auto& typeEntry = GetTypeEntry(typeId);
			typeEntry.mDIType = (llvm::DIType*)type;
			if (typeEntry.mInstDIType == NULL)
				typeEntry.mInstDIType = (llvm::DIType*)type;
		}
		break;
	case BfIRCmd_DbgSetInstType:
		{
			CMD_PARAM(int, typeId);
			CMD_PARAM(llvm::MDNode*, type);
			GetTypeEntry(typeId).mInstDIType = (llvm::DIType*)type;
		}
		break;
	case BfIRCmd_DbgGetType:
		{
			CMD_PARAM(int, typeId);
			SetResult(curId, GetTypeEntry(typeId).mDIType);
		}
		break;
	case BfIRCmd_DbgGetTypeInst:
		{
			CMD_PARAM(int, typeId);
			SetResult(curId, GetTypeEntry(typeId).mInstDIType);
		}
		break;
	case BfIRCmd_DbgTrackDITypes:
		{
            CMD_PARAM(int, typeId);
			auto& typeEntry = GetTypeEntry(typeId);

            if (typeEntry.mDIType != NULL)
				llvm::MetadataTracking::track(*(llvm::Metadata**)&typeEntry.mDIType);
			if (typeEntry.mInstDIType != NULL)
                llvm::MetadataTracking::track(*(llvm::Metadata**)&typeEntry.mInstDIType);
		}
		break;
	case BfIRCmd_DbgCreateNamespace:
		{
			CMD_PARAM(llvm::MDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, lineNum);
			BF_ASSERT(file != NULL);
			SetResult(curId, mDIBuilder->createNameSpace((llvm::DIScope*)scope, name.c_str(), true));
		}
		break;
	case BfIRCmd_DbgCreateImportedModule:
		{
			CMD_PARAM(llvm::MDNode*, context);
			CMD_PARAM(llvm::MDNode*, namespaceNode);
			CMD_PARAM(int, lineNum);
			//SetResult(curId, mDIBuilder->createImportedModule((llvm::DIScope*)context, (llvm::DINamespace*)namespaceNode, lineNum));
		}
		break;
	case BfIRCmd_DbgCreateBasicType:
		{
			CMD_PARAM(String, name);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(int, encoding);
			SetResult(curId, mDIBuilder->createBasicType(name.c_str(), sizeInBits, encoding));
		}
		break;
	case BfIRCmd_DbgCreateStructType:
		{
			CMD_PARAM(llvm::MDNode*, context);
			CMD_PARAM(String, name);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(int, flags);
			CMD_PARAM(llvm::MDNode*, derivedFrom);
			CMD_PARAM(CmdParamVec<llvm::Metadata*>, members);
			auto diMembersArray = mDIBuilder->getOrCreateArray(members);
			BF_ASSERT(file != NULL);
			llvm::DINode::DIFlags diFlags = (llvm::DINode::DIFlags)flags;
            auto mdStruct = mDIBuilder->createStructType((llvm::DIScope*)context, name.c_str(), (llvm::DIFile*)file, lineNum, sizeInBits, (uint32)alignInBits, diFlags, (llvm::DIType*)derivedFrom, diMembersArray);
            SetResult(curId, mdStruct);

            //OutputDebugStrF("BfIRCmd_DbgCreateStructType %p\n", mdStruct);
		}
		break;
	case BfIRCmd_DbgCreateEnumerationType:
		{
			CMD_PARAM(llvm::MDNode*, context);
			CMD_PARAM(String, name);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(CmdParamVec<llvm::Metadata*>, members);
			CMD_PARAM(llvm::MDNode*, underlyingType);
			auto diMembersArray = mDIBuilder->getOrCreateArray(members);

			/*static int typeIdx = 0;
			if (name == "TypeCode")
				name += StrFormat("_%d", typeIdx);
			typeIdx++;*/

			BF_ASSERT(file != NULL);
			auto enumType = mDIBuilder->createEnumerationType((llvm::DIScope*)context, name.c_str(), (llvm::DIFile*)file, lineNum, sizeInBits, (uint32)alignInBits, diMembersArray, (llvm::DIType*)underlyingType);
			SetResult(curId, enumType);

            //OutputDebugStrF("BfIRCmd_DbgCreateEnumerationType %p\n", enumType);
		}
		break;
	case BfIRCmd_DbgCreatePointerType:
		{
			CMD_PARAM(llvm::MDNode*, diType);
			SetResult(curId, mDIBuilder->createPointerType((llvm::DIType*)diType, mPtrSize*8, (uint32)mPtrSize * 8));
		}
		break;
	case BfIRCmd_DbgCreateReferenceType:
		{
			CMD_PARAM(llvm::MDNode*, diType);
			SetResult(curId, mDIBuilder->createReferenceType(llvm::dwarf::DW_TAG_reference_type, (llvm::DIType*)diType));
		}
		break;
	case BfIRCmd_DbgCreateConstType:
		{
			CMD_PARAM(llvm::MDNode*, diType);
			SetResult(curId, mDIBuilder->createQualifiedType(llvm::dwarf::DW_TAG_const_type, (llvm::DIType*)diType));
		}
		break;
	case BfIRCmd_DbgCreateArtificialType:
		{
			CMD_PARAM(llvm::MDNode*, diType);
			SetResult(curId, mDIBuilder->createArtificialType((llvm::DIType*)diType));
		}
		break;
	case BfIRCmd_DbgCreateArrayType:
		{
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(llvm::MDNode*, elementType);
			CMD_PARAM(int64, numElements);

			llvm::SmallVector<llvm::Metadata*, 1> diSizeVec;
			diSizeVec.push_back(mDIBuilder->getOrCreateSubrange(0, numElements));
			auto diSizeArray = mDIBuilder->getOrCreateArray(diSizeVec);
			SetResult(curId, mDIBuilder->createArrayType(sizeInBits, (uint32)alignInBits, (llvm::DIType*)elementType, diSizeArray));
		}
		break;
	case BfIRCmd_DbgCreateReplaceableCompositeType:
		{
			CMD_PARAM(int, tag);
			CMD_PARAM(String, name);
			CMD_PARAM(llvm::MDNode*, scope);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, line);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(int, flags);
			BF_ASSERT(file != NULL);
			llvm::DINode::DIFlags diFlags = (llvm::DINode::DIFlags)flags;
			SetResult(curId, mDIBuilder->createReplaceableCompositeType(tag, name.c_str(), (llvm::DIScope*)scope, (llvm::DIFile*)file, line, 0, sizeInBits, (uint32)alignInBits, diFlags));
		}
		break;
	case BfIRCmd_DbgCreateForwardDecl:
		{
			CMD_PARAM(int, tag);
			CMD_PARAM(String, name);
			CMD_PARAM(llvm::MDNode*, scope);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, line);
			BF_ASSERT(file != NULL);

			auto diType = mDIBuilder->createForwardDecl(tag, name.c_str(), (llvm::DIScope*)scope, (llvm::DIFile*)file, line);
			SetResult(curId, diType);
		}
		break;
	case BfIRCmd_DbgCreateSizedForwardDecl:
		{
			CMD_PARAM(int, tag);
			CMD_PARAM(String, name);
			CMD_PARAM(llvm::MDNode*, scope);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, line);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			BF_ASSERT(file != NULL);
			SetResult(curId, mDIBuilder->createForwardDecl(tag, name.c_str(), (llvm::DIScope*)scope, (llvm::DIFile*)file, line, 0, sizeInBits, (uint32)alignInBits));
		}
		break;
	case BeIRCmd_DbgSetTypeSize:
		{
			CMD_PARAM(llvm::MDNode*, mdType);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);

			class DIMutType : public llvm::DIType
			{
			public:
				void Resize(int64 newSize, int32 newAlign)
				{
					init(getLine(), newSize, newAlign, getOffsetInBits(), getFlags());
				}
			};

			auto diType = (DIMutType*)mdType;
			diType->Resize(sizeInBits, (int32)alignInBits);
		}
		break;
	case BfIRCmd_DbgReplaceAllUses:
		{
			CMD_PARAM(llvm::MDNode*, diPrevNode);
			CMD_PARAM(llvm::MDNode*, diNewNode);
			diPrevNode->replaceAllUsesWith(diNewNode);
		}
		break;
	case BfIRCmd_DbgDeleteTemporary:
		{
			CMD_PARAM(llvm::MDNode*, diNode);
			llvm::MDNode::deleteTemporary(diNode);
		}
		break;
	case BfIRCmd_DbgMakePermanent:
		{
			CMD_PARAM(llvm::MDNode*, diNode);
			CMD_PARAM(llvm::MDNode*, diBaseType);
			CMD_PARAM(CmdParamVec<llvm::Metadata*>, members);

			llvm::MDNode* newNode = diNode;
			if (auto diComposite = llvm::dyn_cast<llvm::DICompositeType>(diNode))
			{
				//diComposite->getBaseType()

				if (diBaseType != NULL)
				{
					// It's unfortunate we have to hard-code the '3' here
					diComposite->replaceOperandWith(3, diBaseType);
					BF_ASSERT(diComposite->getBaseType() == diBaseType);
				}

				if (members.size() != 0)
				{
					llvm::DINodeArray elements = mDIBuilder->getOrCreateArray(members);
					mDIBuilder->replaceArrays(diComposite, elements);
				}
				newNode = llvm::MDNode::replaceWithPermanent(llvm::TempDICompositeType(diComposite));
			}
			/*else if (auto diEnumerator = llvm::dyn_cast<llvm::DIEnumerator>(diNode))
			{
				if (members.size() != 0)
				{
					llvm::DINodeArray elements = mDIBuilder->getOrCreateArray(diNode);
					mDIBuilder->set(diComposite, elements);
				}
				newNode = llvm::MDNode::replaceWithPermanent(llvm::TempDIEnumerator(diEnumerator));
			}*/

			SetResult(curId, newNode);
			break;
		}
	case BfIRCmd_CreateEnumerator:
		{
			CMD_PARAM(String, name);
			CMD_PARAM(int64, val);
			SetResult(curId, mDIBuilder->createEnumerator(name.c_str(), val));
		}
		break;
	case BfIRCmd_DbgCreateMemberType:
		{
			CMD_PARAM(llvm::MDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, lineNumber);
			CMD_PARAM(int64, sizeInBits);
			CMD_PARAM(int64, alignInBits);
			CMD_PARAM(int64, offsetInBits);
			CMD_PARAM(int, flags);
			CMD_PARAM(llvm::MDNode*, type);
			BF_ASSERT(file != NULL);
			llvm::DINode::DIFlags diFlags = (llvm::DINode::DIFlags)flags;

            /*Beefy::debug_ostream os;
            os << "BfIRCmd_DbgCreateMemberType " << name.c_str() << "\n";
            scope->print(os);
            os << "\n";
            type->print(os);
            os << "\n";
            os.flush();*/

			const char* namePtr = name.c_str();
			if (name.IsEmpty())
				namePtr = NULL;

            auto member = mDIBuilder->createMemberType((llvm::DIScope*)scope, namePtr, (llvm::DIFile*)file, lineNumber, sizeInBits, (uint32)alignInBits, offsetInBits, diFlags, (llvm::DIType*)type);
            SetResult(curId, member);
            //OutputDebugStrF("BfIRCmd_DbgCreateMemberType = %p\n", member);
		}
		break;
	case BfIRCmd_DbgStaticCreateMemberType:
		{
			CMD_PARAM(llvm::MDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, lineNumber);
			CMD_PARAM(llvm::MDNode*, type);
			CMD_PARAM(int, flags);
			CMD_PARAM(llvm::Constant*, val);
			BF_ASSERT(file != NULL);
			llvm::DINode::DIFlags diFlags = (llvm::DINode::DIFlags)flags;

            /*Beefy::debug_ostream os;
            os << "BfIRCmd_DbgStaticCreateMemberType " << name.c_str() << "\n";
            scope->print(os);
            os << "\n";
            type->print(os);
            os << "\n";
            os.flush();*/

            auto member = mDIBuilder->createStaticMemberType((llvm::DIScope*)scope, name.c_str(), (llvm::DIFile*)file, lineNumber, (llvm::DIType*)type, diFlags, val, llvm::dwarf::DW_TAG_member);
            SetResult(curId, member);
            //OutputDebugStrF("BfIRCmd_DbgStaticCreateMemberType = %p\n", member);
		}
		break;
	case BfIRCmd_DbgCreateInheritance:
		{
			CMD_PARAM(llvm::MDNode*, type);
			CMD_PARAM(llvm::MDNode*, baseType);
			CMD_PARAM(int64, baseOffset);
			CMD_PARAM(int, flags);
			llvm::DINode::DIFlags diFlags = (llvm::DINode::DIFlags)flags;

            auto member = mDIBuilder->createInheritance((llvm::DIType*)type, (llvm::DIType*)baseType, baseOffset, 0, diFlags);
            SetResult(curId, member);
            //OutputDebugStrF("BfIRCmd_DbgCreateInheritance = %p\n", member);
		}
		break;
	case BfIRCmd_DbgCreateMethod:
		{
			CMD_PARAM(llvm::MDNode*, context);
			CMD_PARAM(String, name);
			CMD_PARAM(String, linkageName);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(llvm::MDNode*, type);
			CMD_PARAM(bool, isLocalToUnit);
			CMD_PARAM(bool, isDefinition);
			CMD_PARAM(int, vk);
			CMD_PARAM(int, vIndex);
			CMD_PARAM(llvm::MDNode*, vTableHolder);
			CMD_PARAM(int, flags);
			CMD_PARAM(bool, isOptimized);
			CMD_PARAM(llvm::Value*, fn);
			CMD_PARAM(CmdParamVec<llvm::MDNode*>, genericArgs);
			CMD_PARAM(CmdParamVec<llvm::Constant*>, genericConstValueArgs);
			BF_ASSERT(file != NULL);

			llvm::DITemplateParameterArray templateParamArr = NULL;
			llvm::DINodeArray templateParamNodes;
			if (genericArgs.size() != 0)
			{
				llvm::SmallVector<llvm::Metadata*, 16> templateParams;
				for (int i = 0; i < (int)genericArgs.size(); i++)
				{
					auto genericArg = (llvm::DIType*)genericArgs[i];
					String name = StrFormat("T%d", i);

					llvm::Constant* constant = NULL;
					if (i < genericConstValueArgs.size())
						constant = genericConstValueArgs[i];

					if (constant != NULL)
						templateParams.push_back(mDIBuilder->createTemplateValueParameter(mDICompileUnit, name.c_str(), genericArg, false, constant));
					else
						templateParams.push_back(mDIBuilder->createTemplateTypeParameter(mDICompileUnit, name.c_str(), genericArg, false));
				}
				templateParamNodes = mDIBuilder->getOrCreateArray(templateParams);
				templateParamArr = templateParamNodes.get();
			}

			llvm::DINode::DIFlags diFlags = (llvm::DINode::DIFlags)flags;
			llvm::DISubprogram::DISPFlags dispFlags = llvm::DISubprogram::DISPFlags::SPFlagZero;
			if (isLocalToUnit)
				dispFlags = (llvm::DISubprogram::DISPFlags)(dispFlags | llvm::DISubprogram::DISPFlags::SPFlagLocalToUnit);
			if (isDefinition)
				dispFlags = (llvm::DISubprogram::DISPFlags)(dispFlags | llvm::DISubprogram::DISPFlags::SPFlagDefinition);
			if (isOptimized)
				dispFlags = (llvm::DISubprogram::DISPFlags)(dispFlags | llvm::DISubprogram::DISPFlags::SPFlagOptimized);
			if (vk != 0)
				dispFlags = (llvm::DISubprogram::DISPFlags)(dispFlags | llvm::DISubprogram::DISPFlags::SPFlagVirtual);

			auto diSubProgram = mDIBuilder->createMethod((llvm::DIScope*)context, name.c_str(), linkageName.c_str(), (llvm::DIFile*)file, lineNum,
				(llvm::DISubroutineType*)type, vIndex, 0, (llvm::DIType*)vTableHolder, diFlags, dispFlags, templateParamArr);
			if (fn != NULL)
				((llvm::Function*)fn)->setSubprogram(diSubProgram);

            SetResult(curId, diSubProgram);

            //OutputDebugStrF("BfIRCmd_DbgCreateMethod = %p\n", diSubProgram);
		}
		break;
	case BfIRCmd_DbgCreateFunction:
		{
			CMD_PARAM(llvm::MDNode*, context);
			CMD_PARAM(String, name);
			CMD_PARAM(String, linkageName);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(llvm::MDNode*, type);
			CMD_PARAM(bool, isLocalToUnit);
			CMD_PARAM(bool, isDefinition);
			CMD_PARAM(int, scopeLine);
			CMD_PARAM(int, flags);
			CMD_PARAM(bool, isOptimized);
			CMD_PARAM(llvm::Value*, fn);
			BF_ASSERT(file != NULL);
			llvm::DINode::DIFlags diFlags = (llvm::DINode::DIFlags)flags;

			llvm::DISubprogram::DISPFlags dispFlags = llvm::DISubprogram::DISPFlags::SPFlagZero;
			if (isLocalToUnit)
				dispFlags = (llvm::DISubprogram::DISPFlags)(dispFlags | llvm::DISubprogram::DISPFlags::SPFlagLocalToUnit);
			if (isDefinition)
				dispFlags = (llvm::DISubprogram::DISPFlags)(dispFlags | llvm::DISubprogram::DISPFlags::SPFlagDefinition);
			if (isOptimized)
				dispFlags = (llvm::DISubprogram::DISPFlags)(dispFlags | llvm::DISubprogram::DISPFlags::SPFlagOptimized);

			auto diSubProgram = mDIBuilder->createFunction((llvm::DIScope*)context, name.c_str(), linkageName.c_str(), (llvm::DIFile*)file, lineNum,
				(llvm::DISubroutineType*)type, scopeLine, diFlags, dispFlags);
			if (fn != NULL)
				((llvm::Function*)fn)->setSubprogram(diSubProgram);
			SetResult(curId, diSubProgram);

            //OutputDebugStrF("BfIRCmd_DbgCreateFunction = %p\n", diSubProgram);
		}
		break;
	case BfIRCmd_DbgCreateParameterVariable:
		{
			CMD_PARAM(llvm::MDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(int, argNo);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(llvm::MDNode*, type);
			CMD_PARAM(bool, alwaysPreserve);
			CMD_PARAM(int, flags);
			BF_ASSERT(file != NULL);
			llvm::DINode::DIFlags diFlags = (llvm::DINode::DIFlags)flags;
			SetResult(curId, mDIBuilder->createParameterVariable((llvm::DIScope*)scope, name.c_str(), argNo, (llvm::DIFile*)file, lineNum, (llvm::DIType*)type,
				alwaysPreserve, diFlags));
		}
		break;
	case BfIRCmd_DbgCreateSubroutineType:
		{
			CMD_PARAM(CmdParamVec<llvm::Metadata*>, elements);
			auto diArray = mDIBuilder->getOrCreateTypeArray(elements);
			SetResult(curId, mDIBuilder->createSubroutineType(diArray));
		}
		break;
	case BfIRCmd_DbgCreateAutoVariable:
		{
			CMD_PARAM(llvm::MDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, lineNo);
			CMD_PARAM(llvm::MDNode*, type);
			CMD_PARAM(int, initType);
			BF_ASSERT(file != NULL);
			llvm::DINode::DIFlags diFlags = (llvm::DINode::DIFlags)0;

			auto loc = mIRBuilder->getCurrentDebugLocation();
			auto dbgLoc = loc.getAsMDNode();

			SetResult(curId, mDIBuilder->createAutoVariable((llvm::DIScope*)scope, name.c_str(), (llvm::DIFile*)file, lineNo, (llvm::DIType*)type, false, diFlags));
		}
		break;
	case BfIRCmd_DbgInsertValueIntrinsic:
		{
			CMD_PARAM(llvm::Value*, val);
			CMD_PARAM(llvm::MDNode*, varInfo);

			auto diVariable = (llvm::DILocalVariable*)varInfo;

			if (val == NULL)
			{
				val = llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), 0);
			}
			else if (mIsCodeView)
			{
				if (auto constant = llvm::dyn_cast<llvm::Constant>(val))
				{
					int64 writeVal = 0;
					if (auto constantInt = llvm::dyn_cast<llvm::ConstantInt>(val))
					{
						writeVal = constantInt->getSExtValue();
					}

					auto nameRef = diVariable->getName();

					if (writeVal < 0)
						diVariable->replaceOperandWith(1, llvm::MDString::get(*mLLVMContext, (String(nameRef.data()) + StrFormat("$_%llu", -writeVal)).c_str()));
					else
						diVariable->replaceOperandWith(1, llvm::MDString::get(*mLLVMContext, (String(nameRef.data()) + StrFormat("$%llu", writeVal)).c_str()));
				}
			}

			mDIBuilder->insertDbgValueIntrinsic(val, diVariable, mDIBuilder->createExpression(),
				mIRBuilder->getCurrentDebugLocation(), (llvm::BasicBlock*)mIRBuilder->GetInsertBlock());
		}
		break;
	case BfIRCmd_DbgInsertDeclare:
		{
			CMD_PARAM(llvm::Value*, val);
			CMD_PARAM(llvm::MDNode*, varInfo);
			CMD_PARAM(llvm::Value*, insertBefore);

			llvm::Instruction* insertBeforeInst = NULL;
			if (insertBefore != NULL)
				insertBeforeInst = llvm::dyn_cast<llvm::Instruction>(insertBefore);

			// Protect against lack of debug location
			if (mIRBuilder->getCurrentDebugLocation())
			{
				if (insertBeforeInst != NULL)
				{
					SetResult(curId, mDIBuilder->insertDeclare(val, (llvm::DILocalVariable*)varInfo, mDIBuilder->createExpression(),
						mIRBuilder->getCurrentDebugLocation(), insertBeforeInst));
				}
				else
				{
					SetResult(curId, mDIBuilder->insertDeclare(val, (llvm::DILocalVariable*)varInfo, mDIBuilder->createExpression(),
						mIRBuilder->getCurrentDebugLocation(), mIRBuilder->GetInsertBlock()));
				}
			}
		}
		break;
	case BfIRCmd_DbgLifetimeEnd:
		{
			CMD_PARAM(llvm::MDNode*, varInfo);
		}
		break;
	case BfIRCmd_DbgCreateGlobalVariable:
		{
			CMD_PARAM(llvm::MDNode*, context);
			CMD_PARAM(String, name);
			CMD_PARAM(String, linkageName);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(llvm::MDNode*, type);
			CMD_PARAM(bool, isLocalToUnit);
			CMD_PARAM(llvm::Constant*, val);
			CMD_PARAM(llvm::MDNode*, decl);
			//BF_ASSERT(file != NULL);
			llvm::DIExpression* diExpr = NULL;
			auto gve = mDIBuilder->createGlobalVariableExpression((llvm::DIScope*)context, name.c_str(), linkageName.c_str(), (llvm::DIFile*)file, lineNum, (llvm::DIType*)type,
				isLocalToUnit, true, diExpr, decl);

			if (val != NULL)
			{
				if (auto globalVar = llvm::dyn_cast<llvm::GlobalVariable>(val))
				{
					globalVar->addDebugInfo(gve);
				}
			}

			SetResult(curId, diExpr);
		}
		break;
	case BfIRCmd_DbgCreateLexicalBlock:
		{
			CMD_PARAM(llvm::MDNode*, scope);
			CMD_PARAM(llvm::MDNode*, file);
			CMD_PARAM(int, lineNum);
			CMD_PARAM(int, col);
			if (lineNum == 0)
				col = 0;
			BF_ASSERT(file != NULL);
			SetResult(curId, mDIBuilder->createLexicalBlock((llvm::DIScope*)scope, (llvm::DIFile*)file, (unsigned)lineNum, (unsigned)col));
		}
		break;
	case BfIRCmd_DbgCreateAnnotation:
		{
			CMD_PARAM(llvm::MDNode*, scope);
			CMD_PARAM(String, name);
			CMD_PARAM(llvm::Value*, value);

			if (auto dbgFunc = llvm::dyn_cast<llvm::DISubprogram>(scope))
			{
				auto beType = value->getType();

				auto diType = mDIBuilder->createBasicType("int32", 4 * 8, llvm::dwarf::DW_ATE_signed);

				llvm::DINode::DIFlags diFlags = (llvm::DINode::DIFlags)0;

				auto loc = mIRBuilder->getCurrentDebugLocation();
				auto dbgLoc = loc.getAsMDNode();
				auto diScope = (llvm::DIScope*)scope;

				String dbgName = "#" + name;

				int64 writeVal = 0;
				if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(value))
				{
					writeVal = constant->getSExtValue();
				}

				if (writeVal < 0)
					dbgName += StrFormat("$_%llu", -writeVal);
				else
					dbgName += StrFormat("$%llu", writeVal);

				auto dbgVar = mDIBuilder->createAutoVariable((llvm::DIScope*)scope, dbgName.c_str(), (llvm::DIFile*)diScope->getFile(), 0, diType, false, diFlags);

				mDIBuilder->insertDbgValueIntrinsic(value, dbgVar, mDIBuilder->createExpression(),
					mIRBuilder->getCurrentDebugLocation(), (llvm::BasicBlock*)mIRBuilder->GetInsertBlock());
			}
		}
		break;
	default:
		BF_FATAL("Unhandled");
		break;
	}
}

void BfIRCodeGen::SetCodeGenOptions(BfCodeGenOptions codeGenOptions)
{
	mCodeGenOptions = codeGenOptions;
}

void BfIRCodeGen::SetConfigConst(int idx, int value)
{
	auto constVal = llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), value);
	BF_ASSERT(idx == (int)mConfigConsts32.size());
	mConfigConsts32.Add(constVal);

	constVal = llvm::ConstantInt::get(llvm::Type::getInt64Ty(*mLLVMContext), value);
	BF_ASSERT(idx == (int)mConfigConsts64.size());
	mConfigConsts64.Add(constVal);
}

void BfIRCodeGen::SetActiveFunctionSimdType(BfIRSimdType type)
{
	BfIRSimdType currentType;
	bool contains = mFunctionsUsingSimd.TryGetValue(mActiveFunction, &currentType);

	if (!contains || type > currentType)
		mFunctionsUsingSimd[mActiveFunction] = type;
}

String BfIRCodeGen::GetSimdTypeString(BfIRSimdType type)
{
	switch (type)
	{
	case BfIRSimdType_SSE:
		return "+sse,+mmx";
	case BfIRSimdType_SSE2:
		return "+sse2,+sse,+mmx";
	case BfIRSimdType_AVX:
		return "+avx,+sse4.2,+sse4.1,+sse3,+sse2,+sse,+mmx";
	case BfIRSimdType_AVX2:
		return "+avx2,+avx,+sse4.2,+sse4.1,+sse3,+sse2,+sse,+mmx";
	case BfIRSimdType_AVX512:
		return "+avx512f,+avx2,+avx,+sse4.2,+sse4.1,+sse3,+sse2,+sse,+mmx";
	default:
		return "";
	}
}

BfIRSimdType BfIRCodeGen::GetSimdTypeFromFunction(llvm::Function* function)
{
	if (function->hasFnAttribute("target-features"))
	{
		auto str = function->getFnAttribute("target-features").getValueAsString();

		if (str.contains("+avx512f"))
			return BfIRSimdType_AVX512;
		if (str.contains("+avx2"))
			return BfIRSimdType_AVX2;
		if (str.contains("+avx"))
			return BfIRSimdType_AVX;
		if (str.contains("+sse2"))
			return BfIRSimdType_SSE2;
		if (str.contains("+sse"))
			return BfIRSimdType_SSE;
	}

	return BfIRSimdType_None;
}

BfIRTypedValue BfIRCodeGen::GetTypedValue(int id)
{
	auto& result = mResults[id];
	if (result.mKind == BfIRCodeGenEntryKind_TypedValue)
		return result.mTypedValue;
	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMValue);
	
	BfIRTypedValue typedValue;
	typedValue.mTypeEx = NULL;
	typedValue.mValue = result.mLLVMValue;
	return typedValue;
}

llvm::Value* BfIRCodeGen::GetLLVMValue(int id)
{
	auto& result = mResults[id];
	if (result.mKind == BfIRCodeGenEntryKind_TypedValue)
		return result.mTypedValue.mValue;
	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMValue);
	return result.mLLVMValue;
}

llvm::Type* BfIRCodeGen::GetLLVMType(int id)
{
	auto& result = mResults[id];
	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMType);
	return result.mLLVMType;
}

llvm::BasicBlock * BfIRCodeGen::GetLLVMBlock(int id)
{
	auto& result = mResults[id];
	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMBasicBlock);
	return result.mLLVMBlock;
}

llvm::MDNode* BfIRCodeGen::GetLLVMMetadata(int id)
{
	auto& result = mResults[id];
	BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMMetadata);
	return result.mLLVMMetadata;
}

llvm::Type* BfIRCodeGen::GetLLVMTypeById(int id)
{
	return GetTypeEntry(id).mType->mLLVMType;
}

// LLVM/Clang 18.1.4
static void addSanitizers(const llvm::Triple& TargetTriple,	BfCodeGenOptions& CodeGenOpts, llvm::PassBuilder& PB) 
{
#if 0	
	auto SanitizersCallback = [&](llvm::ModulePassManager& MPM, llvm::OptimizationLevel Level) {
			if (CodeGenOpts.hasSanitizeCoverage()) 
			{
				auto SancovOpts = getSancovOptsFromCGOpts(CodeGenOpts);
				MPM.addPass(SanitizerCoveragePass(
					SancovOpts, CodeGenOpts.SanitizeCoverageAllowlistFiles,
					CodeGenOpts.SanitizeCoverageIgnorelistFiles));
			}

			if (CodeGenOpts.hasSanitizeBinaryMetadata()) {
				MPM.addPass(SanitizerBinaryMetadataPass(
					getSanitizerBinaryMetadataOptions(CodeGenOpts),
					CodeGenOpts.SanitizeMetadataIgnorelistFiles));
			}

			auto MSanPass = [&](SanitizerMask Mask, bool CompileKernel) {
				if (LangOpts.Sanitize.has(Mask)) {
					int TrackOrigins = CodeGenOpts.SanitizeMemoryTrackOrigins;
					bool Recover = CodeGenOpts.SanitizeRecover.has(Mask);

					MemorySanitizerOptions options(TrackOrigins, Recover, CompileKernel,
						CodeGenOpts.SanitizeMemoryParamRetval);
					MPM.addPass(MemorySanitizerPass(options));
					if (Level != OptimizationLevel::O0) {
						// MemorySanitizer inserts complex instrumentation that mostly follows
						// the logic of the original code, but operates on "shadow" values. It
						// can benefit from re-running some general purpose optimization
						// passes.
						MPM.addPass(RequireAnalysisPass<GlobalsAA, llvm::Module>());
						FunctionPassManager FPM;
						FPM.addPass(EarlyCSEPass(true /* Enable mem-ssa. */));
						FPM.addPass(InstCombinePass());
						FPM.addPass(JumpThreadingPass());
						FPM.addPass(GVNPass());
						FPM.addPass(InstCombinePass());
						MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
					}
				}
				};
			MSanPass(SanitizerKind::Memory, false);
			MSanPass(SanitizerKind::KernelMemory, true);

			if (LangOpts.Sanitize.has(SanitizerKind::Thread)) {
				MPM.addPass(ModuleThreadSanitizerPass());
				MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
			}

			auto ASanPass = [&](SanitizerMask Mask, bool CompileKernel) {
				if (LangOpts.Sanitize.has(Mask)) {
					bool UseGlobalGC = asanUseGlobalsGC(TargetTriple, CodeGenOpts);
					bool UseOdrIndicator = CodeGenOpts.SanitizeAddressUseOdrIndicator;
					llvm::AsanDtorKind DestructorKind =
						CodeGenOpts.getSanitizeAddressDtor();
					AddressSanitizerOptions Opts;
					Opts.CompileKernel = CompileKernel;
					Opts.Recover = CodeGenOpts.SanitizeRecover.has(Mask);
					Opts.UseAfterScope = CodeGenOpts.SanitizeAddressUseAfterScope;
					Opts.UseAfterReturn = CodeGenOpts.getSanitizeAddressUseAfterReturn();
					MPM.addPass(AddressSanitizerPass(Opts, UseGlobalGC, UseOdrIndicator,
						DestructorKind));
				}
				};
			ASanPass(SanitizerKind::Address, false);
			ASanPass(SanitizerKind::KernelAddress, true);

			auto HWASanPass = [&](SanitizerMask Mask, bool CompileKernel) {
				if (LangOpts.Sanitize.has(Mask)) {
					bool Recover = CodeGenOpts.SanitizeRecover.has(Mask);
					MPM.addPass(HWAddressSanitizerPass(
						{ CompileKernel, Recover,
						/*DisableOptimization=*/CodeGenOpts.OptimizationLevel == 0 }));
				}
				};
			HWASanPass(SanitizerKind::HWAddress, false);
			HWASanPass(SanitizerKind::KernelHWAddress, true);

			if (LangOpts.Sanitize.has(SanitizerKind::DataFlow)) {
				MPM.addPass(DataFlowSanitizerPass(LangOpts.NoSanitizeFiles));
			}
		};
	if (ClSanitizeOnOptimizerEarlyEP) {
		PB.registerOptimizerEarlyEPCallback(
			[SanitizersCallback](ModulePassManager& MPM, OptimizationLevel Level) {
				ModulePassManager NewMPM;
				SanitizersCallback(NewMPM, Level);
				if (!NewMPM.isEmpty()) {
					// Sanitizers can abandon<GlobalsAA>.
					NewMPM.addPass(RequireAnalysisPass<GlobalsAA, llvm::Module>());
					MPM.addPass(std::move(NewMPM));
				}
			});
	}
	else {
		// LastEP does not need GlobalsAA.
		PB.registerOptimizerLastEPCallback(SanitizersCallback);
	}
#endif
}

// LLVM/Clang 18.1.4
static void addKCFIPass(const llvm::Triple& TargetTriple, const BfCodeGenOptions& codeGenOpts, llvm::PassBuilder& PB) 
{
#if 0
	// If the back-end supports KCFI operand bundle lowering, skip KCFIPass.
	if (TargetTriple.getArch() == llvm::Triple::x86_64 ||
		TargetTriple.isAArch64(64) || TargetTriple.isRISCV())
		return;

	// Ensure we lower KCFI operand bundles with -O0.
	PB.registerOptimizerLastEPCallback(
		[&](ModulePassManager& MPM, OptimizationLevel Level) {
			if (Level == OptimizationLevel::O0 &&
				LangOpts.Sanitize.has(SanitizerKind::KCFI))
				MPM.addPass(createModuleToFunctionPassAdaptor(KCFIPass()));
		});

	// When optimizations are requested, run KCIFPass after InstCombine to
	// avoid unnecessary checks.
	PB.registerPeepholeEPCallback(
		[&](FunctionPassManager& FPM, OptimizationLevel Level) {
			if (Level != OptimizationLevel::O0 &&
				LangOpts.Sanitize.has(SanitizerKind::KCFI))
				FPM.addPass(KCFIPass());
		});
#endif
}

/// Check whether we should emit a module summary for regular LTO.
  /// The module summary should be emitted by default for regular LTO
  /// except for ld64 targets.
  ///
  /// \return True if the module summary should be emitted.
static bool shouldEmitRegularLTOSummary(const llvm::Triple& targetTriple, const BfCodeGenOptions& codeGenOptions, bool PrepareForLTO)
{
	return PrepareForLTO /*&& !CodeGenOpts.DisableLLVMPasses*/ &&
		targetTriple.getVendor() != llvm::Triple::Apple;
}

/// Check whether we should emit a flag for UnifiedLTO.
/// The UnifiedLTO module flag should be set when UnifiedLTO is enabled for
/// ThinLTO or Full LTO with module summaries.
static bool shouldEmitUnifiedLTOModueFlag(const llvm::Triple& targetTriple, const BfCodeGenOptions& codeGenOptions, bool PrepareForLTO)
{
	return false;
	/*return CodeGenOpts.UnifiedLTO &&
		(CodeGenOpts.PrepareForThinLTO || shouldEmitRegularLTOSummary());*/
}

void BfIRCodeGen::RunOptimizationPipeline(const llvm::Triple& targetTriple)
{
	bool verifyModule = true;

	std::optional<llvm::PGOOptions> pgoOptions;
	mLLVMTargetMachine->setPGOOption(pgoOptions);

	llvm::PipelineTuningOptions pto;
	pto.LoopUnrolling = !mCodeGenOptions.mDisableUnrollLoops;
	// For historical reasons, loop interleaving is set to mirror setting for loop unrolling.
	pto.LoopInterleaving = !mCodeGenOptions.mDisableUnrollLoops;
	pto.LoopVectorization = mCodeGenOptions.mLoopVectorize;
	pto.SLPVectorization = mCodeGenOptions.mSLPVectorize;
	pto.MergeFunctions = mCodeGenOptions.mMergeFunctions;
	//TODO:
	//pto.CallGraphProfile = ???
	//pto.UnifiedLTO = ???

	llvm::LoopAnalysisManager LAM;
	llvm::FunctionAnalysisManager FAM;
	llvm::CGSCCAnalysisManager CGAM;
	llvm::ModuleAnalysisManager MAM;

	llvm::PassInstrumentationCallbacks PIC;
// 	PrintPassOptions PrintPassOpts;
// 	PrintPassOpts.Indent = DebugPassStructure;
// 	PrintPassOpts.SkipAnalyses = DebugPassStructure;
// 	StandardInstrumentations SI(
// 		TheModule->getContext(),
// 		(CodeGenOpts.DebugPassManager || DebugPassStructure),
// 		CodeGenOpts.VerifyEach, PrintPassOpts);
// 	SI.registerCallbacks(PIC, &MAM);
	llvm::PassBuilder PB(mLLVMTargetMachine, pto, pgoOptions, &PIC);

	// Register all the basic analyses with the managers.
	PB.registerModuleAnalyses(MAM);
	PB.registerCGSCCAnalyses(CGAM);
	PB.registerFunctionAnalyses(FAM);
	PB.registerLoopAnalyses(LAM);
	PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
	
	//llvm::ModulePassManager MPM;
	// Add a verifier pass, before any other passes, to catch CodeGen issues.

	llvm::ModulePassManager MPM;
	if (verifyModule)
		MPM.addPass(llvm::VerifierPass());	

	bool disableLLVMPasses = false;
	if (!disableLLVMPasses)
	{
		llvm::OptimizationLevel Level;

		bool PrepareForLTO = false;
		bool PrepareForThinLTO = mCodeGenOptions.mLTOType == BfLTOType_Thin;
		//bool performThinLTO = false;

		Level = llvm::OptimizationLevel::O0;
		switch (mCodeGenOptions.mOptLevel)
		{
		case BfOptLevel_O0:
			Level = llvm::OptimizationLevel::O0;
			break;
		case BfOptLevel_O1:
			Level = llvm::OptimizationLevel::O1;
			break;
		case BfOptLevel_O2:
			Level = llvm::OptimizationLevel::O2;
			break;
		case BfOptLevel_O3:
			Level = llvm::OptimizationLevel::O3;
			break;
		case BfOptLevel_Og:			
			Level = llvm::OptimizationLevel::O1;
			break;
		}

		bool IsThinLTOPostLink = false;

#if 0
		// If we reached here with a non-empty index file name, then the index
		// file was empty and we are not performing ThinLTO backend compilation
		// (used in testing in a distributed build environment).

			bool IsThinLTOPostLink = !CodeGenOpts.ThinLTOIndexFile.empty();
			// If so drop any the type test assume sequences inserted for whole program
			// vtables so that codegen doesn't complain.
			if (IsThinLTOPostLink)
				PB.registerPipelineStartEPCallback(
					[](ModulePassManager& MPM, OptimizationLevel Level) {
						MPM.addPass(LowerTypeTestsPass(/*ExportSummary=*/nullptr,
						/*ImportSummary=*/nullptr,
						/*DropTypeTests=*/true));
					});

		// Register callbacks to schedule sanitizer passes at the appropriate part
		// of the pipeline.
			if (LangOpts.Sanitize.has(SanitizerKind::LocalBounds))
				PB.registerScalarOptimizerLateEPCallback(
					[](FunctionPassManager& FPM, OptimizationLevel Level) {
						FPM.addPass(BoundsCheckingPass());
					});
#endif

		// Don't add sanitizers if we are here from ThinLTO PostLink. That already
		// done on PreLink stage.
		if (!IsThinLTOPostLink) {
			addSanitizers(targetTriple, mCodeGenOptions, PB);
			addKCFIPass(targetTriple, mCodeGenOptions, PB);
		}

#if 0
		if (std::optional<GCOVOptions> Options =
			getGCOVOptions(CodeGenOpts, LangOpts))
			PB.registerPipelineStartEPCallback(
				[Options](ModulePassManager& MPM, OptimizationLevel Level) {
					MPM.addPass(GCOVProfilerPass(*Options));
				});
		if (std::optional<InstrProfOptions> Options =
			getInstrProfOptions(CodeGenOpts, LangOpts))
			PB.registerPipelineStartEPCallback(
				[Options](ModulePassManager& MPM, OptimizationLevel Level) {
					MPM.addPass(InstrProfilingLoweringPass(*Options, false));
				});

		// TODO: Consider passing the MemoryProfileOutput to the pass builder via
		// the PGOOptions, and set this up there.
		if (!CodeGenOpts.MemoryProfileOutput.empty()) {
			PB.registerOptimizerLastEPCallback(
				[](ModulePassManager& MPM, OptimizationLevel Level) {
					MPM.addPass(createModuleToFunctionPassAdaptor(MemProfilerPass()));
					MPM.addPass(ModuleMemProfilerPass());
				});
		}		
#endif

		if (mCodeGenOptions.mLTOType == BfLTOType_Fat)
		{
			MPM.addPass(PB.buildFatLTODefaultPipeline(
				Level, PrepareForThinLTO,
				PrepareForThinLTO || shouldEmitRegularLTOSummary(targetTriple, mCodeGenOptions, PrepareForLTO)));
		}
		else if (PrepareForThinLTO) 
		{
			MPM.addPass(PB.buildThinLTOPreLinkDefaultPipeline(Level));
		}
		else if (PrepareForLTO) 
		{
			MPM.addPass(PB.buildLTOPreLinkDefaultPipeline(Level));
		}
		else 
		{
			MPM.addPass(PB.buildPerModuleDefaultPipeline(Level));
		}
	}


	// Re-link against any bitcodes supplied via the -mlink-builtin-bitcode option
	// Some optimizations may generate new function calls that would not have
	// been linked pre-optimization (i.e. fused sincos calls generated by
	// AMDGPULibCalls::fold_sincos.)
	//TODO:
// 	if (ClRelinkBuiltinBitcodePostop)
// 		MPM.addPass(LinkInModulesPass(BC, false));

	// Add a verifier pass if requested. We don't have to do this if the action
	// requires code generation because there will already be a verifier pass in
	// the code-generation pipeline.
	// Since we already added a verifier pass above, this
	// might even not run the analysis, if previous passes caused no changes.
// 	if (!actionRequiresCodeGen(Action) && CodeGenOpts.VerifyModule)
// 		MPM.addPass(VerifierPass());

	//TODO:
#if 0
	if (Action == Backend_EmitBC || Action == Backend_EmitLL || CodeGenOpts.FatLTO)
	{
		if (CodeGenOpts.PrepareForThinLTO && !CodeGenOpts.DisableLLVMPasses) {
			if (!TheModule->getModuleFlag("EnableSplitLTOUnit"))
				TheModule->addModuleFlag(llvm::Module::Error, "EnableSplitLTOUnit",
					CodeGenOpts.EnableSplitLTOUnit);
			if (Action == Backend_EmitBC) {
				if (!CodeGenOpts.ThinLinkBitcodeFile.empty()) {
					ThinLinkOS = openOutputFile(CodeGenOpts.ThinLinkBitcodeFile);
					if (!ThinLinkOS)
						return;
				}
				MPM.addPass(ThinLTOBitcodeWriterPass(
					*OS, ThinLinkOS ? &ThinLinkOS->os() : nullptr));
			}
			else if (Action == Backend_EmitLL) {
				MPM.addPass(PrintModulePass(*OS, "", CodeGenOpts.EmitLLVMUseLists,
					/*EmitLTOSummary=*/true));
			}
		}
		else {
			// Emit a module summary by default for Regular LTO except for ld64
			// targets
			bool EmitLTOSummary = shouldEmitRegularLTOSummary();
			if (EmitLTOSummary) {
				if (!TheModule->getModuleFlag("ThinLTO") && !CodeGenOpts.UnifiedLTO)
					TheModule->addModuleFlag(llvm::Module::Error, "ThinLTO", uint32_t(0));
				if (!TheModule->getModuleFlag("EnableSplitLTOUnit"))
					TheModule->addModuleFlag(llvm::Module::Error, "EnableSplitLTOUnit",
						uint32_t(1));
			}
			if (Action == Backend_EmitBC) {
				MPM.addPass(BitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists,
					EmitLTOSummary));
			}
			else if (Action == Backend_EmitLL) {
				MPM.addPass(PrintModulePass(*OS, "", CodeGenOpts.EmitLLVMUseLists,
					EmitLTOSummary));
			}
		}

		if (shouldEmitUnifiedLTOModueFlag())
			TheModule->addModuleFlag(llvm::Module::Error, "UnifiedLTO", uint32_t(1));
	}
#endif

	// Print a textual, '-passes=' compatible, representation of pipeline if
	// requested.
//	if (PrintPipelinePasses) {
// 		MPM.printPipeline(outs(), [&PIC](StringRef ClassName) {
// 			auto PassName = PIC.getPassNameForClassName(ClassName);
// 			return PassName.empty() ? ClassName : PassName;
// 			});
// 		outs() << "\n";
// 		return;
// 	}
// 
// 	if (LangOpts.HIPStdPar && !LangOpts.CUDAIsDevice &&
// 		LangOpts.HIPStdParInterposeAlloc)
// 		MPM.addPass(HipStdParAllocationInterpositionPass());

	// Now that we have all of the passes ready, run them.
	{
		//PrettyStackTraceString CrashInfo("Optimizer");
		llvm::TimeTraceScope TimeScope("Optimizer");
		MPM.run(*mLLVMModule, MAM);
	}	
}

bool BfIRCodeGen::WriteObjectFile(const StringImpl& outFileName)
{
	ApplySimdFeatures();

	// 	{
	// 		PassManagerBuilderWrapper pmBuilder;
	//
	//
	// 	}

	mHasDebugLoc = false; // So fails don't show a line number

	bool enableLTO = mCodeGenOptions.mLTOType != BfLTOType_None;

	if (enableLTO)
	{
		// We have some constructs which trip up ThinLTO, and it's not useful to LTO here anyway
		if (GetFileName(outFileName) == "vdata.obj")
		{
			enableLTO = false;
		}

		if (mHadDLLExport) // LTO bug in LLVM-link?
			enableLTO = false;
	}

	std::error_code EC;
	llvm::sys::fs::OpenFlags OpenFlags = llvm::sys::fs::OF_None;

	llvm::raw_fd_ostream out(outFileName.c_str(), EC, OpenFlags);

	if (EC)
		return false;
	// Build up all of the passes that we want to do to the module.
	//llvm::legacy::PassManager PM;
	llvm::legacy::PassManager PM;

	llvm::Triple theTriple = llvm::Triple(mLLVMModule->getTargetTriple());
	// Add an appropriate TargetLibraryInfo pass for the module's triple.
	llvm::TargetLibraryInfoImpl TLII(theTriple);

	PM.add(new llvm::TargetLibraryInfoWrapperPass(TLII));

	// Add the target data from the target machine, if it exists, or the module.
	//PM.add(new DataLayoutPass());
	RunOptimizationPipeline(theTriple);

	llvm::raw_fd_ostream* outStream = NULL;
	defer ( delete outStream; );

	if ((enableLTO) || (mCodeGenOptions.mWriteBitcode))
	{
		std::error_code ec;
		outStream = new llvm::raw_fd_ostream(outFileName.c_str(), ec, llvm::sys::fs::OF_None);
		if (outStream->has_error())
		{
			return false;
		}

// 		if (enableLTO)
// 			PM.add(createWriteThinLTOBitcodePass(*outStream, NULL));
		//else
			PM.add(createBitcodeWriterPass(*outStream, false));
	}

// 	TargetPassConfig *PassConfig = target->createPassConfig(PM);
// 	PM.add(new BfPass());
// 	PM.add(sBfPass);


	// Do
	{
		//formatted_raw_ostream FOS(out);
		//raw_pwrite_stream *OS = &out->os();

		//TODO:
 		llvm::AnalysisID StartAfterID = nullptr;
 		llvm::AnalysisID StopAfterID = nullptr;
 		const llvm::PassRegistry *PR = llvm::PassRegistry::getPassRegistry();
 
 		//WriteBitcode
 		bool noVerify = false; // Option
 
 		if ((!enableLTO) && (!mCodeGenOptions.mWriteBitcode))
 		{
 			// Ask the target to add backend passes as necessary.
 			if (mLLVMTargetMachine->addPassesToEmitFile(PM, out, NULL,
 				(mCodeGenOptions.mAsmKind != BfAsmKind_None) ? llvm::CodeGenFileType::AssemblyFile : llvm::CodeGenFileType::ObjectFile,
 				//TargetMachine::CGFT_AssemblyFile,
 				noVerify /*, StartAfterID, StopAfterID*/))
 			{
 				Fail("Target does not support generation of this file type");
 				/*errs() << argv[0] << ": target does not support generation of this"
 					<< " file type!\n";*/
 				return false;
 			}
 		}

		bool success = PM.run(*mLLVMModule);

		if ((mCodeGenOptions.mOptLevel > BfOptLevel_O0) && (mCodeGenOptions.mWriteLLVMIR))
		{
			BP_ZONE("BfCodeGen::RunLoop.LLVM.IR");
			String fileName = outFileName;
			int dotPos = (int)fileName.LastIndexOf('.');
			if (dotPos != -1)
				fileName.RemoveToEnd(dotPos);

			fileName += "_OPT.ll";
			String irError;
			WriteIR(fileName, irError);
		}
	}

	return true;
}

bool BfIRCodeGen::WriteIR(const StringImpl& outFileName, StringImpl& error)
{
	std::error_code ec;
	llvm::raw_fd_ostream outStream(outFileName.c_str(), ec, llvm::sys::fs::OpenFlags::OF_Text);
	if (ec)
	{
	 	error = ec.message();
		return false;
	}
	mLLVMModule->print(outStream, NULL);
	return true;
}

void BfIRCodeGen::ApplySimdFeatures()
{
	Array<std::tuple<llvm::Function*, BfIRSimdType>> functionsToProcess;

	for (auto pair : mFunctionsUsingSimd)
		functionsToProcess.Add({ pair.mKey, pair.mValue });

	while (functionsToProcess.Count() > 0)
	{
		auto tuple = functionsToProcess.front();
		functionsToProcess.RemoveAt(0);

		auto function = std::get<0>(tuple);
		auto simdType = std::get<1>(tuple);

		auto currentSimdType = GetSimdTypeFromFunction(function);
		simdType = simdType > currentSimdType ? simdType : currentSimdType;

		function->addFnAttr("target-features", GetSimdTypeString(simdType).c_str());

		if (function->hasFnAttribute(llvm::Attribute::AlwaysInline))
		{
			for (auto user : function->users())
			{
				if (auto call = llvm::dyn_cast<llvm::CallInst>(user))
				{
					auto func = call->getFunction();
					functionsToProcess.Add({ func, simdType });
				}
			}
		}
	}
}

int BfIRCodeGen::GetIntrinsicId(const StringImpl& name)
{
	auto itr = std::lower_bound(std::begin(gIntrinEntries), std::end(gIntrinEntries), name);
	if (itr != std::end(gIntrinEntries) && strcmp(itr->mName, name.c_str()) == 0)
	{
		int id = (int)(itr - gIntrinEntries);
		return id;
	}

	if (name.StartsWith("shuffle"))
		return BfIRIntrinsic_Shuffle;

	if (name.Contains(':'))
		return BfIRIntrinsic__PLATFORM;

	return -1;
}

const char* BfIRCodeGen::GetIntrinsicName(int intrinId)
{
	return gIntrinEntries[intrinId].mName;
}

void BfIRCodeGen::SetAsmKind(BfAsmKind asmKind)
{
	const char* args[] = {"", (asmKind == BfAsmKind_ATT) ? "-x86-asm-syntax=att" : "-x86-asm-syntax=intel" };
	llvm::cl::ParseCommandLineOptions(2, args);
}

#ifdef BF_PLATFORM_LINUX
//HACK: I don't know why this is needed, but we get link errors if we don't have it.
int BF_LinuxFixLinkage()
{
	llvm::MCContext* ctx = NULL;
	llvm::raw_pwrite_stream* stream = NULL;

	createWasmStreamer(*ctx, NULL, NULL, NULL, false);
	createMachOStreamer(*ctx, NULL, NULL, NULL, false, false, false);
	createAsmStreamer(*ctx, NULL, false, false, NULL, NULL, NULL, false);
	createELFStreamer(*ctx, NULL, NULL, NULL, false);

	return 0;
}
#endif

//#include "aarch64/Disassembler/X86DisassemblerDecoder.h"
//#include "X86/MCTargetDesc/X86MCTargetDesc.h"
//#include "X86/MCTargetDesc/X86BaseInfo.h"
//#include "X86InstrInfo.h"

#ifdef BF_PLATFORM_MACOS
#include "AArch64/MCTargetDesc/AArch64MCTargetDesc.h"
//#include "AArch64/MCTargetDesc/AArch64BaseInfo.h"
//#include "../X86InstrInfo.h"

int BF_AARC64_Linkage()
{
	LLVMInitializeAArch64TargetInfo();
	LLVMInitializeAArch64Target();
	LLVMInitializeAArch64TargetMC();
	return 0;
}
#endif

void BfIRCodeGen::StaticInit()
{
	LLVMInitializeX86TargetInfo();
	LLVMInitializeX86Target();
	LLVMInitializeX86TargetMC();
	LLVMInitializeX86AsmPrinter();
	LLVMInitializeX86AsmParser();
	LLVMInitializeX86Disassembler();

	LLVMInitializeARMTargetInfo();
	LLVMInitializeARMTarget();
	LLVMInitializeARMTargetMC();
	LLVMInitializeARMAsmPrinter();

	LLVMInitializeAArch64TargetInfo();
	LLVMInitializeAArch64Target();
	LLVMInitializeAArch64TargetMC();
	LLVMInitializeAArch64AsmPrinter();
	//LLVMInitializeAArch64Parser();
	//LLVMInitializeX86Disassembler();

	LLVMInitializeWebAssemblyTargetInfo();
	LLVMInitializeWebAssemblyTarget();
	LLVMInitializeWebAssemblyTargetMC();
	LLVMInitializeWebAssemblyAsmPrinter();
	//LLVMInitializeWebAssemblyAsmParser();
	LLVMInitializeWebAssemblyDisassembler();
}