mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 19:48:20 +02:00
1990 lines
51 KiB
C++
1990 lines
51 KiB
C++
#include "CeMachine.h"
|
|
#include "BfModule.h"
|
|
#include "BfCompiler.h"
|
|
#include "BfIRBuilder.h"
|
|
#include "..\Backend\BeIRCodeGen.h"
|
|
|
|
USING_NS_BF;
|
|
|
|
|
|
struct CeOpInfo
|
|
{
|
|
const char* mName;
|
|
CeOperandInfoKind mResultKind;
|
|
CeOperandInfoKind mOperandA;
|
|
CeOperandInfoKind mOperandB;
|
|
CeOperandInfoKind mOperandC;
|
|
};
|
|
|
|
#define CEOPINFO_SIZED_2(OPNAME, OPINFOA, OPINFOB) \
|
|
{OPNAME##"_8", OPINFOA, OPINFOB}, \
|
|
{OPNAME##"_16", OPINFOA, OPINFOB}, \
|
|
{OPNAME##"_32", OPINFOA, OPINFOB}, \
|
|
{OPNAME##"_64", OPINFOA, OPINFOB}, \
|
|
{OPNAME##"_X", OPINFOA, CEOI_IMM32, OPINFOB}
|
|
|
|
#define CEOPINFO_SIZED_3(OPNAME, OPINFOA, OPINFOB, OPINFOC) \
|
|
{OPNAME##"_8", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_16", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_32", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_64", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_X", OPINFOA, CEOI_IMM32, OPINFOB, OPINFOC}
|
|
|
|
#define CEOPINFO_SIZED_NUMERIC(OPNAME, OPINFOA, OPINFOB, OPINFOC) \
|
|
{OPNAME##"_I8", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_I16", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_I32", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_I64", OPINFOA, OPINFOB, OPINFOC}
|
|
|
|
#define CEOPINFO_SIZED_NUMERIC_PLUSF_2(OPNAME, OPINFOA, OPINFOB) \
|
|
{OPNAME##"_I8", OPINFOA, OPINFOB}, \
|
|
{OPNAME##"_I16", OPINFOA, OPINFOB}, \
|
|
{OPNAME##"_I32", OPINFOA, OPINFOB}, \
|
|
{OPNAME##"_I64", OPINFOA, OPINFOB}, \
|
|
{OPNAME##"_F32", OPINFOA, OPINFOB}, \
|
|
{OPNAME##"_F64", OPINFOA, OPINFOB}
|
|
#define CEOPINFO_SIZED_NUMERIC_PLUSF_3(OPNAME, OPINFOA, OPINFOB, OPINFOC) \
|
|
{OPNAME##"_I8", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_I16", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_I32", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_I64", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_F32", OPINFOA, OPINFOB, OPINFOC}, \
|
|
{OPNAME##"_F64", OPINFOA, OPINFOB, OPINFOC}
|
|
|
|
static CeOpInfo gOpInfo[] =
|
|
{
|
|
{"InvalidOp"},
|
|
{"Ret"},
|
|
{"Jmp", CEOI_None, CEOI_JMPREL},
|
|
{"JmpIf", CEOI_None, CEOI_JMPREL, CEOI_FrameRef},
|
|
{"JmpIfNot", CEOI_None, CEOI_JMPREL, CEOI_FrameRef},
|
|
{"FrameAddr32", CEOI_FrameRef, CEOI_FrameRef},
|
|
{"FrameAddr64", CEOI_FrameRef, CEOI_FrameRef},
|
|
|
|
{"Const_8", CEOI_FrameRef, CEOI_IMM8},
|
|
{"Const_16", CEOI_FrameRef, CEOI_IMM16},
|
|
{"Const_32", CEOI_FrameRef, CEOI_IMM32},
|
|
{"Const_64", CEOI_FrameRef, CEOI_IMM64},
|
|
{"Const_X", CEOI_FrameRef, CEOI_IMM_VAR},
|
|
|
|
CEOPINFO_SIZED_2("Load", CEOI_FrameRef, CEOI_FrameRef),
|
|
CEOPINFO_SIZED_3("Store", CEOI_None, CEOI_FrameRef, CEOI_FrameRef),
|
|
CEOPINFO_SIZED_3("Move", CEOI_None, CEOI_FrameRef, CEOI_FrameRef),
|
|
CEOPINFO_SIZED_2("Push", CEOI_None, CEOI_FrameRef),
|
|
|
|
{"AdjustSP", CEOI_None, CEOI_FrameRef},
|
|
{"Call", CEOI_None, CEOI_IMM32},
|
|
|
|
{"Conv_I32_I64", CEOI_FrameRef, CEOI_FrameRef},
|
|
|
|
CEOPINFO_SIZED_NUMERIC_PLUSF_3("Add", CEOI_FrameRef, CEOI_FrameRef, CEOI_FrameRef),
|
|
CEOPINFO_SIZED_NUMERIC_PLUSF_3("Cmp_EQ", CEOI_FrameRef, CEOI_FrameRef, CEOI_FrameRef),
|
|
CEOPINFO_SIZED_NUMERIC_PLUSF_3("Cmp_SLT", CEOI_FrameRef, CEOI_FrameRef, CEOI_FrameRef),
|
|
//{"Cmp_SLT_I32", CEOI_FrameRef, CEOI_FrameRef, CEOI_FrameRef},
|
|
|
|
CEOPINFO_SIZED_NUMERIC_PLUSF_2("Neg", CEOI_FrameRef, CEOI_FrameRef),
|
|
};
|
|
|
|
static_assert(BF_ARRAY_COUNT(gOpInfo) == (int)CeOp_COUNT, "gOpName incorrect size");
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#define CE_GET(T) *((T*)(mPtr += sizeof(T)) - 1)
|
|
|
|
void CeDumpContext::DumpOperandInfo(CeOperandInfoKind operandInfoKind)
|
|
{
|
|
switch (operandInfoKind)
|
|
{
|
|
case CEOI_FrameRef:
|
|
{
|
|
int32 addr = CE_GET(int32);
|
|
char str[64];
|
|
if (addr >= 0)
|
|
sprintf(str, "FR+0x%X", addr);
|
|
else
|
|
sprintf(str, "FR-0x%X", -addr);
|
|
mStr += str;
|
|
}
|
|
break;
|
|
case CEOI_IMM8:
|
|
{
|
|
int32 val = CE_GET(int8);
|
|
char str[64];
|
|
sprintf(str, "%d", val);
|
|
mStr += str;
|
|
}
|
|
break;
|
|
case CEOI_IMM16:
|
|
{
|
|
int32 val = CE_GET(int16);
|
|
char str[64];
|
|
sprintf(str, "%d", val);
|
|
mStr += str;
|
|
}
|
|
break;
|
|
case CEOI_IMM32:
|
|
{
|
|
int32 val = CE_GET(int32);
|
|
char str[64];
|
|
sprintf(str, "%d", val);
|
|
mStr += str;
|
|
}
|
|
break;
|
|
case CEOI_IMM64:
|
|
{
|
|
int64 val = CE_GET(int64);
|
|
char str[64];
|
|
sprintf(str, "%lld", val);
|
|
mStr += str;
|
|
}
|
|
break;
|
|
case CEOI_IMM_VAR:
|
|
{
|
|
mStr += '[';
|
|
int32 size = CE_GET(int32);
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
if (i != 0)
|
|
mStr += ", ";
|
|
uint8 val = CE_GET(uint8);
|
|
char str[64];
|
|
sprintf(str, "%X", val);
|
|
mStr += str;
|
|
}
|
|
mStr += ']';
|
|
}
|
|
break;
|
|
case CEOI_JMPREL:
|
|
{
|
|
int32 val = CE_GET(int32);
|
|
char str[64];
|
|
sprintf(str, "JMP:%04X", (int32)(val + (mPtr - mStart)));
|
|
mStr += str;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CeDumpContext::Dump()
|
|
{
|
|
if (!mCeFunction->mGenError.IsEmpty())
|
|
mStr += StrFormat("Gen Error: %s\n", mCeFunction->mGenError.c_str());
|
|
mStr += StrFormat("Frame Size: %d\n", mCeFunction->mFrameSize);
|
|
|
|
uint8* start = mPtr;
|
|
int curEmitIdx = 0;
|
|
CeEmitEntry* curEmitEntry = NULL;
|
|
|
|
while (mPtr < mEnd)
|
|
{
|
|
int ofs = mPtr - start;
|
|
|
|
while ((curEmitIdx < mCeFunction->mEmitTable.mSize) && (ofs > mCeFunction->mEmitTable[curEmitIdx].mCodePos))
|
|
curEmitIdx++;
|
|
|
|
if (curEmitIdx < mCeFunction->mEmitTable.mSize)
|
|
curEmitEntry = &mCeFunction->mEmitTable[curEmitIdx];
|
|
|
|
CeOp op = CE_GET(CeOp);
|
|
|
|
CeOpInfo& opInfo = gOpInfo[op];
|
|
|
|
mStr += StrFormat("%04X: ", ofs);
|
|
|
|
if (opInfo.mResultKind != CEOI_None)
|
|
{
|
|
DumpOperandInfo(opInfo.mResultKind);
|
|
mStr += " = ";
|
|
}
|
|
|
|
mStr += opInfo.mName;
|
|
if (opInfo.mOperandA != CEOI_None)
|
|
{
|
|
mStr += " ";
|
|
DumpOperandInfo(opInfo.mOperandA);
|
|
}
|
|
|
|
if (opInfo.mOperandB != CEOI_None)
|
|
{
|
|
mStr += ", ";
|
|
DumpOperandInfo(opInfo.mOperandB);
|
|
}
|
|
|
|
if (opInfo.mOperandC != CEOI_None)
|
|
{
|
|
mStr += ", ";
|
|
DumpOperandInfo(opInfo.mOperandC);
|
|
}
|
|
|
|
if ((curEmitEntry != NULL) && (curEmitEntry->mFile != -1))
|
|
{
|
|
mStr += StrFormat(" @%d[%s:%d]", curEmitIdx, GetFileName(mCeFunction->mFiles[curEmitEntry->mFile]).c_str(),
|
|
curEmitEntry->mLine + 1, curEmitEntry->mColumn + 1);
|
|
}
|
|
|
|
mStr += "\n";
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void CeBuilder::Fail(const StringImpl& str)
|
|
{
|
|
if (!mCeFunction->mGenError.IsEmpty())
|
|
return;
|
|
|
|
String errStr = StrFormat("Failure during const code generation of %s: %s", mBeFunction->mName.c_str(), str.c_str());
|
|
|
|
if (mCurDbgLoc != NULL)
|
|
{
|
|
BeDumpContext dumpCtx;
|
|
errStr += "\n DbgLoc : ";
|
|
dumpCtx.ToString(errStr, mCurDbgLoc);
|
|
}
|
|
|
|
mCeFunction->mGenError = errStr;
|
|
}
|
|
|
|
void CeBuilder::Emit(uint8 val)
|
|
{
|
|
mCeFunction->mCode.Add((uint8)val);
|
|
}
|
|
|
|
void CeBuilder::Emit(CeOp val)
|
|
{
|
|
*(CeOp*)mCeFunction->mCode.GrowUninitialized(sizeof(CeOp)) = val;
|
|
}
|
|
|
|
void CeBuilder::Emit(int32 val)
|
|
{
|
|
*(int32*)mCeFunction->mCode.GrowUninitialized(4) = val;
|
|
}
|
|
|
|
void CeBuilder::Emit(bool val)
|
|
{
|
|
BF_FATAL("Invalid emit");
|
|
}
|
|
|
|
void CeBuilder::Emit(void* ptr, int size)
|
|
{
|
|
memcpy(mCeFunction->mCode.GrowUninitialized(size), ptr, size);
|
|
}
|
|
|
|
void CeBuilder::EmitJump(CeOp op, const CeOperand& block)
|
|
{
|
|
BF_ASSERT(block.mKind == CeOperandKind_Block);
|
|
Emit(op);
|
|
|
|
CeJumpEntry jumpEntry;
|
|
jumpEntry.mBlockIdx = block.mBlockIdx;
|
|
jumpEntry.mEmitPos = GetCodePos();
|
|
mJumpTable.Add(jumpEntry);
|
|
|
|
Emit((int32)0);
|
|
}
|
|
|
|
void CeBuilder::EmitBinarySwitchSection(BeSwitchInst* switchInst, int startIdx, int endIdx)
|
|
{
|
|
// This is an empirically determined binary switching limit
|
|
if (endIdx - startIdx >= 18)
|
|
{
|
|
// int gteLabel = mCurLabelIdx++;
|
|
//
|
|
// auto mcDefaultBlock = GetOperand(switchInst->mDefaultBlock);
|
|
//
|
|
// int midIdx = startIdx + (endIdx - startIdx) / 2;
|
|
// auto& switchCase = switchInst->mCases[midIdx];
|
|
// auto switchBlock = GetOperand(switchCase.mBlock);
|
|
// auto mcValue = GetOperand(switchInst->mValue);
|
|
// auto valueType = GetType(mcValue);
|
|
//
|
|
// AllocInst(BeMCInstKind_Cmp, mcValue, GetOperand(switchCase.mValue));
|
|
// AllocInst(BeMCInstKind_CondBr, BeMCOperand::FromLabel(gteLabel), BeMCOperand::FromCmpKind(BeCmpKind_SGE));
|
|
// switchBlock.mBlock->AddPred(mActiveBlock);
|
|
//
|
|
// CreateBinarySwitchSection(switchInst, startIdx, midIdx);
|
|
// AllocInst(BeMCInstKind_Br, mcDefaultBlock);
|
|
// CreateLabel(-1, gteLabel);
|
|
// CreateBinarySwitchSection(switchInst, midIdx, endIdx);
|
|
// return;
|
|
}
|
|
|
|
for (int caseIdx = startIdx; caseIdx < endIdx; caseIdx++)
|
|
{
|
|
auto& switchCase = switchInst->mCases[caseIdx];
|
|
auto switchBlock = GetOperand(switchCase.mBlock);
|
|
auto mcValue = GetOperand(switchInst->mValue);
|
|
CeOperand result;
|
|
EmitBinaryOp(CeOp_Cmp_EQ_I8, CeOp_Cmp_EQ_F32, mcValue, GetOperand(switchCase.mValue), result);
|
|
EmitJump(CeOp_JmpIf, switchBlock);
|
|
EmitFrameOffset(result);
|
|
}
|
|
}
|
|
|
|
int CeBuilder::GetCodePos()
|
|
{
|
|
return (int)mCeFunction->mCode.size();
|
|
}
|
|
|
|
void CeBuilder::EmitFrameOffset(const CeOperand& val)
|
|
{
|
|
BF_ASSERT(val.mKind == CeOperandKind_FrameOfs);
|
|
Emit((int32)val.mFrameOfs);
|
|
}
|
|
|
|
void CeBuilder::FlushPhi(CeBlock* ceBlock, int targetBlockIdx)
|
|
{
|
|
for (int i = 0; i < (int)ceBlock->mPhiOutgoing.size(); i++)
|
|
{
|
|
auto& phiOutgoing = ceBlock->mPhiOutgoing[i];
|
|
if (phiOutgoing.mPhiBlockIdx == targetBlockIdx)
|
|
{
|
|
auto targetPhi = GetOperand(phiOutgoing.mPhiInst);
|
|
auto mcVal = GetOperand(phiOutgoing.mPhiValue);
|
|
EmitSizedOp(CeOp_Move_8, mcVal, NULL, true);
|
|
EmitFrameOffset(targetPhi);
|
|
|
|
ceBlock->mPhiOutgoing.RemoveAt(i);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CeBuilder::EmitBinaryOp(CeOp iOp, CeOp fOp, const CeOperand& lhs, const CeOperand& rhs, CeOperand& result)
|
|
{
|
|
CeOp op = iOp;
|
|
if (lhs.mType->IsIntegral())
|
|
{
|
|
if (lhs.mType->mSize == 1)
|
|
op = iOp;
|
|
else if (lhs.mType->mSize == 2)
|
|
op = (CeOp)(iOp + 1);
|
|
else if (lhs.mType->mSize == 4)
|
|
op = (CeOp)(iOp + 2);
|
|
else if (lhs.mType->mSize == 8)
|
|
op = (CeOp)(iOp + 3);
|
|
else
|
|
Fail("Invalid int operand size");
|
|
}
|
|
else if (lhs.mType->IsFloat())
|
|
{
|
|
if (lhs.mType->mSize == 4)
|
|
op = fOp;
|
|
else if (lhs.mType->mSize == 8)
|
|
op = (CeOp)(fOp + 1);
|
|
else
|
|
Fail("Invalid float operand size");
|
|
}
|
|
else
|
|
Fail("Invalid binary operand");
|
|
Emit(op);
|
|
|
|
result = FrameAlloc(lhs.mType);
|
|
EmitFrameOffset(result);
|
|
EmitFrameOffset(lhs);
|
|
EmitFrameOffset(rhs);
|
|
}
|
|
|
|
void CeBuilder::EmitUnaryOp(CeOp iOp, CeOp fOp, const CeOperand& val, CeOperand& result)
|
|
{
|
|
CeOp op = iOp;
|
|
if (val.mType->IsIntegral())
|
|
{
|
|
if (val.mType->mSize == 1)
|
|
op = iOp;
|
|
else if (val.mType->mSize == 2)
|
|
op = (CeOp)(iOp + 1);
|
|
else if (val.mType->mSize == 4)
|
|
op = (CeOp)(iOp + 2);
|
|
else if (val.mType->mSize == 8)
|
|
op = (CeOp)(iOp + 3);
|
|
else
|
|
Fail("Invalid int operand size");
|
|
}
|
|
else if (val.mType->IsFloat())
|
|
{
|
|
if (val.mType->mSize == 4)
|
|
op = fOp;
|
|
else if (val.mType->mSize == 8)
|
|
op = (CeOp)(fOp + 1);
|
|
else
|
|
Fail("Invalid float operand size");
|
|
}
|
|
else
|
|
Fail("Invalid unary operand");
|
|
Emit(op);
|
|
|
|
result = FrameAlloc(val.mType);
|
|
EmitFrameOffset(result);
|
|
EmitFrameOffset(val);
|
|
}
|
|
|
|
void CeBuilder::EmitSizedOp(CeOp baseOp, const CeOperand& operand, CeOperand* outResult, bool allowNonStdSize)
|
|
{
|
|
bool isStdSize = true;
|
|
CeOp op = CeOp_InvalidOp;
|
|
if (operand.mType->mSize == 1)
|
|
op = baseOp;
|
|
else if (operand.mType->mSize == 2)
|
|
op = (CeOp)(baseOp + 1);
|
|
else if (operand.mType->mSize == 4)
|
|
op = (CeOp)(baseOp + 2);
|
|
else if (operand.mType->mSize == 8)
|
|
op = (CeOp)(baseOp + 3);
|
|
else
|
|
{
|
|
isStdSize = false;
|
|
op = (CeOp)(baseOp + 4);
|
|
}
|
|
|
|
Emit(op);
|
|
|
|
if (outResult != NULL)
|
|
{
|
|
*outResult = FrameAlloc(operand.mType);
|
|
EmitFrameOffset(*outResult);
|
|
}
|
|
|
|
if (!isStdSize)
|
|
{
|
|
if (!allowNonStdSize)
|
|
Fail("Invalid operand size");
|
|
Emit((int32)operand.mType->mSize);
|
|
}
|
|
|
|
EmitFrameOffset(operand);
|
|
}
|
|
|
|
CeOperand CeBuilder::FrameAlloc(BeType* type)
|
|
{
|
|
mFrameSize += type->mSize;
|
|
|
|
CeOperand result;
|
|
result.mKind = CeOperandKind_FrameOfs;
|
|
result.mFrameOfs = -mFrameSize;
|
|
result.mType = type;
|
|
return result;
|
|
}
|
|
|
|
CeOperand CeBuilder::GetOperand(BeValue* value, bool allowAlloca)
|
|
{
|
|
if (value == NULL)
|
|
return CeOperand();
|
|
|
|
switch (value->GetTypeId())
|
|
{
|
|
case BeGlobalVariable::TypeId:
|
|
{
|
|
// auto globalVar = (BeGlobalVariable*)value;
|
|
// if ((globalVar->mIsTLS) && (mTLSVRegIdx == -1))
|
|
// {
|
|
// auto tlsVReg = AllocVirtualReg(mNativeIntType);
|
|
// auto vregInfo = GetVRegInfo(tlsVReg);
|
|
// vregInfo->mMustExist = true;
|
|
// vregInfo->mForceReg = true;
|
|
// mTLSVRegIdx = tlsVReg.mVRegIdx;
|
|
// }
|
|
//
|
|
// auto sym = mCOFFObject->GetSymbol(globalVar);
|
|
// if (sym != NULL)
|
|
// {
|
|
// CeOperand mcOperand;
|
|
// mcOperand.mKind = CeOperandKind_SymbolAddr;
|
|
// mcOperand.mSymbolIdx = sym->mIdx;
|
|
// return mcOperand;
|
|
// }
|
|
}
|
|
break;
|
|
case BeCastConstant::TypeId:
|
|
{
|
|
// auto constant = (BeCastConstant*)value;
|
|
//
|
|
// CeOperand mcOperand;
|
|
// auto relTo = GetOperand(constant->mTarget);
|
|
// if (relTo.mKind == CeOperandKind_Immediate_Null)
|
|
// {
|
|
// mcOperand.mKind = CeOperandKind_Immediate_Null;
|
|
// mcOperand.mType = constant->mType;
|
|
// return mcOperand;
|
|
// }
|
|
//
|
|
// mcOperand = AllocVirtualReg(constant->mType);
|
|
// auto vregInfo = GetVRegInfo(mcOperand);
|
|
// vregInfo->mDefOnFirstUse = true;
|
|
// vregInfo->mRelTo = relTo;
|
|
// vregInfo->mIsExpr = true;
|
|
//
|
|
// return mcOperand;
|
|
}
|
|
break;
|
|
case BeConstant::TypeId:
|
|
{
|
|
uint64 u64Val = 0;
|
|
float fVal = 0;
|
|
void* dataPtr = NULL;
|
|
int dataSize = 0;
|
|
|
|
auto constant = (BeConstant*)value;
|
|
CeOperand mcOperand;
|
|
switch (constant->mType->mTypeCode)
|
|
{
|
|
case BeTypeCode_Boolean:
|
|
case BeTypeCode_Int8:
|
|
case BeTypeCode_Int16:
|
|
case BeTypeCode_Int32:
|
|
case BeTypeCode_Int64:
|
|
case BeTypeCode_Double:
|
|
dataPtr = &constant->mUInt64;
|
|
dataSize = constant->mType->mSize;
|
|
break;
|
|
case BeTypeCode_Float:
|
|
fVal = (float)constant->mDouble;
|
|
dataPtr = &fVal;
|
|
dataSize = 4;
|
|
break;
|
|
case BeTypeCode_Pointer:
|
|
{
|
|
if (constant->mTarget == NULL)
|
|
{
|
|
dataPtr = &u64Val;
|
|
dataSize = mPtrSize;
|
|
}
|
|
else
|
|
{
|
|
// auto relTo = GetOperand(constant->mTarget);
|
|
//
|
|
// if (relTo.mKind == CeOperandKind_Immediate_Null)
|
|
// {
|
|
// mcOperand.mKind = CeOperandKind_Immediate_Null;
|
|
// mcOperand.mType = constant->mType;
|
|
// return mcOperand;
|
|
// }
|
|
//
|
|
// mcOperand = AllocVirtualReg(constant->mType);
|
|
// auto vregInfo = GetVRegInfo(mcOperand);
|
|
// vregInfo->mDefOnFirstUse = true;
|
|
// vregInfo->mRelTo = relTo;
|
|
// vregInfo->mIsExpr = true;
|
|
//
|
|
// return mcOperand;
|
|
}
|
|
}
|
|
break;
|
|
// case BeTypeCode_Struct:
|
|
// case BeTypeCode_SizedArray:
|
|
// case BeTypeCode_Vector:
|
|
// mcOperand.mImmediate = constant->mInt64;
|
|
// mcOperand.mKind = CeOperandKind_Immediate_i64;
|
|
// break;
|
|
// default:
|
|
// Fail("Unhandled constant type");
|
|
}
|
|
|
|
if (dataPtr != NULL)
|
|
{
|
|
auto beType = constant->mType;
|
|
auto result = FrameAlloc(beType);
|
|
|
|
CeSizeClass sizeClass = GetSizeClass(dataSize);
|
|
Emit((CeOp)(CeOp_Const_8 + sizeClass));
|
|
EmitFrameOffset(result);
|
|
if (sizeClass == CeSizeClass_X)
|
|
Emit((int32)dataSize);
|
|
Emit(dataPtr, dataSize);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
break;
|
|
case BeStructConstant::TypeId:
|
|
{
|
|
// auto structConstant = (BeStructConstant*)value;
|
|
//
|
|
// CeOperand mcOperand;
|
|
// mcOperand.mKind = CeOperandKind_ConstAgg;
|
|
// mcOperand.mConstant = structConstant;
|
|
//
|
|
// return mcOperand;
|
|
}
|
|
case BeGEPConstant::TypeId:
|
|
{
|
|
// auto gepConstant = (BeGEPConstant*)value;
|
|
//
|
|
// auto mcVal = GetOperand(gepConstant->mTarget);
|
|
//
|
|
// BePointerType* ptrType = (BePointerType*)GetType(mcVal);
|
|
// BEMC_ASSERT(ptrType->mTypeCode == BeTypeCode_Pointer);
|
|
//
|
|
// auto result = mcVal;
|
|
//
|
|
// // We assume we never do both an idx0 and idx1 at once. Fix if we change that.
|
|
// int byteOffset = 0;
|
|
// BeType* elementType = NULL;
|
|
// byteOffset += gepConstant->mIdx0 * ptrType->mElementType->mSize;
|
|
//
|
|
// if (ptrType->mElementType->mTypeCode == BeTypeCode_Struct)
|
|
// {
|
|
// BeStructType* structType = (BeStructType*)ptrType->mElementType;
|
|
// auto& structMember = structType->mMembers[gepConstant->mIdx1];
|
|
// elementType = structMember.mType;
|
|
// byteOffset = structMember.mByteOffset;
|
|
// }
|
|
// else
|
|
// {
|
|
// BEMC_ASSERT(ptrType->mElementType->mTypeCode == BeTypeCode_SizedArray);
|
|
// auto arrayType = (BeSizedArrayType*)ptrType->mElementType;
|
|
// elementType = arrayType->mElementType;
|
|
// byteOffset = gepConstant->mIdx1 * elementType->mSize;
|
|
// }
|
|
//
|
|
// auto elementPtrType = mModule->mContext->GetPointerTo(elementType);
|
|
// result = AllocRelativeVirtualReg(elementPtrType, result, GetImmediate(byteOffset), 1);
|
|
// // The def is primary to create a single 'master location' for the GEP vreg to become legalized before use
|
|
// auto vregInfo = GetVRegInfo(result);
|
|
// vregInfo->mDefOnFirstUse = true;
|
|
// result.mKind = CeOperandKind_VReg;
|
|
//
|
|
// return result;
|
|
}
|
|
break;
|
|
case BeExtractValueConstant::TypeId:
|
|
{
|
|
// Note: this only handles zero-aggregates
|
|
// auto extractConstant = (BeExtractValueConstant*)value;
|
|
// auto elementType = extractConstant->GetType();
|
|
//
|
|
// auto mcVal = GetOperand(extractConstant->mTarget);
|
|
// auto valType = GetType(mcVal);
|
|
//
|
|
// BeConstant beConstant;
|
|
// beConstant.mType = elementType;
|
|
// beConstant.mUInt64 = 0;
|
|
// return GetOperand(&beConstant);
|
|
}
|
|
break;
|
|
case BeFunction::TypeId:
|
|
{
|
|
// auto sym = mCOFFObject->GetSymbol(value);
|
|
// BEMC_ASSERT(sym != NULL);
|
|
// if (sym != NULL)
|
|
// {
|
|
// CeOperand mcOperand;
|
|
// mcOperand.mKind = CeOperandKind_SymbolAddr;
|
|
// mcOperand.mSymbolIdx = sym->mIdx;
|
|
// return mcOperand;
|
|
// }
|
|
}
|
|
break;
|
|
case BeCallInst::TypeId:
|
|
{
|
|
// auto callInst = (BeCallInst*)value;
|
|
// if (callInst->mInlineResult != NULL)
|
|
// return GetOperand(callInst->mInlineResult);
|
|
}
|
|
break;
|
|
}
|
|
|
|
CeOperand* operandPtr = NULL;
|
|
mValueToOperand.TryGetValue(value, &operandPtr);
|
|
|
|
//if (!allowFail)
|
|
{
|
|
if (operandPtr == NULL)
|
|
{
|
|
BeDumpContext dumpCtx;
|
|
String str;
|
|
dumpCtx.ToString(str, value);
|
|
Fail(StrFormat("Unable to find bevalue for operand: %s", str.c_str()));
|
|
}
|
|
}
|
|
if (operandPtr == NULL)
|
|
{
|
|
return FrameAlloc(mIntPtrType);
|
|
}
|
|
|
|
auto operand = *operandPtr;
|
|
|
|
if ((operand.mKind == CeOperandKind_AllocaAddr) && (!allowAlloca))
|
|
{
|
|
auto irCodeGen = mCeMachine->mCeModule->mBfIRBuilder->mBeIRCodeGen;
|
|
auto result = FrameAlloc(mIntPtrType);
|
|
Emit((mPtrSize == 4) ? CeOp_FrameAddr32 : CeOp_FrameAddr64);
|
|
EmitFrameOffset(result);
|
|
Emit((int32)operand.mFrameOfs);
|
|
return result;
|
|
}
|
|
|
|
// if ((operand.mKind == CeOperandKind_Phi) && (!allowMetaResult))
|
|
// {
|
|
// auto phi = operand.mPhi;
|
|
//
|
|
// int phiInstIdx = 0;
|
|
//
|
|
// auto mcBlock = phi->mBlock;
|
|
// for (auto instIdx = 0; instIdx < mcBlock->mInstructions.size(); instIdx++)
|
|
// {
|
|
// auto inst = mcBlock->mInstructions[instIdx];
|
|
// if (inst->mKind == BeMCInstKind_DefPhi)
|
|
// {
|
|
// BEMC_ASSERT(inst->mArg0.mPhi == phi);
|
|
// phiInstIdx = instIdx;
|
|
// RemoveInst(mcBlock, phiInstIdx);
|
|
// break;
|
|
// }
|
|
// }
|
|
//
|
|
// SetAndRestoreValue<BeMCBlock*> prevBlock(mActiveBlock, mcBlock);
|
|
// SetAndRestoreValue<int*> prevInstIdxRef(mInsertInstIdxRef, &phiInstIdx);
|
|
//
|
|
// auto resultType = value->GetType();
|
|
// auto result = AllocVirtualReg(resultType);
|
|
// auto vregInfo = GetVRegInfo(result);
|
|
// vregInfo->mHasDynLife = true; // No specific 'def' location
|
|
// mValueToOperand[value] = result;
|
|
//
|
|
// if (resultType->mTypeCode == BeTypeCode_Boolean)
|
|
// {
|
|
// CreateDefineVReg(result);
|
|
//
|
|
// CeOperand falseLabel = CeOperand::FromLabel(mCurLabelIdx++);
|
|
// CeOperand trueLabel = CeOperand::FromLabel(mCurLabelIdx++);
|
|
// CeOperand endLabel = CeOperand::FromLabel(mCurLabelIdx++);
|
|
// CreateCondBr(mActiveBlock, operand, trueLabel, falseLabel);
|
|
//
|
|
// AllocInst(BeMCInstKind_Label, falseLabel);
|
|
// AllocInst(BeMCInstKind_Mov, result, CeOperand::FromImmediate(0));
|
|
// AllocInst(BeMCInstKind_Br, endLabel);
|
|
// AllocInst(BeMCInstKind_Label, trueLabel);
|
|
// AllocInst(BeMCInstKind_Mov, result, CeOperand::FromImmediate(1));
|
|
// AllocInst(BeMCInstKind_Label, endLabel);
|
|
// }
|
|
// else
|
|
// {
|
|
// // Attempt to find common ancestor to insert a 'def' at
|
|
// SizedArray<BeMCBlock*, 16> blockSearch;
|
|
// blockSearch.reserve(phi->mValues.size());
|
|
// BeMCBlock* lowestBlock = NULL;
|
|
// for (auto& phiValue : phi->mValues)
|
|
// {
|
|
// if ((lowestBlock == NULL) || (phiValue.mBlockFrom->mBlockIdx < lowestBlock->mBlockIdx))
|
|
// lowestBlock = phiValue.mBlockFrom;
|
|
// blockSearch.push_back(phiValue.mBlockFrom);
|
|
// }
|
|
// while (true)
|
|
// {
|
|
// bool allMatched = true;
|
|
// bool didWork = false;
|
|
// for (int searchIdx = 0; searchIdx < (int)blockSearch.size(); searchIdx++)
|
|
// {
|
|
// auto& blockRef = blockSearch[searchIdx];
|
|
// if (blockRef != lowestBlock)
|
|
// {
|
|
// allMatched = false;
|
|
//
|
|
// for (auto& pred : blockRef->mPreds)
|
|
// {
|
|
// // Try find a block closer to start, but not below the current lowestBlock
|
|
// if ((pred->mBlockIdx >= lowestBlock->mBlockIdx) && (pred->mBlockIdx < blockRef->mBlockIdx))
|
|
// {
|
|
// blockRef = pred;
|
|
// didWork = true;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// if (allMatched)
|
|
// {
|
|
// SetAndRestoreValue<BeMCBlock*> prevActiveBlock(mActiveBlock, lowestBlock);
|
|
// SetAndRestoreValue<int*> prevInstIdxRef(mInsertInstIdxRef, NULL);
|
|
// auto inst = CreateDefineVReg(result);
|
|
// inst->mVRegsInitialized = NULL;
|
|
// inst->mDbgLoc = NULL;
|
|
// break;
|
|
// }
|
|
//
|
|
// if (!didWork)
|
|
// {
|
|
// BeMCBlock* nextLowestBlock = NULL;
|
|
//
|
|
// // Find the next candidate block
|
|
// for (auto& blockRef : blockSearch)
|
|
// {
|
|
// for (auto& pred : blockRef->mPreds)
|
|
// {
|
|
// if (pred->mBlockIdx < lowestBlock->mBlockIdx)
|
|
// {
|
|
// if ((nextLowestBlock == NULL) || (pred->mBlockIdx > nextLowestBlock->mBlockIdx))
|
|
// nextLowestBlock = pred;
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// if (nextLowestBlock == NULL)
|
|
// break;
|
|
// lowestBlock = nextLowestBlock;
|
|
// }
|
|
// }
|
|
//
|
|
// CeOperand doneLabel = CeOperand::FromLabel(mCurLabelIdx++);
|
|
// CreatePhiAssign(mActiveBlock, operand, result, doneLabel);
|
|
//
|
|
// // Don't use an explicit dbgLoc
|
|
// SetAndRestoreValue<BeDbgLoc*> prevDbgLoc(mCurDbgLoc, NULL);
|
|
// AllocInst(BeMCInstKind_Label, doneLabel);
|
|
// }
|
|
//
|
|
// return result;
|
|
// }
|
|
//
|
|
// if ((operand.mKind == CeOperandKind_CmpResult) && (!allowMetaResult))
|
|
// {
|
|
// auto& cmpResult = mCmpResults[operand.mCmpResultIdx];
|
|
// if (cmpResult.mResultVRegIdx == -1)
|
|
// {
|
|
// // Create the vreg now, and insert the CmpToBool during legalization
|
|
// BeType* boolType = mModule->mContext->GetPrimitiveType(BeTypeCode_Boolean);
|
|
// operand = AllocVirtualReg(boolType);
|
|
// cmpResult.mResultVRegIdx = operand.mVRegIdx;
|
|
//
|
|
// auto vregInfo = GetVRegInfo(operand);
|
|
// vregInfo->mDefOnFirstUse = true;
|
|
// }
|
|
//
|
|
// operand = CeOperand::FromVReg(cmpResult.mResultVRegIdx);
|
|
// }
|
|
//
|
|
// if ((operand.mKind == CeOperandKind_NotResult) && (!allowMetaResult))
|
|
// {
|
|
// auto mcValue = GetOperand(operand.mNotResult->mValue, false, allowFail);
|
|
//
|
|
// operand = AllocVirtualReg(GetType(mcValue));
|
|
// CreateDefineVReg(operand);
|
|
// AllocInst(BeMCInstKind_Mov, operand, mcValue);
|
|
//
|
|
// CeOperand xorVal;
|
|
// xorVal.mKind = CeOperandKind_Immediate_i8;
|
|
// xorVal.mImmediate = 0x1;
|
|
// AllocInst(BeMCInstKind_Xor, operand, xorVal);
|
|
// }
|
|
|
|
return operand;
|
|
}
|
|
|
|
CeSizeClass CeBuilder::GetSizeClass(int size)
|
|
{
|
|
switch (size)
|
|
{
|
|
case 1:
|
|
return CeSizeClass_8;
|
|
case 2:
|
|
return CeSizeClass_16;
|
|
case 4:
|
|
return CeSizeClass_32;
|
|
case 8:
|
|
return CeSizeClass_64;
|
|
default:
|
|
return CeSizeClass_X;
|
|
}
|
|
}
|
|
|
|
void CeBuilder::HandleParams()
|
|
{
|
|
auto beModule = mBeFunction->mModule;
|
|
// int regIdxOfs = 0;
|
|
// int paramOfs = 0;
|
|
auto retType = mBeFunction->GetFuncType()->mReturnType;
|
|
|
|
int frameOffset = 0;
|
|
|
|
if (retType->mSize > 0)
|
|
{
|
|
mReturnVal.mKind = CeOperandKind_AllocaAddr;
|
|
mReturnVal.mFrameOfs = frameOffset;
|
|
mReturnVal.mType = retType;
|
|
|
|
frameOffset += retType->mSize;
|
|
}
|
|
|
|
int paramOfs = 0;
|
|
//for (int paramIdx = (int)mBeFunction->mParams.size() - 1; paramIdx >= 0; paramIdx--)
|
|
|
|
for (int paramIdx = 0; paramIdx < mBeFunction->mParams.size(); paramIdx++)
|
|
{
|
|
auto funcType = mBeFunction->GetFuncType();
|
|
auto& typeParam = funcType->mParams[paramIdx + paramOfs];
|
|
auto& param = mBeFunction->mParams[paramIdx + paramOfs];
|
|
auto beArg = beModule->GetArgument(paramIdx + paramOfs);
|
|
auto paramType = typeParam.mType;
|
|
|
|
CeOperand ceOperand;
|
|
ceOperand.mKind = CeOperandKind_FrameOfs;
|
|
ceOperand.mFrameOfs = frameOffset;
|
|
ceOperand.mType = paramType;
|
|
|
|
frameOffset += paramType->mSize;
|
|
|
|
mValueToOperand[beArg] = ceOperand;
|
|
}
|
|
}
|
|
|
|
void CeBuilder::Build()
|
|
{
|
|
mCeFunction->mFailed = true;
|
|
|
|
auto methodInstance = mCeFunction->mMethodInstance;
|
|
auto methodDef = methodInstance->mMethodDef;
|
|
|
|
BfMethodInstance dupMethodInstance = *methodInstance;
|
|
if (dupMethodInstance.mMethodInfoEx != NULL)
|
|
{
|
|
dupMethodInstance.mMethodInfoEx = new BfMethodInfoEx();
|
|
*dupMethodInstance.mMethodInfoEx = *(methodInstance->mMethodInfoEx);
|
|
for (auto genericParam : dupMethodInstance.mMethodInfoEx->mGenericParams)
|
|
genericParam->AddRef();
|
|
dupMethodInstance.mMethodInfoEx->mMethodCustomAttributes = NULL;
|
|
}
|
|
dupMethodInstance.mHasBeenProcessed = false;
|
|
dupMethodInstance.mIRFunction = BfIRValue();
|
|
//dupMethodInstance.mIRFunction = workItem.mFunc;
|
|
dupMethodInstance.mMethodProcessRequest = NULL;
|
|
dupMethodInstance.mIsReified = true;
|
|
dupMethodInstance.mHotMethod = NULL;
|
|
|
|
mCeMachine->mCeModule->ProcessMethod(&dupMethodInstance, true);
|
|
|
|
if (!dupMethodInstance.mIRFunction)
|
|
{
|
|
mCeFunction->mFailed = true;
|
|
return;
|
|
}
|
|
|
|
auto irCodeGen = mCeMachine->mCeModule->mBfIRBuilder->mBeIRCodeGen;
|
|
mIntPtrType = irCodeGen->mBeContext->GetPrimitiveType((mPtrSize == 4) ? BeTypeCode_Int32 : BeTypeCode_Int64);
|
|
mBeFunction = (BeFunction*)irCodeGen->GetBeValue(dupMethodInstance.mIRFunction.mId);
|
|
|
|
mCeFunction->mName = mBeFunction->mName;
|
|
mCeMachine->mNamedFunctionMap[mCeFunction->mName] = mCeFunction;
|
|
|
|
auto beModule = irCodeGen->mBeModule;
|
|
SetAndRestoreValue<BeFunction*> prevBeFunction(beModule->mActiveFunction, mBeFunction);
|
|
|
|
// Create blocks
|
|
for (int blockIdx = 0; blockIdx < (int)mBeFunction->mBlocks.size(); blockIdx++)
|
|
{
|
|
auto beBlock = mBeFunction->mBlocks[blockIdx];
|
|
|
|
CeBlock ceBlock;
|
|
mBlocks.Add(ceBlock);
|
|
|
|
CeOperand ceOperand;
|
|
ceOperand.mKind = CeOperandKind_Block;
|
|
ceOperand.mBlockIdx = blockIdx;
|
|
mValueToOperand[beBlock] = ceOperand;
|
|
}
|
|
|
|
// Instruction pre-pass
|
|
for (int blockIdx = 0; blockIdx < (int)mBeFunction->mBlocks.size(); blockIdx++)
|
|
{
|
|
auto beBlock = mBeFunction->mBlocks[blockIdx];
|
|
auto& ceBlock = mBlocks[blockIdx];
|
|
|
|
for (int instIdx = 0; instIdx < (int)beBlock->mInstructions.size(); instIdx++)
|
|
{
|
|
auto inst = beBlock->mInstructions[instIdx];
|
|
|
|
int instType = inst->GetTypeId();
|
|
|
|
switch (instType)
|
|
{
|
|
case BePhiInst::TypeId:
|
|
{
|
|
auto castedInst = (BePhiInst*)inst;
|
|
|
|
auto resultType = castedInst->GetType();
|
|
|
|
auto phiResult = FrameAlloc(resultType);
|
|
mValueToOperand[castedInst] = phiResult;
|
|
|
|
for (auto& phiIncoming : castedInst->mIncoming)
|
|
{
|
|
auto incomingBlockOpr = GetOperand(phiIncoming->mBlock);
|
|
auto& incomingBlock = mBlocks[incomingBlockOpr.mBlockIdx];
|
|
|
|
CePhiOutgoing phiOutgoing;
|
|
phiOutgoing.mPhiValue = phiIncoming->mValue;
|
|
phiOutgoing.mPhiInst = castedInst;
|
|
phiOutgoing.mPhiBlockIdx = blockIdx;
|
|
incomingBlock.mPhiOutgoing.Add(phiOutgoing);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Primary instruction pass
|
|
BeDbgLoc* prevEmitDbgPos = NULL;
|
|
bool inHeadAlloca = true;
|
|
for (int blockIdx = 0; blockIdx < (int)mBeFunction->mBlocks.size(); blockIdx++)
|
|
{
|
|
auto beBlock = mBeFunction->mBlocks[blockIdx];
|
|
auto ceBlock = &mBlocks[blockIdx];
|
|
|
|
ceBlock->mEmitOfs = GetCodePos();
|
|
|
|
if (blockIdx == 0)
|
|
HandleParams();
|
|
|
|
for (int instIdx = 0; instIdx < (int)beBlock->mInstructions.size(); instIdx++)
|
|
{
|
|
auto inst = beBlock->mInstructions[instIdx];
|
|
CeOperand result;
|
|
|
|
int startCodePos = GetCodePos();
|
|
|
|
mCurDbgLoc = inst->mDbgLoc;
|
|
|
|
int instType = inst->GetTypeId();
|
|
|
|
switch (instType)
|
|
{
|
|
case BeAllocaInst::TypeId:
|
|
case BeNumericCastInst::TypeId:
|
|
case BeBitCastInst::TypeId:
|
|
break;
|
|
default:
|
|
inHeadAlloca = false;
|
|
break;
|
|
}
|
|
|
|
switch (instType)
|
|
{
|
|
case BeEnsureInstructionAtInst::TypeId:
|
|
case BeNopInst::TypeId:
|
|
case BeDbgDeclareInst::TypeId:
|
|
case BeLifetimeStartInst::TypeId:
|
|
case BeLifetimeEndInst::TypeId:
|
|
case BeLifetimeExtendInst::TypeId:
|
|
case BeValueScopeStartInst::TypeId:
|
|
case BeValueScopeEndInst::TypeId:
|
|
break;
|
|
case BeUnreachableInst::TypeId:
|
|
Emit(CeOp_InvalidOp);
|
|
break;
|
|
case BeAllocaInst::TypeId:
|
|
{
|
|
auto castedInst = (BeAllocaInst*)inst;
|
|
int size = castedInst->mType->mSize;
|
|
bool isAligned16 = false;
|
|
int align = castedInst->mAlign;
|
|
BeType* allocType = castedInst->mType;
|
|
bool preservedVolatiles = false;
|
|
bool doPtrCast = false;
|
|
|
|
if (inHeadAlloca)
|
|
{
|
|
mFrameSize += size;
|
|
|
|
result.mKind = CeOperandKind_AllocaAddr;
|
|
result.mFrameOfs = -mFrameSize;
|
|
result.mType = castedInst->mType;
|
|
}
|
|
else
|
|
{
|
|
Fail("Non-head alloca");
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case BeLoadInst::TypeId:
|
|
{
|
|
auto castedInst = (BeLoadInst*)inst;
|
|
auto ceTarget = GetOperand(castedInst->mTarget, true);
|
|
|
|
if (ceTarget.mKind == CeOperandKind_AllocaAddr)
|
|
{
|
|
result = ceTarget;
|
|
result.mKind = CeOperandKind_FrameOfs;
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(ceTarget.mType->IsPointer());
|
|
auto pointerType = (BePointerType*)ceTarget.mType;
|
|
auto elemType = pointerType->mElementType;
|
|
|
|
CeOperand refOperand = ceTarget;
|
|
refOperand.mType = elemType;
|
|
EmitSizedOp(CeOp_Load_8, refOperand, &result, true);
|
|
}
|
|
}
|
|
break;
|
|
case BeBinaryOpInst::TypeId:
|
|
{
|
|
auto castedInst = (BeBinaryOpInst*)inst;
|
|
auto ceLHS = GetOperand(castedInst->mLHS);
|
|
auto ceRHS = GetOperand(castedInst->mRHS);
|
|
|
|
// if (castedInst->mOpKind == BeBinaryOpKind_Subtract)
|
|
// {
|
|
// if (((mcLHS.IsImmediateFloat()) && (mcLHS.GetImmediateDouble() == 0.0)) ||
|
|
// ((mcLHS.IsImmediateInt()) && (mcLHS.mImmediate == 0)))
|
|
// {
|
|
// auto castedInst = (BeNumericCastInst*)inst;
|
|
//
|
|
// result = AllocVirtualReg(GetType(mcRHS));
|
|
// CreateDefineVReg(result);
|
|
// AllocInst(BeMCInstKind_Mov, result, mcRHS);
|
|
// AllocInst(BeMCInstKind_Neg, result);
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
switch (castedInst->mOpKind)
|
|
{
|
|
case BeBinaryOpKind_Add:
|
|
EmitBinaryOp(CeOp_Add_I8, CeOp_Add_F32, ceLHS, ceRHS, result);
|
|
break;
|
|
default:
|
|
Fail("Invalid binary op");
|
|
}
|
|
|
|
// auto type = GetType(mcLHS);
|
|
//
|
|
// switch (castedInst->mOpKind)
|
|
// {
|
|
// case BeBinaryOpKind_Add: result = AllocBinaryOp(BeMCInstKind_Add, mcLHS, mcRHS, BeMCBinIdentityKind_Any_IsZero); break;
|
|
// case BeBinaryOpKind_Subtract: result = AllocBinaryOp(BeMCInstKind_Sub, mcLHS, mcRHS, BeMCBinIdentityKind_Right_IsZero); break;
|
|
// case BeBinaryOpKind_Multiply: result = AllocBinaryOp(BeMCInstKind_IMul, mcLHS, mcRHS, BeMCBinIdentityKind_Any_IsOne); break;
|
|
// case BeBinaryOpKind_SDivide: result = AllocBinaryOp(BeMCInstKind_IDiv, mcLHS, mcRHS, BeMCBinIdentityKind_Right_IsOne); break;
|
|
// case BeBinaryOpKind_UDivide: result = AllocBinaryOp(BeMCInstKind_Div, mcLHS, mcRHS, BeMCBinIdentityKind_Right_IsOne); break;
|
|
// case BeBinaryOpKind_SModulus: result = AllocBinaryOp(BeMCInstKind_IRem, mcLHS, mcRHS, type->IsFloat() ? BeMCBinIdentityKind_None : BeMCBinIdentityKind_Right_IsOne_Result_Zero); break;
|
|
// case BeBinaryOpKind_UModulus: result = AllocBinaryOp(BeMCInstKind_Rem, mcLHS, mcRHS, type->IsFloat() ? BeMCBinIdentityKind_None : BeMCBinIdentityKind_Right_IsOne_Result_Zero); break;
|
|
// case BeBinaryOpKind_BitwiseAnd: result = AllocBinaryOp(BeMCInstKind_And, mcLHS, mcRHS, BeMCBinIdentityKind_None); break;
|
|
// case BeBinaryOpKind_BitwiseOr: result = AllocBinaryOp(BeMCInstKind_Or, mcLHS, mcRHS, BeMCBinIdentityKind_Any_IsZero); break;
|
|
// case BeBinaryOpKind_ExclusiveOr: result = AllocBinaryOp(BeMCInstKind_Xor, mcLHS, mcRHS, BeMCBinIdentityKind_Any_IsZero); break;
|
|
// case BeBinaryOpKind_LeftShift: result = AllocBinaryOp(BeMCInstKind_Shl, mcLHS, mcRHS, BeMCBinIdentityKind_Any_IsZero); break;
|
|
// case BeBinaryOpKind_RightShift: result = AllocBinaryOp(BeMCInstKind_Shr, mcLHS, mcRHS, BeMCBinIdentityKind_Any_IsZero); break;
|
|
// case BeBinaryOpKind_ARightShift: result = AllocBinaryOp(BeMCInstKind_Sar, mcLHS, mcRHS, BeMCBinIdentityKind_Any_IsZero); break;
|
|
// }
|
|
}
|
|
break;
|
|
case BeNumericCastInst::TypeId:
|
|
{
|
|
auto castedInst = (BeNumericCastInst*)inst;
|
|
auto ceValue = GetOperand(castedInst->mValue);
|
|
auto fromType = ceValue.mType;
|
|
if (fromType == castedInst->mToType)
|
|
{
|
|
// If it's just a sign change then leave it alone
|
|
result = ceValue;
|
|
}
|
|
else
|
|
{
|
|
auto toType = castedInst->mToType;
|
|
if ((toType->IsIntable()) && (fromType->IsIntable()) && (toType->mSize < fromType->mSize))
|
|
{
|
|
// For truncating values, no actual instructions are needed
|
|
result = ceValue;
|
|
result.mType = toType;
|
|
}
|
|
else
|
|
{
|
|
bool doSignExtension = (toType->IsIntable()) && (fromType->IsIntable()) && (toType->mSize > fromType->mSize) && (castedInst->mToSigned) && (castedInst->mValSigned);
|
|
// if ((toType->IsFloat()) && (fromType->IsIntable()) && (castedInst->mValSigned))
|
|
// doSignExtension = true;
|
|
|
|
result = FrameAlloc(toType);
|
|
|
|
CeOp op = CeOp_InvalidOp;
|
|
|
|
if (doSignExtension)
|
|
{
|
|
switch (fromType->mTypeCode)
|
|
{
|
|
case BeTypeCode_Int32:
|
|
switch (toType->mTypeCode)
|
|
{
|
|
case BeTypeCode_Int64:
|
|
op = CeOp_Conv_I32_I64;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
|
|
if (op == CeOp_InvalidOp)
|
|
Fail("Invalid conversion op");
|
|
|
|
Emit(op);
|
|
EmitFrameOffset(result);
|
|
EmitFrameOffset(ceValue);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case BeStoreInst::TypeId:
|
|
{
|
|
auto castedInst = (BeStoreInst*)inst;
|
|
auto mcVal = GetOperand(castedInst->mVal);
|
|
auto mcPtr = GetOperand(castedInst->mPtr, true);
|
|
|
|
if (mcPtr.mKind == CeOperandKind_AllocaAddr)
|
|
{
|
|
EmitSizedOp(CeOp_Move_8, mcVal, NULL, true);
|
|
Emit((int32)mcPtr.mFrameOfs);
|
|
}
|
|
else
|
|
{
|
|
EmitSizedOp(CeOp_Store_8, mcVal, NULL, true);
|
|
EmitFrameOffset(mcPtr);
|
|
}
|
|
}
|
|
break;
|
|
case BeRetInst::TypeId:
|
|
{
|
|
auto castedInst = (BeRetInst*)inst;
|
|
if (castedInst->mRetValue != NULL)
|
|
{
|
|
auto mcVal = GetOperand(castedInst->mRetValue);
|
|
|
|
BF_ASSERT(mReturnVal.mKind == CeOperandKind_AllocaAddr);
|
|
EmitSizedOp(CeOp_Move_8, mcVal, NULL, true);
|
|
Emit((int32)mReturnVal.mFrameOfs);
|
|
}
|
|
Emit(CeOp_Ret);
|
|
}
|
|
break;
|
|
case BeCmpInst::TypeId:
|
|
{
|
|
auto castedInst = (BeCmpInst*)inst;
|
|
auto ceLHS = GetOperand(castedInst->mLHS);
|
|
auto ceRHS = GetOperand(castedInst->mRHS);
|
|
|
|
CeOp iOp = CeOp_InvalidOp;
|
|
CeOp fOp = CeOp_InvalidOp;
|
|
|
|
switch (castedInst->mCmpKind)
|
|
{
|
|
case BeCmpKind_SLT:
|
|
iOp = CeOp_Cmp_SLT_I8;
|
|
fOp = CeOp_Cmp_SLT_F32;
|
|
break;
|
|
}
|
|
|
|
if (iOp == CeOp_InvalidOp)
|
|
{
|
|
Fail("Invalid cmp");
|
|
break;
|
|
}
|
|
|
|
auto boolType = inst->GetType();
|
|
result = FrameAlloc(boolType);
|
|
EmitBinaryOp(iOp, fOp, ceLHS, ceRHS, result);
|
|
|
|
// auto mcInst = AllocInst(BeMCInstKind_Cmp, mcLHS, mcRHS);
|
|
//
|
|
// auto cmpResultIdx = (int)mCmpResults.size();
|
|
// BeCmpResult cmpResult;
|
|
// cmpResult.mCmpKind = castedInst->mCmpKind;
|
|
// mCmpResults.push_back(cmpResult);
|
|
//
|
|
// result.mKind = BeMCOperandKind_CmpResult;
|
|
// result.mCmpResultIdx = cmpResultIdx;
|
|
//
|
|
// mcInst->mResult = result;
|
|
}
|
|
break;
|
|
case BeBrInst::TypeId:
|
|
{
|
|
auto castedInst = (BeBrInst*)inst;
|
|
auto targetBlock = GetOperand(castedInst->mTargetBlock);
|
|
|
|
BF_ASSERT(targetBlock.mKind == CeOperandKind_Block);
|
|
|
|
FlushPhi(ceBlock, targetBlock.mBlockIdx);
|
|
|
|
if (targetBlock.mBlockIdx == blockIdx + 1)
|
|
{
|
|
// Do nothing - just continuing to next block
|
|
break;
|
|
}
|
|
|
|
EmitJump(CeOp_Jmp, targetBlock);
|
|
}
|
|
break;
|
|
case BeCondBrInst::TypeId:
|
|
{
|
|
auto castedInst = (BeCondBrInst*)inst;
|
|
auto testVal = GetOperand(castedInst->mCond, true);
|
|
auto trueBlock = GetOperand(castedInst->mTrueBlock);
|
|
auto falseBlock = GetOperand(castedInst->mFalseBlock);
|
|
|
|
FlushPhi(ceBlock, trueBlock.mBlockIdx);
|
|
|
|
EmitJump(CeOp_JmpIf, trueBlock);
|
|
EmitFrameOffset(testVal);
|
|
|
|
FlushPhi(ceBlock, falseBlock.mBlockIdx);
|
|
|
|
EmitJump(CeOp_Jmp, falseBlock);
|
|
}
|
|
break;
|
|
case BePhiInst::TypeId:
|
|
result = GetOperand(inst);
|
|
BF_ASSERT(result);
|
|
break;
|
|
case BeNegInst::TypeId:
|
|
{
|
|
auto castedInst = (BeNumericCastInst*)inst;
|
|
auto ceValue = GetOperand(castedInst->mValue);
|
|
EmitUnaryOp(CeOp_Neg_I8, CeOp_Neg_F32, ceValue, result);
|
|
}
|
|
break;
|
|
case BeSwitchInst::TypeId:
|
|
{
|
|
auto castedInst = (BeSwitchInst*)inst;
|
|
|
|
std::stable_sort(castedInst->mCases.begin(), castedInst->mCases.end(), [&](const BeSwitchCase& lhs, const BeSwitchCase& rhs)
|
|
{
|
|
return lhs.mValue->mInt64 < rhs.mValue->mInt64;
|
|
});
|
|
|
|
int numVals = castedInst->mCases.size();
|
|
|
|
if (numVals > 0)
|
|
{
|
|
EmitBinarySwitchSection(castedInst, 0, castedInst->mCases.size());
|
|
}
|
|
|
|
auto mcDefaultBlock = GetOperand(castedInst->mDefaultBlock);
|
|
EmitJump(CeOp_Jmp, mcDefaultBlock);
|
|
}
|
|
break;
|
|
case BeCallInst::TypeId:
|
|
{
|
|
auto castedInst = (BeCallInst*)inst;
|
|
CeOperand ceFunc;
|
|
BeType* returnType = NULL;
|
|
bool isVarArg = false;
|
|
|
|
bool useAltArgs = false;
|
|
SizedArray<BeValue*, 6> args;
|
|
|
|
int callIdx = -1;
|
|
|
|
if (auto intrin = BeValueDynCast<BeIntrinsic>(castedInst->mFunc))
|
|
{
|
|
Fail("Intrinsics not allowed");
|
|
}
|
|
else if (auto beFunction = BeValueDynCast<BeFunction>(castedInst->mFunc))
|
|
{
|
|
int* callIdxPtr = NULL;
|
|
if (mFunctionMap.TryAdd(beFunction, NULL, &callIdxPtr))
|
|
{
|
|
CeCallEntry callEntry;
|
|
callEntry.mFunctionName = beFunction->mName;
|
|
*callIdxPtr = (int)mCeFunction->mCallTable.size();
|
|
mCeFunction->mCallTable.Add(callEntry);
|
|
}
|
|
|
|
callIdx = *callIdxPtr;
|
|
|
|
int stackAdjust = 0;
|
|
|
|
auto beFuncType = beFunction->GetFuncType();
|
|
if (beFuncType->mReturnType->mSize > 0)
|
|
{
|
|
Emit(CeOp_AdjustSP);
|
|
Emit((int32)-beFuncType->mReturnType->mSize);
|
|
stackAdjust += beFuncType->mReturnType->mSize;
|
|
}
|
|
|
|
Emit(CeOp_Call);
|
|
Emit((int32)callIdx);
|
|
|
|
if (stackAdjust > 0)
|
|
{
|
|
Emit(CeOp_AdjustSP);
|
|
Emit(stackAdjust);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Fail("Cannot call through function pointers");
|
|
// auto funcPtrType = castedInst->mFunc->GetType();
|
|
// if (funcPtrType->IsPointer())
|
|
// {
|
|
// auto elementType = ((BePointerType*)funcPtrType)->mElementType;
|
|
// if (elementType->mTypeCode == BeTypeCode_Function)
|
|
// {
|
|
// isVarArg = ((BeFunctionType*)elementType)->mIsVarArg;
|
|
// }
|
|
// }
|
|
//
|
|
// returnType = castedInst->GetType();
|
|
// ceFunc = GetOperand(castedInst->mFunc);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
Fail("Unhandled instruction");
|
|
return;
|
|
}
|
|
|
|
if (result.mKind != CeOperandKind_None)
|
|
mValueToOperand[inst] = result;
|
|
|
|
if ((startCodePos != GetCodePos()) && (prevEmitDbgPos != mCurDbgLoc))
|
|
{
|
|
prevEmitDbgPos = mCurDbgLoc;
|
|
|
|
int fileIdx = -1;
|
|
BeDbgFile* dbgFile = NULL;
|
|
if (mCurDbgLoc != NULL)
|
|
{
|
|
auto dbgFile = mCurDbgLoc->GetDbgFile();
|
|
int* valuePtr = NULL;
|
|
if (mDbgFileMap.TryAdd(dbgFile, NULL, &valuePtr))
|
|
{
|
|
fileIdx = (int)mCeFunction->mFiles.size();
|
|
mCeFunction->mFiles.Add(dbgFile->mFileName);
|
|
*valuePtr = fileIdx;
|
|
}
|
|
else
|
|
fileIdx = *valuePtr;
|
|
}
|
|
|
|
CeEmitEntry emitEntry;
|
|
emitEntry.mCodePos = startCodePos;
|
|
emitEntry.mFile = fileIdx;
|
|
if (mCurDbgLoc != NULL)
|
|
{
|
|
emitEntry.mLine = mCurDbgLoc->mLine;
|
|
emitEntry.mColumn = mCurDbgLoc->mColumn;
|
|
}
|
|
else
|
|
{
|
|
emitEntry.mLine = -1;
|
|
emitEntry.mColumn = -1;
|
|
}
|
|
mCeFunction->mEmitTable.Add(emitEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto& jumpEntry : mJumpTable)
|
|
{
|
|
auto& ceBlock = mBlocks[jumpEntry.mBlockIdx];
|
|
*((int32*)(&mCeFunction->mCode[0] + jumpEntry.mEmitPos)) = ceBlock.mEmitOfs - jumpEntry.mEmitPos - 4;
|
|
}
|
|
|
|
mCeFunction->mFailed = false;
|
|
mCeFunction->mFrameSize = mFrameSize;
|
|
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
CeMachine::CeMachine(BfCompiler* compiler)
|
|
{
|
|
mCompiler = compiler;
|
|
mCeModule = NULL;
|
|
mStackMin = NULL;
|
|
mRevision = 0;
|
|
}
|
|
|
|
CeMachine::~CeMachine()
|
|
{
|
|
delete mCeModule;
|
|
}
|
|
|
|
void CeMachine::Init()
|
|
{
|
|
mCeModule = new BfModule(mCompiler->mContext, "__constEval");
|
|
mCeModule->mIsSpecialModule = true;
|
|
//mCeModule->mIsScratchModule = true;
|
|
mCeModule->mIsConstModule = true;
|
|
mCeModule->mIsReified = true;
|
|
mCeModule->Init();
|
|
|
|
mCeModule->mBfIRBuilder = new BfIRBuilder(mCeModule);
|
|
mCeModule->mBfIRBuilder->mDbgVerifyCodeGen = true;
|
|
mCeModule->FinishInit();
|
|
mCeModule->mBfIRBuilder->mHasDebugInfo = false; // Only line info
|
|
mCeModule->mBfIRBuilder->mIgnoreWrites = false;
|
|
mCeModule->mWantsIRIgnoreWrites = false;
|
|
}
|
|
|
|
void CeMachine::CompileStarted()
|
|
{
|
|
mRevision++;
|
|
if (mCeModule != NULL)
|
|
{
|
|
delete mCeModule;
|
|
mCeModule = NULL;
|
|
}
|
|
}
|
|
|
|
void CeMachine::RemoveMethod(BfMethodInstance* methodInstance)
|
|
{
|
|
auto itr = mFunctions.Find(methodInstance);
|
|
auto ceFunction = itr->mValue;
|
|
mNamedFunctionMap.Remove(ceFunction->mName);
|
|
mFunctions.Remove(itr);
|
|
}
|
|
|
|
int CeMachine::GetConstantSize(BfConstant* constant)
|
|
{
|
|
switch (constant->mTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_Boolean:
|
|
case BfTypeCode_Char8:
|
|
return 1;
|
|
case BfTypeCode_Int16:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_Char16:
|
|
return 2;
|
|
case BfTypeCode_Int32:
|
|
case BfTypeCode_UInt32:
|
|
case BfTypeCode_Char32:
|
|
return 4;
|
|
case BfTypeCode_Int64:
|
|
case BfTypeCode_UInt64:
|
|
return 8;
|
|
case BfTypeCode_NullPtr:
|
|
return 4;
|
|
case BfTypeCode_Float:
|
|
return 4;
|
|
case BfTypeCode_Double:
|
|
return 8;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
#define CE_GETC(T) *((T*)(ptr += sizeof(T)) - 1)
|
|
|
|
void CeMachine::WriteConstant(uint8* ptr, BfConstant* constant)
|
|
{
|
|
switch (constant->mTypeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
case BfTypeCode_UInt8:
|
|
case BfTypeCode_Boolean:
|
|
case BfTypeCode_Char8:
|
|
CE_GETC(int8) = constant->mInt8;
|
|
return;
|
|
case BfTypeCode_Int16:
|
|
case BfTypeCode_UInt16:
|
|
case BfTypeCode_Char16:
|
|
CE_GETC(int16) = constant->mInt16;
|
|
return;
|
|
case BfTypeCode_Int32:
|
|
case BfTypeCode_UInt32:
|
|
case BfTypeCode_Char32:
|
|
CE_GETC(int32) = constant->mInt32;
|
|
return;
|
|
case BfTypeCode_Int64:
|
|
case BfTypeCode_UInt64:
|
|
CE_GETC(int64) = constant->mInt64;
|
|
return;
|
|
case BfTypeCode_NullPtr:
|
|
CE_GETC(int32) = 0;
|
|
return;
|
|
case BfTypeCode_Float:
|
|
CE_GETC(float) = (float)constant->mDouble;
|
|
return;
|
|
case BfTypeCode_Double:
|
|
CE_GETC(double) = constant->mDouble;
|
|
return;
|
|
}
|
|
}
|
|
|
|
#define CE_GETINST(T) *((T*)(instPtr += sizeof(T)) - 1)
|
|
#define CE_GETFRAME(T) *(T*)(framePtr + *((int32*)(instPtr += sizeof(int32)) - 1))
|
|
#define CEOP_BIN(OP, T) \
|
|
{ \
|
|
auto& result = CE_GETFRAME(T); \
|
|
auto lhs = CE_GETFRAME(T); \
|
|
auto rhs = CE_GETFRAME(T); \
|
|
result = lhs OP rhs; \
|
|
}
|
|
#define CEOP_UNARY(OP, T) \
|
|
{ \
|
|
auto& result = CE_GETFRAME(T); \
|
|
auto val = CE_GETFRAME(T); \
|
|
result = OP val; \
|
|
}
|
|
#define CEOP_CMP(OP, T) \
|
|
{ \
|
|
auto& result = CE_GETFRAME(bool); \
|
|
auto lhs = CE_GETFRAME(T); \
|
|
auto rhs = CE_GETFRAME(T); \
|
|
result = lhs OP rhs; \
|
|
}
|
|
#define CE_CAST(TFROM, TTO) \
|
|
{ \
|
|
auto& result = CE_GETFRAME(TTO); \
|
|
auto val = CE_GETFRAME(TFROM); \
|
|
result = (TTO)val; \
|
|
}
|
|
#define CE_MOVE(T) \
|
|
{ \
|
|
auto val = CE_GETFRAME(T); \
|
|
auto& ptr = CE_GETFRAME(T); \
|
|
ptr = val; \
|
|
}
|
|
|
|
bool CeMachine::Execute()
|
|
{
|
|
uint8* memStart = &mMemory[0];
|
|
uint8* framePtr;
|
|
uint8* instPtr;
|
|
uint8* stackPtr;
|
|
//
|
|
{
|
|
auto& ceFrame = mCallStack.back();
|
|
instPtr = ceFrame.mInstPtr;
|
|
stackPtr = ceFrame.mStackPtr;
|
|
framePtr = ceFrame.mFramePtr;
|
|
framePtr = mCallStack.back().mFramePtr;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
CeOp op = CE_GETINST(CeOp);
|
|
switch (op)
|
|
{
|
|
case CeOp_Ret:
|
|
{
|
|
auto& ceFrame = mCallStack.back();
|
|
instPtr = ceFrame.mInstPtr;
|
|
stackPtr = ceFrame.mStackPtr;
|
|
framePtr = ceFrame.mFramePtr;
|
|
|
|
mCallStack.pop_back();
|
|
|
|
if (mCallStack.mSize == 0)
|
|
return true;
|
|
}
|
|
break;
|
|
case CeOp_Jmp:
|
|
{
|
|
auto relOfs = CE_GETINST(int32);
|
|
instPtr += relOfs;
|
|
}
|
|
break;
|
|
case CeOp_JmpIf:
|
|
{
|
|
auto relOfs = CE_GETINST(int32);
|
|
bool cond = CE_GETFRAME(bool);
|
|
if (cond)
|
|
instPtr += relOfs - 4;
|
|
}
|
|
break;
|
|
case CeOp_JmpIfNot:
|
|
{
|
|
auto relOfs = CE_GETINST(int32);
|
|
bool cond = CE_GETFRAME(bool);
|
|
if (!cond)
|
|
instPtr += relOfs - 4;
|
|
}
|
|
break;
|
|
case CeOp_FrameAddr64:
|
|
{
|
|
auto& result = CE_GETFRAME(int64);
|
|
auto addr = &CE_GETFRAME(uint8);
|
|
result = addr - memStart;
|
|
}
|
|
break;
|
|
case CeOp_Const_8:
|
|
{
|
|
auto& result = CE_GETFRAME(int8);
|
|
result = CE_GETINST(int8);
|
|
}
|
|
break;
|
|
case CeOp_Const_16:
|
|
{
|
|
auto& result = CE_GETFRAME(int16);
|
|
result = CE_GETINST(int16);
|
|
}
|
|
break;
|
|
case CeOp_Const_32:
|
|
{
|
|
auto& result = CE_GETFRAME(int32);
|
|
result = CE_GETINST(int32);
|
|
}
|
|
break;
|
|
case CeOp_Const_64:
|
|
{
|
|
auto& result = CE_GETFRAME(int64);
|
|
result = CE_GETINST(int64);
|
|
}
|
|
break;
|
|
case CeOp_Const_X:
|
|
{
|
|
auto resultPtr = &CE_GETFRAME(uint8);
|
|
int32 constSize = CE_GETINST(int32);
|
|
memcpy(resultPtr, instPtr, constSize);
|
|
instPtr += constSize;
|
|
}
|
|
break;
|
|
case CeOp_Load_32:
|
|
{
|
|
auto& result = CE_GETFRAME(uint32);
|
|
auto ceAddr = CE_GETFRAME(uint32);
|
|
// This check will fail for addresses < 256 (null pointer), or out-of-bounds the other direction
|
|
if ((ceAddr - 256) + sizeof(uint32) > (BF_CE_STACK_SIZE - 256))
|
|
{
|
|
//TODO: Throw error
|
|
return false;
|
|
}
|
|
result = *(uint32*)(memStart + ceAddr);
|
|
}
|
|
break;
|
|
case CeOp_Move_8:
|
|
CE_MOVE(int8);
|
|
break;
|
|
case CeOp_Move_16:
|
|
CE_MOVE(int16);
|
|
break;
|
|
case CeOp_Move_32:
|
|
CE_MOVE(int32);
|
|
break;
|
|
case CeOp_Move_64:
|
|
CE_MOVE(int64);
|
|
break;
|
|
case CeOp_Conv_I32_I64:
|
|
CE_CAST(int32, int64);
|
|
break;
|
|
case CeOp_Add_I32:
|
|
CEOP_BIN(+, int32);
|
|
break;
|
|
case CeOp_Add_I64:
|
|
CEOP_BIN(+, int64);
|
|
break;
|
|
case CeOp_Cmp_EQ_I32:
|
|
CEOP_CMP(==, int32);
|
|
break;
|
|
case CeOp_Cmp_SLT_I32:
|
|
CEOP_CMP(<, int32);
|
|
break;
|
|
case CeOp_Neg_I32:
|
|
CEOP_UNARY(-, int32);
|
|
break;
|
|
case CeOp_Neg_I64:
|
|
CEOP_UNARY(-, int64);
|
|
break;
|
|
default:
|
|
BF_FATAL("CeMachine op failure");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CeMachine::PrepareFunction(CeFunction* ceFunction)
|
|
{
|
|
if (mCeModule == NULL)
|
|
Init();
|
|
|
|
BF_ASSERT(!ceFunction->mInitialized);
|
|
ceFunction->mInitialized = true;
|
|
|
|
CeBuilder ceBuilder;
|
|
ceBuilder.mPtrSize = mCeModule->mCompiler->mSystem->mPtrSize;
|
|
ceBuilder.mCeMachine = this;
|
|
ceBuilder.mCeFunction = ceFunction;
|
|
ceBuilder.Build();
|
|
|
|
if (!ceFunction->mCode.IsEmpty())
|
|
{
|
|
CeDumpContext dumpCtx;
|
|
dumpCtx.mCeFunction = ceFunction;
|
|
dumpCtx.mStart = &ceFunction->mCode[0];
|
|
dumpCtx.mPtr = dumpCtx.mStart;
|
|
dumpCtx.mEnd = dumpCtx.mPtr + ceFunction->mCode.mSize;
|
|
dumpCtx.Dump();
|
|
|
|
OutputDebugStrF("Code for %s:\n%s\n", ceBuilder.mBeFunction->mName.c_str(), dumpCtx.mStr.c_str());
|
|
}
|
|
}
|
|
|
|
void CeMachine::ProcessWorkQueue()
|
|
{
|
|
while (!mWorkQueue.IsEmpty())
|
|
{
|
|
auto ceFunction = mWorkQueue.back();
|
|
mWorkQueue.pop_back();
|
|
PrepareFunction(ceFunction);
|
|
}
|
|
}
|
|
|
|
CeFunction* CeMachine::GetFunction(BfMethodInstance* methodInstance)
|
|
{
|
|
CeFunction** functionValuePtr = NULL;
|
|
CeFunction* ceFunction = NULL;
|
|
if (mFunctions.TryAdd(methodInstance, NULL, &functionValuePtr))
|
|
{
|
|
auto module = methodInstance->GetOwner()->mModule;
|
|
|
|
BF_ASSERT(!methodInstance->mInCEMachine);
|
|
methodInstance->mInCEMachine = true;
|
|
|
|
ceFunction = new CeFunction();
|
|
ceFunction->mMethodInstance = methodInstance;
|
|
|
|
*functionValuePtr = ceFunction;
|
|
}
|
|
else
|
|
ceFunction = *functionValuePtr;
|
|
return ceFunction;
|
|
}
|
|
|
|
void CeMachine::QueueMethod(BfMethodInstance* methodInstance)
|
|
{
|
|
auto ceFunction = GetFunction(methodInstance);
|
|
if (!ceFunction->mInitialized)
|
|
mWorkQueue.Add(ceFunction);
|
|
}
|
|
|
|
BfTypedValue CeMachine::Call(BfModule* module, BfMethodInstance* methodInstance, const BfSizedArray<BfIRValue>& args, CeEvalFlags flags)
|
|
{
|
|
// for (int argIdx = 0; argIdx < (int)args.size(); argIdx++)
|
|
// {
|
|
// auto arg = args[argIdx];
|
|
// if (!arg.IsConst())
|
|
// return BfTypedValue();
|
|
// }
|
|
|
|
// DISABLED
|
|
return BfTypedValue();
|
|
|
|
BF_ASSERT(mCallStack.IsEmpty());
|
|
|
|
auto methodDef = methodInstance->mMethodDef;
|
|
if (!methodDef->mIsStatic)
|
|
{
|
|
if (!methodInstance->GetOwner()->IsValueType())
|
|
return BfTypedValue();
|
|
}
|
|
|
|
CeFunction* ceFunction = GetFunction(methodInstance);
|
|
if (!ceFunction->mInitialized)
|
|
{
|
|
PrepareFunction(ceFunction);
|
|
ProcessWorkQueue();
|
|
}
|
|
BF_ASSERT(mWorkQueue.IsEmpty());
|
|
|
|
mMemory.Resize(BF_CE_STACK_SIZE);
|
|
auto stackPtr = &mMemory[0] + mMemory.mSize;
|
|
mStackMin = &mMemory[0];
|
|
|
|
for (int argIdx = (int)args.size() - 1; argIdx >= 0; argIdx--)
|
|
{
|
|
auto arg = args[argIdx];
|
|
if (!arg.IsConst())
|
|
return BfTypedValue();
|
|
|
|
auto constant = module->mBfIRBuilder->GetConstant(arg);
|
|
int constSize = GetConstantSize(constant);
|
|
if (constSize == -1)
|
|
return BfTypedValue();
|
|
|
|
stackPtr -= constSize;
|
|
WriteConstant(stackPtr, constant);
|
|
}
|
|
|
|
uint8* retPtr = NULL;
|
|
auto returnType = methodInstance->mReturnType;
|
|
if (!returnType->IsValuelessType())
|
|
{
|
|
int retSize = methodInstance->mReturnType->mSize;
|
|
stackPtr -= retSize;
|
|
retPtr = stackPtr;
|
|
}
|
|
|
|
CeFrame ceFrame;
|
|
ceFrame.mFunction = ceFunction;
|
|
ceFrame.mFramePtr = stackPtr;
|
|
ceFrame.mStackPtr = stackPtr - ceFunction->mFrameSize;
|
|
ceFrame.mInstPtr = &ceFunction->mCode[0];
|
|
mCallStack.Add(ceFrame);
|
|
|
|
bool success = Execute();
|
|
|
|
mCallStack.Clear();
|
|
|
|
auto constHolder = module->mBfIRBuilder;
|
|
|
|
if (success)
|
|
{
|
|
BfTypedValue retValue;
|
|
if (retPtr != NULL)
|
|
{
|
|
BfIRValue constVal;
|
|
|
|
if (returnType->IsPrimitiveType())
|
|
{
|
|
auto primType = (BfPrimitiveType*)returnType;
|
|
|
|
auto typeCode = primType->mTypeDef->mTypeCode;
|
|
if (typeCode == BfTypeCode_IntPtr)
|
|
typeCode = (module->mCompiler->mSystem->mPtrSize == 4) ? BfTypeCode_Int32 : BfTypeCode_Int64;
|
|
else if (typeCode == BfTypeCode_UIntPtr)
|
|
typeCode = (module->mCompiler->mSystem->mPtrSize == 4) ? BfTypeCode_UInt32 : BfTypeCode_UInt64;
|
|
|
|
switch (typeCode)
|
|
{
|
|
case BfTypeCode_Int8:
|
|
constVal = constHolder->CreateConst(primType->mTypeDef->mTypeCode, *(int8*)retPtr);
|
|
break;
|
|
case BfTypeCode_UInt8:
|
|
constVal = constHolder->CreateConst(primType->mTypeDef->mTypeCode, *(uint8*)retPtr);
|
|
break;
|
|
case BfTypeCode_Int16:
|
|
constVal = constHolder->CreateConst(primType->mTypeDef->mTypeCode, *(int16*)retPtr);
|
|
break;
|
|
case BfTypeCode_UInt16:
|
|
constVal = constHolder->CreateConst(primType->mTypeDef->mTypeCode, *(uint16*)retPtr);
|
|
break;
|
|
case BfTypeCode_Int32:
|
|
constVal = constHolder->CreateConst(primType->mTypeDef->mTypeCode, *(int32*)retPtr);
|
|
break;
|
|
case BfTypeCode_UInt32:
|
|
constVal = constHolder->CreateConst(primType->mTypeDef->mTypeCode, (uint64)*(uint32*)retPtr);
|
|
break;
|
|
case BfTypeCode_Int64:
|
|
case BfTypeCode_UInt64:
|
|
constVal = constHolder->CreateConst(primType->mTypeDef->mTypeCode, *(uint64*)retPtr);
|
|
break;
|
|
case BfTypeCode_Float:
|
|
constVal = constHolder->CreateConst(primType->mTypeDef->mTypeCode, *(float*)retPtr);
|
|
break;
|
|
case BfTypeCode_Double:
|
|
constVal = constHolder->CreateConst(primType->mTypeDef->mTypeCode, *(double*)retPtr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (constVal)
|
|
return BfTypedValue(constVal, returnType);
|
|
}
|
|
else
|
|
{
|
|
return BfTypedValue(module->mBfIRBuilder->GetFakeVal(), returnType);
|
|
}
|
|
}
|
|
|
|
return BfTypedValue();
|
|
}
|