#if defined _DEBUG || true #define BEMC_ASSERT(_Expression) (void)( (!!(_Expression)) || (AssertFail(#_Expression, __LINE__), 0) ) #else #define BEMC_ASSERT(_Expression) (void)(0) #endif #include #include "BeMCContext.h" #include "BeCOFFObject.h" #include "BeIRCodeGen.h" #include "../Compiler/BfIRCodeGen.h" #include "BeefySysLib/util/BeefPerf.h" #include "BeefySysLib/util/AllocDebug.h" #pragma warning(disable:4146) USING_NS_BF; ////////////////////////////////////////////////////////////////////////// #define IS_BIT_SET_32(bits, idx) (((bits)[(idx) / 32] & (1 << ((idx) % 32))) != 0) // Only use "rep stosb"/"rep movsb" if total size is at least this value. The drawback is requiring saving/restore of RAX, RDI, and RCX (and RSI for movsb) const int BF_REP_MOV_LIMIT = 128; static const X64CPURegister gVolatileRegs[] = { X64Reg_RAX, X64Reg_RCX, X64Reg_RDX, X64Reg_R8, X64Reg_R9, X64Reg_R10, X64Reg_R11, X64Reg_XMM0_f64, X64Reg_XMM1_f64, X64Reg_XMM2_f64, X64Reg_XMM3_f64, X64Reg_XMM4_f64, X64Reg_XMM5_f64 }; static const char* gOpName[] = { "None", "@Def", "@DefLoad", "@DefPhi", "@DbgDecl", "@DbgRangeStart", "@DbgRangeEnd", "@LifetimeExtend", "@LifetimeStart", "@LifetimeEnd", "@ValueScopeSoftEnd", "@ValueScopeHardEnd", "@Label", //"PhiValue", "CmpToBool", "MemSet", "MemCpy", "FastCheckStack", "TLSSetup", "PreserveVolatiles", "RestoreVolatiles", "Unwind_PushReg", "Unwind_SaveXMM", "Unwind_Alloc", "Unwind_SetBP", "Rem", "IRem", "Nop", "Unreachable", "EnsureCodeAt", "DbgBreak", "MFence", "Mov", "MovRaw", "MovSX", "XChg", "XAdd", "CmpXChg", "Load", "Store", "Push", "Pop", "Neg", "Not", "Add", "Sub", "Mul", "IMul", "Div", "IDiv", "Cmp", "And", "Or", "Xor", "Shl", "Shr", "Sar", "Test", "CondBr", "Br", "Ret", "Call", }; static_assert(BF_ARRAY_COUNT(gOpName) == (int)BeMCInstKind_COUNT, "gOpName incorrect size"); void PrintBoolVec(Array& boolVec) { String str; for (int i = 0; i < (int)boolVec.size(); i++) { str += StrFormat("%d: %s\n", i, boolVec[i] ? "true" : "false"); } OutputDebugStr(str); } static bool IsPowerOfTwo(int64 val) { return (val != 0) && ((val & (val - 1)) == 0); } bool BeVTrackingBits::IsSet(int idx) { return IS_BIT_SET_32((uint32*)this, idx); } void BeVTrackingBits::Set(int idx) { uint32* bits = (uint32*)this; bits[(idx) / 32] |= (1 << (idx % 32)); } void BeVTrackingBits::Clear(int idx) { uint32* bits = (uint32*)this; bits[(idx) / 32] &= ~(1 << (idx % 32)); } BeVTrackingContext::BeVTrackingContext(BeMCContext* mcContext) { mMCContext = mcContext; mNumEntries = 0; mNumItems = 0; mNumBlocks = -1; mTrackKindCount = (int)BeTrackKind_COUNT; } void BeVTrackingContext::Init(int numItems) { mNumItems = numItems; mNumEntries = mNumItems * mTrackKindCount; mNumBlocks = (mNumEntries + 31) / 32; } void BeVTrackingContext::Clear() { mAlloc.Clear(); mNumBlocks = -1; } int BeVTrackingContext::GetBitsBytes() { return mNumBlocks * 4; } int BeVTrackingContext::GetIdx(int baseIdx, BeTrackKind liveKind) { return baseIdx + mNumItems * (int)liveKind; } void BeVTrackingContext::Print(BeVTrackingList* list) { String str; for (int i : *list) str += StrFormat("%d ", i); if (list->mNumChanges > 0) { str += " |"; for (int changeIdx = 0; changeIdx < list->mNumChanges; changeIdx++) { int change = list->GetChange(changeIdx); if (change >= 0) str += StrFormat(" +%d", change); else str += StrFormat(" -%d", -change - 1); } } str += "\n"; OutputDebugStr(str); } BeVTrackingList* BeVTrackingContext::AllocEmptyList() { int allocBytes = sizeof(int) * (2); auto newList = (BeVTrackingList*)mAlloc.AllocBytes(allocBytes); mStats.mListBytes += allocBytes; newList->mSize = 0; newList->mNumChanges = 0; return newList; } BeVTrackingList* BeVTrackingContext::AddFiltered(BeVTrackingList* list, SizedArrayImpl& filteredAdds, bool perserveChangeList) { int newSize = list->mSize + filteredAdds.size(); int allocBytes = sizeof(int) * (2 + newSize); if (perserveChangeList) allocBytes += sizeof(int) * (int)(list->mNumChanges + filteredAdds.size()); auto newList = (BeVTrackingList*)mAlloc.AllocBytes(allocBytes); mStats.mListBytes += allocBytes; { if (filteredAdds.size() > 1) std::sort(filteredAdds.begin(), filteredAdds.end()); int addIdx = 0; int nextAdd; if (addIdx < (int)filteredAdds.size()) nextAdd = filteredAdds[addIdx++]; else nextAdd = 0x7FFFFFFF; int* outPtr = &newList->mEntries[0]; for (auto idx : *list) { while (idx > nextAdd) { *(outPtr++) = nextAdd; if (addIdx < (int)filteredAdds.size()) nextAdd = filteredAdds[addIdx++]; else nextAdd = 0x7FFFFFFF; } *(outPtr++) = idx; } while (nextAdd != 0x7FFFFFFF) { *(outPtr++) = nextAdd; if (addIdx >= (int)filteredAdds.size()) break; nextAdd = filteredAdds[addIdx++]; } } newList->mSize = newSize; if (perserveChangeList) { for (int changeIdx = 0; changeIdx < list->mNumChanges; changeIdx++) { newList->mEntries[newSize + changeIdx] = list->GetChange(changeIdx); } for (int changeIdx = 0; changeIdx < (int)filteredAdds.size(); changeIdx++) { newList->mEntries[newSize + list->mNumChanges + changeIdx] = filteredAdds[changeIdx]; } newList->mNumChanges = list->mNumChanges + (int)filteredAdds.size(); } else newList->mNumChanges = 0; return newList; } BeVTrackingList* BeVTrackingContext::AddFiltered(BeVTrackingList* list, int idx, bool perserveChangeList) { int newSize = list->mSize + 1; int allocBytes = sizeof(int) * (2 + newSize); if (perserveChangeList) allocBytes += sizeof(int) * (int)(list->mNumChanges + 1); auto newList = (BeVTrackingList*)mAlloc.AllocBytes(allocBytes); mStats.mListBytes += allocBytes; { int addIdx = 0; int nextAdd; nextAdd = idx; int* outPtr = &newList->mEntries[0]; for (auto idx : *list) { while (idx > nextAdd) { *(outPtr++) = nextAdd; nextAdd = 0x7FFFFFFF; } *(outPtr++) = idx; } while (nextAdd != 0x7FFFFFFF) { *(outPtr++) = nextAdd; break; } } newList->mSize = newSize; if (perserveChangeList) { for (int changeIdx = 0; changeIdx < list->mNumChanges; changeIdx++) { newList->mEntries[newSize + changeIdx] = list->GetChange(changeIdx); } newList->mEntries[newSize + list->mNumChanges] = idx; newList->mNumChanges = list->mNumChanges + (int)1; } else newList->mNumChanges = 0; return newList; } BeVTrackingList* BeVTrackingContext::Add(BeVTrackingList* list, const SizedArrayImpl& indices, bool perserveChangeList) { SizedArray newIndices; for (int idx : indices) { if (!IsSet(list, idx)) { newIndices.push_back(idx); } } if (newIndices.empty()) return list; return AddFiltered(list, newIndices, perserveChangeList); } BeVTrackingList* BeVTrackingContext::Add(BeVTrackingList* list, int idx, bool perserveChangeList) { if (IsSet(list, idx)) return list; return AddFiltered(list, idx, perserveChangeList); } // Performs an 'add' for items that were in prevDest BeVTrackingList* BeVTrackingContext::SetChanges(BeVTrackingList* prevDestEntry, BeVTrackingList* mergeFrom) { int removeCount = 0; int addCount = 0; int newSize = prevDestEntry->mSize; auto prevItr = prevDestEntry->begin(); auto prevEnd = prevDestEntry->end(); auto mergeFromItr = mergeFrom->begin(); auto mergeFromEnd = mergeFrom->end(); while ((prevItr != prevEnd) && (mergeFromItr != mergeFromEnd)) { int prevIdx = *prevItr; int mergeIdx = *mergeFromItr; bool done = false; while (mergeIdx < prevIdx) { removeCount++; ++mergeFromItr; if (mergeFromItr == mergeFromEnd) { done = true; break; } mergeIdx = *mergeFromItr; } if (done) break; while (prevIdx < mergeIdx) { addCount++; ++prevItr; if (prevItr == prevEnd) { done = true; break; } prevIdx = *prevItr; } if (done) break; if (prevIdx == mergeIdx) { ++prevItr; ++mergeFromItr; } } while (prevItr != prevEnd) { addCount++; ++prevItr; } while (mergeFromItr != mergeFromEnd) { removeCount++; ++mergeFromItr; } int allocBytes = sizeof(int) * (2 + newSize + addCount + removeCount); auto newList = (BeVTrackingList*)mAlloc.AllocBytes(allocBytes); mStats.mListBytes += allocBytes; int* outPtr = &newList->mEntries[0]; int* changePtr = &newList->mEntries[newSize]; prevItr = prevDestEntry->begin(); mergeFromItr = mergeFrom->begin(); while ((prevItr != prevEnd) && (mergeFromItr != mergeFromEnd)) { int prevIdx = *prevItr; int mergeIdx = *mergeFromItr; bool done = false; while (mergeIdx < prevIdx) { *(changePtr++) = ~mergeIdx; ++mergeFromItr; if (mergeFromItr == mergeFromEnd) { done = true; break; } mergeIdx = *mergeFromItr; } if (done) break; while (prevIdx < mergeIdx) { *(outPtr++) = prevIdx; *(changePtr++) = prevIdx; ++prevItr; if (prevItr == prevEnd) { done = true; break; } prevIdx = *prevItr; } if (done) break; if (prevIdx == mergeIdx) { *(outPtr++) = *prevItr; ++prevItr; ++mergeFromItr; } } while (prevItr != prevEnd) { int prevIdx = *prevItr; *(outPtr++) = *prevItr; *(changePtr++) = prevIdx; ++prevItr; } while (mergeFromItr != mergeFromEnd) { int mergeIdx = *mergeFromItr; *(changePtr++) = ~mergeIdx; ++mergeFromItr; } BF_ASSERT((outPtr - &newList->mEntries[0]) == newSize); BF_ASSERT((changePtr - &newList->mEntries[newSize]) == addCount + removeCount); newList->mSize = newSize; newList->mNumChanges = addCount + removeCount; return newList; } BeVTrackingList* BeVTrackingContext::ClearFiltered(BeVTrackingList* list, const SizedArrayImpl& indices) { /*int newSize = list->mSize - indices.size(); auto newList = (BeVTrackingList*)mAlloc.AllocBytes(sizeof(int) * (1 + newSize)); if (indices.size() == 1) { int findIdx0 = indices[0]; int* outPtr = 0; for (auto idx : indices) { if (idx != findIdx0) *(outPtr++) = idx; } BF_ASSERT(outPtr == &newList->mEntries[0] + indices.size()); } else if (indices.size() == 2) { int findIdx0 = indices[0]; int findIdx1 = indices[1]; int* outPtr = 0; for (auto idx : indices) { if ((idx != findIdx0) && (idx != findIdx1)) *(outPtr++) = idx; } BF_ASSERT(outPtr == &newList->mEntries[0] + indices.size()); } else { int* outPtr = 0; for (auto idx : indices) { if (std::find(indices.begin(), indices.end(), idx) == indices.end()) *(outPtr++) = idx; } BF_ASSERT(outPtr == &newList->mEntries[0] + indices.size()); } newList->mSize = newSize; return newList;*/ BF_FATAL("Broken"); return NULL; } BeVTrackingList* BeVTrackingContext::Modify(BeVTrackingList* list, const SizedArrayImpl& inAdds, const SizedArrayImpl& inRemoves, SizedArrayImpl& filteredAdds, SizedArrayImpl& filteredRemoves, bool preserveChanges) { for (int idx : inAdds) { if (!IsSet(list, idx)) { filteredAdds.push_back(idx); } } for (int idx : inRemoves) { if (IsSet(list, idx)) { filteredRemoves.push_back(idx); } } if ((filteredAdds.empty()) && (filteredRemoves.empty())) return list; int newSize = list->mSize - filteredRemoves.size() + filteredAdds.size(); int changeSize; if (preserveChanges) changeSize = list->mNumChanges; else changeSize = filteredRemoves.size() + filteredAdds.size(); int allocBytes = sizeof(int) * (2 + newSize + changeSize); auto newList = (BeVTrackingList*)mAlloc.AllocBytes(allocBytes); mStats.mListBytes += allocBytes; if (filteredAdds.size() > 1) std::sort(filteredAdds.begin(), filteredAdds.end()); if (filteredRemoves.size() > 1) std::sort(filteredRemoves.begin(), filteredRemoves.end()); int addIdx = 0; int nextAdd; if (addIdx < (int)filteredAdds.size()) nextAdd = filteredAdds[addIdx++]; else nextAdd = 0x7FFFFFFF; int removeIdx = 0; int nextRemove; if (removeIdx < (int)filteredRemoves.size()) nextRemove = filteredRemoves[removeIdx++]; else nextRemove = 0x7FFFFFFF; int* outPtr = &newList->mEntries[0]; for (auto idx : *list) { if (idx == nextRemove) { if (removeIdx < (int)filteredRemoves.size()) nextRemove = filteredRemoves[removeIdx++]; else nextRemove = 0x7FFFFFFF; continue; } while (idx > nextAdd) { *(outPtr++) = nextAdd; if (addIdx < (int)filteredAdds.size()) nextAdd = filteredAdds[addIdx++]; else nextAdd = 0x7FFFFFFF; } *(outPtr++) = idx; } while (nextAdd != 0x7FFFFFFF) { *(outPtr++) = nextAdd; if (addIdx >= (int)filteredAdds.size()) break; nextAdd = filteredAdds[addIdx++]; } BF_ASSERT(outPtr == &newList->mEntries[0] + newSize); BF_ASSERT((nextAdd = 0x7FFFFFFF) && (nextRemove == 0x7FFFFFFF)); if (preserveChanges) { for (int i = 0; i < list->mNumChanges; i++) newList->mEntries[newSize + i] = list->mEntries[list->mSize + i]; } else { for (int i = 0; i < (int)filteredRemoves.size(); i++) { newList->mEntries[newSize + i] = -filteredRemoves[i] - 1; } for (int i = 0; i < (int)filteredAdds.size(); i++) { newList->mEntries[newSize + filteredRemoves.size() + i] = filteredAdds[i]; } } newList->mSize = newSize; newList->mNumChanges = changeSize; /// /*for (int i = 0; i < mNumEntries; i++) BF_ASSERT(IsSet(newList, i) == unit.mBits->IsSet(i)); int prevIdx = -1; for (int idx : *newList) { BF_ASSERT(idx > prevIdx); prevIdx = idx; } OutputDebugStrF("Modify %d %@\n", modifyItrIdx, newList);*/ /// return newList; } int BeVTrackingContext::FindIndex(BeVTrackingList* entry, int val) { int lo = 0; int hi = entry->mSize - 1; while (lo <= hi) { int i = (lo + hi) / 2; int midVal = entry->mEntries[i]; int c = midVal - val; if (c == 0) return i; if (c < 0) lo = i + 1; else hi = i - 1; } return ~lo; } bool BeVTrackingContext::IsSet(BeVTrackingList* entry, int idx) { return FindIndex(entry, idx) >= 0; } bool BeVTrackingContext::IsSet(BeVTrackingList* entry, int idx, BeTrackKind trackKind) { return IsSet(entry, GetIdx(idx, trackKind)); } BeVTrackingList* BeVTrackingContext::Clear(BeVTrackingList* list, const SizedArrayImpl& indices) { SizedArray newIndices; for (int idx : indices) { if (IsSet(list, idx)) { newIndices.push_back(idx); } } if (newIndices.empty()) return list; return ClearFiltered(list, newIndices); } bool BeVTrackingContext::IsEmpty(BeVTrackingList* list) { return list->mSize == 0; } BeVTrackingList* BeVTrackingContext::Merge(BeVTrackingList* prevDestEntry, BeVTrackingList* mergeFrom) { if (prevDestEntry == NULL) return mergeFrom; if (mergeFrom->mSize == 0) return prevDestEntry; if (prevDestEntry->mSize == 0) return mergeFrom; int newSize = prevDestEntry->mSize; auto prevItr = prevDestEntry->begin(); auto prevEnd = prevDestEntry->end(); auto mergeFromItr = mergeFrom->begin(); auto mergeFromEnd = mergeFrom->end(); while ((prevItr != prevEnd) && (mergeFromItr != mergeFromEnd)) { int prevIdx = *prevItr; int mergeIdx = *mergeFromItr; bool done = false; while (mergeIdx < prevIdx) { newSize++; ++mergeFromItr; if (mergeFromItr == mergeFromEnd) { done = true; break; } mergeIdx = *mergeFromItr; } if (done) break; while (prevIdx < mergeIdx) { ++prevItr; if (prevItr == prevEnd) { done = true; break; } prevIdx = *prevItr; } if (done) break; if (prevIdx == mergeIdx) { ++prevItr; ++mergeFromItr; } } while (mergeFromItr != mergeFromEnd) { newSize++; ++mergeFromItr; } if (newSize == prevDestEntry->mSize) return prevDestEntry; int allocBytes = sizeof(int) * (2 + newSize); auto newList = (BeVTrackingList*)mAlloc.AllocBytes(allocBytes); mStats.mListBytes += allocBytes; int* outPtr = &newList->mEntries[0]; prevItr = prevDestEntry->begin(); mergeFromItr = mergeFrom->begin(); while ((prevItr != prevEnd) && (mergeFromItr != mergeFromEnd)) { int prevIdx = *prevItr; int mergeIdx = *mergeFromItr; bool done = false; while (mergeIdx < prevIdx) { *(outPtr++) = mergeIdx; ++mergeFromItr; if (mergeFromItr == mergeFromEnd) { done = true; break; } mergeIdx = *mergeFromItr; } if (done) break; while (prevIdx < mergeIdx) { *(outPtr++) = prevIdx; ++prevItr; if (prevItr == prevEnd) { done = true; break; } prevIdx = *prevItr; } if (done) break; if (prevIdx == mergeIdx) { *(outPtr++) = *prevItr; ++prevItr; ++mergeFromItr; } } while (prevItr != prevEnd) { *(outPtr++) = *prevItr; ++prevItr; } while (mergeFromItr != mergeFromEnd) { *(outPtr++) = *mergeFromItr; ++mergeFromItr; } BF_ASSERT((outPtr - &newList->mEntries[0]) == newSize); newList->mSize = newSize; newList->mNumChanges = 0; return newList; } BeVTrackingList* BeVTrackingContext::MergeChanges(BeVTrackingList* prevDestEntry, BeVTrackingList* mergeFrom) { if (prevDestEntry == mergeFrom) return prevDestEntry; if (mergeFrom->mNumChanges == 0) return prevDestEntry; SizedArray changes; for (int changeIdx = 0; changeIdx < (int)prevDestEntry->mNumChanges; changeIdx++) changes.push_back(prevDestEntry->GetChange(changeIdx)); for (int changeIdx = 0; changeIdx < (int)mergeFrom->mNumChanges; changeIdx++) { // If there isn't already a change (whether and add or a remove) that refers to this same vreg, // then we add this change as well int change = mergeFrom->GetChange(changeIdx); int negChange = -change - 1; /*if (((std::find(changes.begin(), changes.end(), change) == changes.end())) && ((std::find(changes.begin(), changes.end(), negChange) == changes.end())))*/ if ((!changes.Contains(change)) && (!changes.Contains(negChange))) { changes.push_back(change); } } int newSize = prevDestEntry->mSize; int allocBytes = (int)(sizeof(int) * (2 + newSize + changes.size())); auto newList = (BeVTrackingList*)mAlloc.AllocBytes(allocBytes); mStats.mListBytes += allocBytes; memcpy(&newList->mEntries[0], &prevDestEntry->mEntries[0], newSize * sizeof(int)); for (int i = 0; i < (int)changes.size(); i++) { newList->mEntries[newSize + i] = changes[i]; } newList->mSize = newSize; newList->mNumChanges = (int)changes.size(); return newList; } BeVTrackingList * BeVTrackingContext::RemoveChange(BeVTrackingList* prevDestEntry, int idx) { int newSize = prevDestEntry->mSize; int allocBytes = (int)(sizeof(int) * (2 + newSize + prevDestEntry->mNumChanges - 1)); auto newList = (BeVTrackingList*)mAlloc.AllocBytes(allocBytes); mStats.mListBytes += allocBytes; memcpy(&newList->mEntries[0], &prevDestEntry->mEntries[0], newSize * sizeof(int)); bool found = false; int outIdx = newSize; for (int i = 0; i < (int)prevDestEntry->mNumChanges; i++) { int change = prevDestEntry->GetChange(i); if (change == idx) { found = true; continue; } newList->mEntries[outIdx++] = change; } if (!found) return prevDestEntry; //BF_ASSERT(found); newList->mSize = newSize; newList->mNumChanges = prevDestEntry->mNumChanges - 1; return newList; } ////////////////////////////////////////////////////////////////////////// BeMCColorizer::BeMCColorizer(BeMCContext* mcContext) { mContext = mcContext; mReserveParamRegs = false; } void BeMCColorizer::Prepare() { mReserveParamRegs = false; mNodes.Resize(mContext->mVRegInfo.size()); for (int vregIdx = 0; vregIdx < (int)mNodes.size(); vregIdx++) { auto node = &mNodes[vregIdx]; node->Prepare(); //node->mActualVRegIdx = vregIdx; auto vregInfo = mContext->mVRegInfo[vregIdx]; if ((vregInfo->mIsRetVal) && (mContext->mCompositeRetVRegIdx != -1) && (vregIdx != mContext->mCompositeRetVRegIdx)) continue; if (vregInfo->mRelTo) { BF_ASSERT(vregInfo->mIsExpr); /*if ((vregInfo->IsDirectRelTo()) && (vregInfo->mRelTo.mKind == BeMCOperandKind_VReg)) node->mActualVRegIdx = vregInfo->mRelTo.mVRegIdx; */ //node->mWantsReg = false; //vregInfo->mReg = X64Reg_None; //continue; } if ((vregInfo->mRefCount > 0) || (vregInfo->mIsRetVal)) { node->mWantsReg = ((vregInfo->mType->mSize > 0) && (!vregInfo->mRegNumPinned) && (!vregInfo->mSpilled) && (!vregInfo->mIsExpr) && (!vregInfo->mForceMem) && (vregInfo->mFrameOffset == INT_MIN)); } // if (vregInfo->mIsRetVal) // node->mWantsReg = true; if (!node->mWantsReg) vregInfo->mReg = X64Reg_None; vregInfo->mVRegAffinity = -1; /*if (vregInfo->mForceReg) { // We can't have reg restrictions when we have forceReg set BF_ASSERT(!vregInfo->mDisableR12); BF_ASSERT(!vregInfo->mDisableRDX); }*/ if (vregInfo->mDisableR11) node->AdjustRegCost(X64Reg_R11, 0x0FFFFFFF); if (vregInfo->mDisableR12) node->AdjustRegCost(X64Reg_R12, 0x0FFFFFFF); if (vregInfo->mDisableR13) node->AdjustRegCost(X64Reg_R13, 0x0FFFFFFF); if (vregInfo->mDisableRAX) node->AdjustRegCost(X64Reg_RAX, 0x0FFFFFFF); if (vregInfo->mDisableRDX) node->AdjustRegCost(X64Reg_RDX, 0x0FFFFFFF); if (vregInfo->mDisableEx) { for (int i = X64Reg_RSI; i <= X64Reg_R15; i++) node->AdjustRegCost((X64CPURegister)i, 0x0FFFFFFF); } } } void BeMCColorizer::AddEdge(int vreg0, int vreg1) { int checkVRegIdx0 = mContext->GetUnderlyingVReg(vreg0); int checkVRegIdx1 = mContext->GetUnderlyingVReg(vreg1); if (checkVRegIdx0 == checkVRegIdx1) return; auto node0 = &mNodes[checkVRegIdx0]; auto node1 = &mNodes[checkVRegIdx1]; if ((node0->mWantsReg) && (node1->mWantsReg)) { node0->mEdges.Add(checkVRegIdx1); node1->mEdges.Add(checkVRegIdx0); } } void BeMCColorizer::PropogateMemCost(const BeMCOperand & operand, int memCost) { if (operand.IsVRegAny()) { auto vregInfo = mContext->mVRegInfo[operand.mVRegIdx]; mNodes[operand.mVRegIdx].mMemCost += memCost; PropogateMemCost(vregInfo->mRelTo, memCost); PropogateMemCost(vregInfo->mRelOffset, memCost); } } void BeMCColorizer::GenerateRegCosts() { bool doRegCost = true; // Disallow param reg cross-refs { Array paramRegsLeft = mContext->mParamsUsedRegs; Array prevRegMovs; auto mcBlock = mContext->mBlocks[0]; for (int instIdx = 0; instIdx < (int)mcBlock->mInstructions.size(); instIdx++) { auto inst = mcBlock->mInstructions[instIdx]; if ((inst->IsMov()) && (inst->mArg1.IsNativeReg()) && (inst->mArg0.IsVReg())) { int vregIdx = mContext->GetUnderlyingVReg(inst->mArg0.mVRegIdx); auto reg = mContext->GetFullRegister(inst->mArg1.mReg); // Don't allow any previous "direct reg movs" assign to a vreg that gets bound // to this reg. This is to avoid "cross-referencing" param vregs. IE: // Mov %vreg0, RCX // Mov %vreg1, RDX // Because the first MOV would destroy the incoming RDX for (auto prevVRegIdx : prevRegMovs) { auto node = &mNodes[prevVRegIdx]; node->AdjustRegCost(reg, 0x0FFFFFFF); } /*auto itr = std::find(paramRegsLeft.begin(), paramRegsLeft.end(), reg); if (itr != paramRegsLeft.end()) { paramRegsLeft.erase(itr); if (paramRegsLeft.size() == 0) break; prevRegMovs.push_back(vregIdx); }*/ if (paramRegsLeft.Remove(reg)) { if (paramRegsLeft.size() == 0) break; prevRegMovs.push_back(vregIdx); } } } BF_ASSERT(paramRegsLeft.size() == 0); } int preserveDepth = 0; for (auto mcBlock : mContext->mBlocks) { int costMult = mcBlock->mIsLooped ? 4 : 1; for (int instIdx = (int)mcBlock->mInstructions.size() - 1; instIdx >= 0; instIdx--) { auto inst = mcBlock->mInstructions[instIdx]; // We could adjust register cost based on a possibility of cross-dependencies that won't allow us // to reorder the movs for param setup if (inst->mKind == BeMCInstKind_PreserveVolatiles) { preserveDepth++; int preserveCost = 8 * costMult; if (inst->mArg0.mKind == BeMCOperandKind_PreserveFlag) { // Just use a small cost for NoReturns, those are implied to be "unlikely" paths (ie: errors) // But at the least, the code is smaller if we don't need to save the regs so use a small cost if ((inst->mArg0.mPreserveFlag & BeMCPreserveFlag_NoRestore) != 0) preserveCost = 1; } auto restoreIdx = mContext->FindRestoreVolatiles(mcBlock, instIdx); if (restoreIdx == -1) restoreIdx = mcBlock->mInstructions.size() - 1; // 'Ret' auto restoreInst = mcBlock->mInstructions[restoreIdx]; // Any vregs alive during this call will incur a cost of saving/restoring if we allocate onto a volatile register. // If the vreg is either input-only or output-only (short lived temporary) then we can map it directly to a volatile register for (int vregIdx : *inst->mLiveness) { if (vregIdx >= mContext->mLivenessContext.mNumItems) continue; auto checkVRegIdx = mContext->GetUnderlyingVReg(vregIdx); // Already handled the underlying vreg? if ((checkVRegIdx != vregIdx) && (mContext->mLivenessContext.IsSet(inst->mLiveness, checkVRegIdx))) continue; if (mContext->mLivenessContext.IsSet(restoreInst->mLiveness, vregIdx)) { auto node = &mNodes[checkVRegIdx]; if (inst->mArg0.IsNativeReg()) { // Only one specific register used being preserved node->AdjustRegCost(inst->mArg0.mReg, preserveCost); } else { // All volatile registers preserved for (auto reg : gVolatileRegs) { node->AdjustRegCost(reg, preserveCost); } } } } } else if (inst->mKind == BeMCInstKind_RestoreVolatiles) { preserveDepth--; } if (inst->IsPsuedo()) continue; // If both args are non-registers then we presume we may have to create a scratch register // so inflate the cost. Note that the first time through, this will presumably occur for all // operations but we can change our minds as instructions are legalized if ((inst->mArg0.IsVReg()) && (inst->mArg1.IsVReg())) { auto vregInfo0 = mContext->mVRegInfo[inst->mArg0.mVRegIdx]; auto vregInfo1 = mContext->mVRegInfo[inst->mArg1.mVRegIdx]; if ((vregInfo0->mReg == X64Reg_None) && (vregInfo1->mReg == X64Reg_None)) { Node* node0 = &mNodes[inst->mArg1.mVRegIdx]; Node* node1 = &mNodes[inst->mArg1.mVRegIdx]; node0->mMemCost += 4 * costMult; node1->mMemCost += 4 * costMult; } } if ((inst->mKind == BeMCInstKind_Div) || (inst->mKind == BeMCInstKind_IDiv)) { if (inst->mArg0.IsVReg()) { Node* node = &mNodes[inst->mArg0.mVRegIdx]; int adjustCost = -4; if (preserveDepth >= 2) { // This has to be large enough to counteract the 'PreserveVolatile RAX' adjustCost -= 8; } node->AdjustRegCost(X64Reg_RAX, costMult * adjustCost); continue; } } if (inst->mKind == BeMCInstKind_MovSX) { // We can only perform MovSX on reg targets if (inst->mArg0.IsVReg()) mNodes[inst->mArg0.mVRegIdx].mMemCost += 4 * costMult; } if (inst->mKind == BeMCInstKind_Mov) { if ((inst->mArg0.mKind == BeMCOperandKind_VReg) && (inst->mArg1.mKind == BeMCOperandKind_VReg)) { int arg0Idx = mContext->GetUnderlyingVReg(inst->mArg0.mVRegIdx); int arg1Idx = mContext->GetUnderlyingVReg(inst->mArg1.mVRegIdx); auto vregInfo0 = mContext->mVRegInfo[arg0Idx]; auto vregInfo1 = mContext->mVRegInfo[arg1Idx]; if (vregInfo0->mVRegAffinity == -1) vregInfo0->mVRegAffinity = arg1Idx; if (vregInfo1->mVRegAffinity == -1) vregInfo1->mVRegAffinity = arg0Idx; } } if (inst->mKind == BeMCInstKind_Call) { if (inst->mArg0.IsVRegAny()) { for (int checkInstIdx = instIdx - 1; checkInstIdx >= 0; checkInstIdx--) { auto checkInst = mcBlock->mInstructions[checkInstIdx]; if ((checkInst->mKind == BeMCInstKind_Mov) && (checkInst->mArg0.IsNativeReg())) { // This can save us from needing to mov the call addr to RAX before the call mNodes[inst->mArg0.mVRegIdx].AdjustRegCost(checkInst->mArg0.mReg, 2 * costMult); } else break; } } } if (inst->IsMov()) { auto argTo = inst->mArg0; auto vregInfo = mContext->GetVRegInfo(argTo); if ((vregInfo != NULL) && (vregInfo->IsDirectRelTo())) argTo = vregInfo->mRelTo; auto argFrom = inst->mArg1; vregInfo = mContext->GetVRegInfo(argFrom); if ((vregInfo != NULL) && (vregInfo->IsDirectRelTo())) argFrom = vregInfo->mRelTo; if (argTo == argFrom) continue; } auto operands = { &inst->mResult, &inst->mArg0, &inst->mArg1 }; for (auto operand : operands) { if (operand->IsVRegAny()) { Node* node = &mNodes[operand->mVRegIdx]; auto vregInfo = mContext->mVRegInfo[operand->mVRegIdx]; if ((vregInfo->mIsRetVal) && (mContext->mCompositeRetVRegIdx != -1)) vregInfo = mContext->mVRegInfo[mContext->mCompositeRetVRegIdx]; if (operand->mKind == BeMCOperandKind_VRegLoad) { // We propagate this mem cost to RelTo/RelOffset layer Node* node = &mNodes[operand->mVRegIdx]; node->mMemCost += 6 * costMult; } while (vregInfo->IsDirectRelTo()) { node = &mNodes[vregInfo->mRelTo.mVRegIdx]; vregInfo = mContext->mVRegInfo[vregInfo->mRelTo.mVRegIdx]; } node->mMemCost += 4 * costMult; if (vregInfo->mRelTo.IsVRegAny()) mNodes[vregInfo->mRelTo.mVRegIdx].mMemCost += 4 * costMult; if (vregInfo->mRelOffset.IsVRegAny()) // Higher cost, will definitely require a reg mNodes[vregInfo->mRelOffset.mVRegIdx].mMemCost += 8 * costMult; if (inst->IsMov()) { if (operand != &inst->mResult) { auto otherReg = X64Reg_None; if (operand == &inst->mArg0) { if (inst->mArg1.mKind == BeMCOperandKind_NativeReg) otherReg = inst->mArg1.mReg; if (inst->mArg1.IsImmediateFloat()) // Moving a float immediate into memory takes two instructions node->mMemCost += 4 * costMult; } else { if (inst->mArg0.mKind == BeMCOperandKind_NativeReg) otherReg = inst->mArg0.mReg; } // If we assigned ourselves to this reg then it would be a "mov reg, reg" which could // be eliminated so the cost is negative. We could be tempted to more strongly favor this, // but that could cause RAX to be used for __return when it would be less costly to use // memory so we don't have to preserve/restore it across call boundaries if (otherReg != X64Reg_None) node->AdjustRegCost(mContext->GetFullRegister(otherReg), -2 * costMult); } } } } } } for (int nodeIdx = 0; nodeIdx < (int)mNodes.size(); nodeIdx++) { auto node = &mNodes[nodeIdx]; if (node->mMemCost > 0) { auto vregInfo = mContext->mVRegInfo[nodeIdx]; if (vregInfo->mIsExpr) { PropogateMemCost(vregInfo->mRelTo, node->mMemCost); PropogateMemCost(vregInfo->mRelOffset, node->mMemCost); } } } BF_ASSERT(preserveDepth == 0); } void BeMCColorizer::AssignRegs(RegKind regKind) { X64CPURegister highestReg; int totalRegs32 = 0; int totalRegs16 = 0; SizedArray validRegs; if (regKind == BeMCColorizer::RegKind_Ints) { highestReg = X64Reg_R15; validRegs = { X64Reg_RAX, X64Reg_RBX, X64Reg_RCX, X64Reg_RDX, X64Reg_RSI, X64Reg_RDI, X64Reg_R8, X64Reg_R9, X64Reg_R10, X64Reg_R11, X64Reg_R12, X64Reg_R13, X64Reg_R14, X64Reg_R15 }; } else { highestReg = X64Reg_M128_XMM14; // Leave X64Reg_M128_XMM15 as a scratch reg validRegs = { X64Reg_M128_XMM0, X64Reg_M128_XMM1, X64Reg_M128_XMM2, X64Reg_M128_XMM3, X64Reg_M128_XMM4, X64Reg_M128_XMM5, X64Reg_M128_XMM6, X64Reg_M128_XMM7, X64Reg_M128_XMM8, X64Reg_M128_XMM9, X64Reg_M128_XMM10, X64Reg_M128_XMM11, X64Reg_M128_XMM12, X64Reg_M128_XMM13, X64Reg_M128_XMM14 /*, X64Reg_M128_XMM15*/}; } int totalRegs = (int)validRegs.size(); #define BF_DEQUE #ifdef BF_DEQUE std::deque vregStack; #else std::vector vregStack; #endif //vregStack.reserve(mNodes.size()); SizedArray vregHiPriStack; vregHiPriStack.reserve(mNodes.size()); SizedArray vregGraph; vregGraph.reserve(mNodes.size()); SizedArray orderedSpillList; // { BP_ZONE("AssignRegs:vregGraph build"); for (int vregIdx = (int)mNodes.size() - 1; vregIdx >= 0; vregIdx--) { auto node = &mNodes[vregIdx]; if (node->mWantsReg) { auto vregInfo = mContext->mVRegInfo[vregIdx]; bool canBeReg = false; if (regKind == RegKind_Ints) { if ((vregInfo->mType->IsInt()) || (vregInfo->mType->mTypeCode == BeTypeCode_Boolean) || (vregInfo->mType->mTypeCode == BeTypeCode_Pointer)) canBeReg = true; } else if (regKind == RegKind_Floats) { if ((vregInfo->mType->IsFloat()) || (vregInfo->mType->IsVector())) canBeReg = true; } if (canBeReg) { node->mInGraph = true; node->mGraphEdgeCount = 0; vregGraph.push_back(vregIdx); } else { node->mInGraph = false; } } } } for (int vregIdx = (int)mNodes.size() - 1; vregIdx >= 0; vregIdx--) { auto node = &mNodes[vregIdx]; if (node->mInGraph) { for (auto connNodeIdx : node->mEdges) { Node* connNode = &mNodes[connNodeIdx]; if (connNode->mInGraph) node->mGraphEdgeCount++; } } } // { int graphSize = (int)vregGraph.size(); BP_ZONE("AssignRegs:buildStack"); while (graphSize > 0) { bool hadNewStackItem = false; #ifdef _DEBUG // for (int graphIdx = 0; graphIdx < (int)vregGraph.size(); graphIdx++) // { // int validatedCount = 0; // int vregIdx = vregGraph[graphIdx]; // if (vregIdx == -1) // continue; // Node* node = &mNodes[vregIdx]; // for (auto connNodeIdx : node->mEdges) // { // Node* connNode = &mNodes[connNodeIdx]; // if (connNode->mInGraph) // validatedCount++; // } // BF_ASSERT(validatedCount == node->mGraphEdgeCount); // } #endif for (int graphIdx = 0; graphIdx < (int)vregGraph.size(); graphIdx++) { int vregIdx = vregGraph[graphIdx]; if (vregIdx == -1) continue; Node* node = &mNodes[vregIdx]; auto vregInfo = mContext->mVRegInfo[vregIdx]; int validRegCount = (int)validRegs.size(); BF_ASSERT(node->mGraphEdgeCount <= vregGraph.size() - 1); if ((node->mSpilled) || (node->mGraphEdgeCount < validRegCount)) { node->mInGraph = false; for (auto connNodeIdx : node->mEdges) { Node* connNode = &mNodes[connNodeIdx]; connNode->mGraphEdgeCount--; } vregGraph[graphIdx] = -1; graphSize--; if (!node->mSpilled) { vregStack.push_back(vregIdx); hadNewStackItem = true; } else { // We insert spills at the front so we can try to "optimistically" unspill them // after all the definite coloring is done #ifdef BF_DEQUE vregStack.push_front(vregIdx); #else vregStack.insert(vregStack.begin(), vregIdx); #endif } } } if (!hadNewStackItem) { BP_ZONE("Spill"); // We need to spill! int bestSpillVReg = -1; for (int regPassIdx = 0; regPassIdx < 2; regPassIdx++) { while (!orderedSpillList.empty()) { int vregIdx = orderedSpillList.back(); orderedSpillList.pop_back(); if (mNodes[vregIdx].mInGraph) { bestSpillVReg = vregIdx; break; } } if (bestSpillVReg != -1) break; // Order by mem cost orderedSpillList.reserve(vregGraph.size()); for (int graphIdx = 0; graphIdx < (int)vregGraph.size(); graphIdx++) { int vregIdx = vregGraph[graphIdx]; if (vregIdx == -1) continue; auto vregInfo = mContext->mVRegInfo[vregIdx]; if (vregInfo->mForceReg) continue; orderedSpillList.push_back(vregIdx); } std::sort(orderedSpillList.begin(), orderedSpillList.end(), [&](int lhs, int rhs) { return mNodes[lhs].mMemCost > mNodes[rhs].mMemCost; }); } /*int bestSpillVReg = -1; int bestSpillCost = INT_MAX; // We need to spill now! Try to spill something with the fewest references for (int graphIdx = 0; graphIdx < (int)vregGraph.size(); graphIdx++) { int vregIdx = vregGraph[graphIdx]; auto vregInfo = mContext->mVRegInfo[vregIdx]; if (vregInfo->mForceReg) continue; if (vregInfo->mRefCount < bestSpillCost) { bestSpillCost = vregInfo->mRefCount; bestSpillVReg = vregIdx; } }*/ if (bestSpillVReg != -1) { auto node = &mNodes[bestSpillVReg]; node->mSpilled = true; auto spillVRegInfo = mContext->mVRegInfo[bestSpillVReg]; spillVRegInfo->mSpilled = true; } else { mContext->Fail("Unable to spill vreg"); } } } } SizedArray globalRegUsedVec; globalRegUsedVec.resize(highestReg + 1); SizedArray regUsedVec; regUsedVec.resize(highestReg + 1); /*String dbgStr; if (mContext->mDebugging) dbgStr += "AssignRegs ";*/ BP_ZONE("AssignRegs:assign"); while (vregStack.size() > 0) { int vregIdx = vregStack.back(); vregStack.pop_back(); /*if (mContext->mDebugging) { dbgStr += StrFormat("VReg %d ", vregIdx); }*/ BeMCVRegInfo* vregInfo = mContext->mVRegInfo[vregIdx]; Node* node = &mNodes[vregIdx]; if (vregInfo->mVRegAffinity != -1) { auto affinityVRegInfo = mContext->mVRegInfo[vregInfo->mVRegAffinity]; if (affinityVRegInfo->mReg != X64Reg_None) node->AdjustRegCost(affinityVRegInfo->mReg, -2); } for (int i = 0; i <= highestReg; i++) regUsedVec[i] = false; if (mReserveParamRegs) { // This is a fallback case for when the "streams get crossed" during initialization- // IE: when arg0 gets assigned to RDX and arg1 gets assigned to RCX and we end up with: // MOV arg0, RCX // MOV arg1, RDX // Which is bad. for (auto reg : mContext->mParamsUsedRegs) { if (((int)reg < regUsedVec.size()) && (reg != vregInfo->mNaturalReg)) regUsedVec[(int)reg] = true; } } for (auto connNodeIdx : node->mEdges) { Node* connNode = &mNodes[connNodeIdx]; if (connNode->mInGraph) { auto connVRegInfo = mContext->mVRegInfo[connNodeIdx]; auto usedReg = mContext->GetFullRegister(connVRegInfo->mReg); BF_ASSERT(usedReg != X64Reg_None); regUsedVec[(int)usedReg] = true; } } auto bestReg = X64Reg_None; int bestRegCost = 0x07FFFFFF; // 0x0FFFFFFF is considered illegal for a reg, so set the mem cost to lower than that...; // This is the cost of just leaving the vreg as a memory access. In cases where we bind to a volatile // register, we need to consider the cost of preserving and restoring that register across calls, so // it cases where we have just a few accesses to this vreg but it spans a lot of calls then we just // leave it as memory if (!vregInfo->mForceReg) bestRegCost = node->mMemCost; //for (auto validReg : validRegs) int validRegCount = (int)validRegs.size(); for (int regIdx = 0; regIdx < validRegCount; regIdx++) { auto validReg = validRegs[regIdx]; if (!regUsedVec[(int)validReg]) { int checkCost = node->mRegCost[(int)validReg]; // If this register is non-volatile then we'd have to save and restore it, which costs... unless // some other vreg has already used this, then there's no additional cost if ((!globalRegUsedVec[(int)validReg]) && (!mContext->IsVolatileReg(validReg))) { int costMult = 1; if (regKind == BeMCColorizer::RegKind_Floats) costMult = 2; checkCost += 7 * costMult; } if (checkCost < bestRegCost) { // Try not to use registers that other params may want for (auto argReg : mContext->mParamsUsedRegs) { if (validReg == argReg) checkCost += 1; } } /*if (mContext->mDebugging) { dbgStr += StrFormat("Cost %d:%d ", validReg, checkCost); }*/ if (checkCost < bestRegCost) { bestReg = validReg; bestRegCost = checkCost; } } } if (mContext->mDebugging) { //auto itr = mContext->mDbgPreferredRegs.find(vregIdx); //if (itr != mContext->mDbgPreferredRegs.end()) X64CPURegister* regPtr = NULL; if (mContext->mDbgPreferredRegs.TryGetValue(vregIdx, ®Ptr)) { auto reg = *regPtr; if (reg == X64Reg_None) { if (!vregInfo->mForceReg) bestReg = reg; } else { if (!regUsedVec[(int)reg]) bestReg = reg; } } } if (vregInfo->mSpilled) { if (bestReg != X64Reg_None) { // We managed to optimistically unspill vregInfo->mSpilled = false; } } else { if (vregInfo->mForceReg) { // We didn't end up with a usable reg -- steal a reg from one of our edges if (bestReg == X64Reg_None) { int bestSpillVReg = -1; int bestSpillCost = INT_MAX; for (auto connNodeIdx : node->mEdges) { Node* connNode = &mNodes[connNodeIdx]; if (connNode->mInGraph) { auto connVRegInfo = mContext->mVRegInfo[connNodeIdx]; auto usedReg = mContext->GetFullRegister(connVRegInfo->mReg); if (connVRegInfo->mForceReg) continue; if (node->mRegCost[usedReg] < 0x07FFFFFFF) { if (connVRegInfo->mRefCount < bestSpillCost) { bestSpillCost = connVRegInfo->mRefCount; bestSpillVReg = connNodeIdx; } } } } if (bestSpillVReg != -1) { /*if (mContext->mDebugging) { dbgStr += StrFormat("StealingVReg %d ", bestSpillVReg); }*/ auto connNode = &mNodes[bestSpillVReg]; auto connVRegInfo = mContext->mVRegInfo[bestSpillVReg]; bestReg = mContext->GetFullRegister(connVRegInfo->mReg); connVRegInfo->mReg = X64Reg_None; connNode->mSpilled = true; connVRegInfo->mSpilled = true; connNode->mInGraph = false; for (auto connNodeEdgeIdx : connNode->mEdges) { Node* connNodeEdge = &mNodes[connNodeEdgeIdx]; connNodeEdge->mGraphEdgeCount--; } // We insert spills at the front so we can try to "optimistically" unspill them // after all the definite coloring is done vregStack.insert(vregStack.begin(), bestSpillVReg); } } // The only way we should have failed to allocate a register is if we spilled... BF_ASSERT((bestReg != X64Reg_None) /*|| (mReserveParamRegs)*/); } } vregInfo->mReg = mContext->ResizeRegister(bestReg, vregInfo->mType); if (bestReg != X64Reg_None) { node->mInGraph = true; globalRegUsedVec[(int)bestReg] = true; for (auto connNodeIdx : node->mEdges) { Node* connNode = &mNodes[connNodeIdx]; connNode->mGraphEdgeCount++; } } /*if (mContext->mDebugging) { dbgStr += StrFormat("Reg %d ", vregInfo->mReg); }*/ } /*if (!dbgStr.empty()) { dbgStr += "\n\n"; OutputDebugStr(dbgStr); }*/ /*bool dumpStats = true; if (dumpStats) { String str; str += "Register costs:\n"; str += " Mem "; for (int regNum = 0; regNum <= X64Reg_EDI; regNum++) { str += StrFormat("%4s", X64CPURegisters::GetRegisterName((X64CPURegister)regNum)); } str += "\n"; for (int liveVRregIdx = 0; liveVRregIdx < mContext->mVRegInfo.size(); liveVRregIdx++) { auto vregInfo = mContext->mVRegInfo[liveVRregIdx]; auto node = &mNodes[liveVRregIdx]; String name = mContext->ToString(BeMCOperand::FromVReg(liveVRregIdx)); str += StrFormat("%24s", name.c_str()); str += StrFormat(" %3d", node->mMemCost); for (int regNum = 0; regNum <= X64Reg_EDI; regNum++) { str += StrFormat(" %3d", node->mRegCost[regNum]); } str += "\n"; } OutputDebugStr(str); }*/ } bool BeMCColorizer::Validate() { #ifdef _DEBUG auto paramsLeft = mContext->mParamsUsedRegs; for (auto mcBlock : mContext->mBlocks) { for (auto inst : mcBlock->mInstructions) { if (paramsLeft.size() == 0) break; if (inst->IsMov()) { if (inst->mArg1.IsNativeReg()) { BF_ASSERT(inst->mArg0.IsVReg()); BF_ASSERT(mContext->GetFullRegister(inst->mArg1.mReg) == paramsLeft[0]); paramsLeft.erase(paramsLeft.begin()); auto vregInfo = mContext->mVRegInfo[inst->mArg0.mVRegIdx]; if (vregInfo->mReg != X64Reg_None) { auto checkReg = mContext->GetFullRegister(vregInfo->mReg); //auto itr = std::find(paramsLeft.begin(), paramsLeft.end(), checkReg); //if (itr != paramsLeft.end()) if (paramsLeft.Contains(checkReg)) { // This will happen if we have assigned a 'wrong' register to a parameter- // a register that is not the 'natural' register (ie: rcx for param0), // but it's a register that is already bound to another parameter which // will be needed later. Given the 'natural' order of RCX, RDX, R8, R9, // This is a valid order still: R10, RCX, R8, R9 since RCX will get written // to R10 first so it won't get clobbered, but: RDX, R10, R8, R9 is not // valid because RDX gets clobbered before it can be written to R10. return false; } } } } } } #endif return true; } ////////////////////////////////////////////////////////////////////////// BeMCLoopDetector::BeMCLoopDetector(BeMCContext* context) : mTrackingContext(context) { mMCContext = context; mTrackingContext.mTrackKindCount = 1; mTrackingContext.Init((int)mMCContext->mMCBlockAlloc.size()); mNodes.Resize(mMCContext->mMCBlockAlloc.size()); } void BeMCLoopDetector::DetectLoops(BeMCBlock* mcBlock, BeVTrackingList* predBlocksSeen) { mMCContext->mDetectLoopIdx++; auto node = &mNodes[mcBlock->mBlockIdx]; auto blocksSeen = mTrackingContext.Merge(node->mPredBlocksSeen, predBlocksSeen); if (blocksSeen == node->mPredBlocksSeen) return; node->mPredBlocksSeen = blocksSeen; //SizedArray addVec = { mcBlock->mBlockIdx }; //auto newBlocksSeen = mTrackingContext.Add(blocksSeen, addVec, false); auto newBlocksSeen = mTrackingContext.Add(blocksSeen, mcBlock->mBlockIdx, false); if (newBlocksSeen == blocksSeen) { // Our ID was already set, so this is a re-entry and thus we are looped mcBlock->mIsLooped = true; } blocksSeen = newBlocksSeen; for (auto succ : mcBlock->mSuccs) { DetectLoops(succ, blocksSeen); } } void BeMCLoopDetector::DetectLoops() { for (auto block : mMCContext->mBlocks) { for (auto succ : block->mSuccs) { if (succ->mBlockIdx < block->mBlockIdx) { auto prevBlock = mMCContext->mBlocks[block->mBlockIdx - 1]; //if ((!succ->mIsLooped) || (!prevBlock->mIsLooped)) { for (int setBlockIdx = succ->mBlockIdx; setBlockIdx < block->mBlockIdx; setBlockIdx++) mMCContext->mBlocks[setBlockIdx]->mIsLooped = true; } } } } } ////////////////////////////////////////////////////////////////////////// void BeMCBlock::AddPred(BeMCBlock* pred) { if (!mPreds.Contains(pred)) { pred->mSuccs.push_back(this); mPreds.push_back(pred); } } int BeMCBlock::FindLabelInstIdx(int labelIdx) { for (int instIdx = 0; instIdx < (int)mInstructions.size(); instIdx++) { auto inst = mInstructions[instIdx]; if ((inst->mKind == BeMCInstKind_Label) && (inst->mArg0.mLabelIdx == labelIdx)) return instIdx; } return -1; } ////////////////////////////////////////////////////////////////////////// void BeInstEnumerator::Next() { if (mRemoveCurrent) { if ((mWriteIdx > 0) && (mReadIdx < mBlock->mInstructions.size() - 1)) mContext->MergeInstFlags(mBlock->mInstructions[mWriteIdx - 1], mBlock->mInstructions[mReadIdx], mBlock->mInstructions[mReadIdx + 1]); mBlock->mInstructions[mReadIdx] = NULL; mRemoveCurrent = false; } else { if (mWriteIdx != mReadIdx) { mBlock->mInstructions[mWriteIdx] = mBlock->mInstructions[mReadIdx]; mBlock->mInstructions[mReadIdx] = NULL; } mWriteIdx++; } mReadIdx++; } ////////////////////////////////////////////////////////////////////////// BeMCContext::BeMCContext(BeCOFFObject* coffObject) : mOut(coffObject->mTextSect.mData), mLivenessContext(this), mVRegInitializedContext(this), mColorizer(this) { mLivenessContext.mTrackKindCount = 1; mCOFFObject = coffObject; mModule = NULL; mBeFunction = NULL; mActiveBeBlock = NULL; mActiveBlock = NULL; mActiveInst = NULL; mDbgFunction = NULL; mCompositeRetVRegIdx = -1; mTLSVRegIdx = -1; mStackSize = 0; mCurLabelIdx = 0; mCurPhiIdx = 0; mMaxCallParamCount = -1; mCurDbgLoc = NULL; mCurVRegsInit = NULL; mCurVRegsLive = NULL; mUseBP = false; mHasVAStart = false; mInsertInstIdxRef = NULL; mNativeIntType = mCOFFObject->mBeModule->mContext->GetPrimitiveType(BeTypeCode_Int64); mDebugging = false; mFailed = false; mDetectLoopIdx = 0; mLegalizationIterations = 0; } void BeMCContext::NotImpl() { Fail("Not implemented"); } void BeMCContext::Fail(const StringImpl& str) { String errStr = StrFormat("Failure during codegen of %s in %s: %s", mBeFunction->mName.c_str(), mModule->mModuleName.c_str(), str.c_str()); if (mActiveBlock != NULL) errStr += StrFormat("\n MCBlock: %s", mActiveBlock->mName.c_str()); if ((mActiveInst != NULL) && (mActiveInst->mDbgLoc != NULL)) { BeDumpContext dumpCtx; errStr += "\n DbgLoc : "; dumpCtx.ToString(errStr, mActiveInst->mDbgLoc); } BfpSystem_FatalError(errStr.c_str(), "FATAL ERROR"); } void BeMCContext::AssertFail(const StringImpl& str, int line) { Fail(StrFormat("Assert '%s' failed on line %d", str.c_str(), line)); } void BeMCContext::SoftFail(const StringImpl& str, BeDbgLoc* dbgLoc) { if (mFailed) return; mFailed = true; String errStr = StrFormat("Failure during codegen of %s: %s", mBeFunction->mName.c_str(), str.c_str()); if (dbgLoc != NULL) { auto dbgFile = dbgLoc->GetDbgFile(); if (dbgFile != NULL) errStr += StrFormat(" at line %d:%d in %s/%s", dbgLoc->mLine + 1, dbgLoc->mColumn + 1, dbgFile->mDirectory.c_str(), dbgFile->mFileName.c_str()); } mModule->mBeIRCodeGen->Fail(errStr); } String BeMCContext::ToString(const BeMCOperand& operand) { if (operand.mKind == BeMCOperandKind_NativeReg) { return String("%") + X64CPURegisters::GetRegisterName((int)operand.mReg); } if (operand.IsVRegAny()) { auto vregInfo = GetVRegInfo(operand); String str; mModule->ToString(str, GetType(operand)); str += " "; if (operand.IsVRegAny()) { auto vregInfo = GetVRegInfo(operand); if (operand.mKind == BeMCOperandKind_VRegAddr) str += "&"; if (operand.mKind == BeMCOperandKind_VRegLoad) str += "*"; if (vregInfo->mDbgVariable != NULL) str += "#" + vregInfo->mDbgVariable->mName + StrFormat("/%d", operand.mVRegIdx); else str += StrFormat("%%vreg%d", operand.mVRegIdx); } if (vregInfo->mReg != X64Reg_None) { str += "<"; str += X64CPURegisters::GetRegisterName((int)vregInfo->mReg); str += ">"; } else if (vregInfo->mForceReg) { str += ""; } if (vregInfo->mDisableR11) { str += ""; } if (vregInfo->mDisableR12) { str += ""; } if (vregInfo->mDisableR13) { str += ""; } if (vregInfo->mDisableRAX) { str += ""; } if (vregInfo->mDisableRDX) { str += ""; } if (vregInfo->mDisableEx) { str += ""; } if (vregInfo->mForceMem) { str += ""; } if (vregInfo->mIsRetVal) { str += ""; } if (vregInfo->mForceMerge) { str += ""; } if (vregInfo->mRelTo) { str += "("; str += ToString(vregInfo->mRelTo); if (vregInfo->mRelOffset) { str += "+"; str += ToString(vregInfo->mRelOffset); } if (vregInfo->mRelOffsetScale != 1) str += StrFormat("*%d", vregInfo->mRelOffsetScale); str += ")"; } return str; } if (operand.IsImmediate()) { String str; switch (operand.mKind) { case BeMCOperandKind_Immediate_i8: str += "i8 "; break; case BeMCOperandKind_Immediate_i16: str += "i16 "; break; case BeMCOperandKind_Immediate_i32: str += "i32 "; break; case BeMCOperandKind_Immediate_i64: str += "i64 "; break; case BeMCOperandKind_Immediate_HomeSize: str += "homesize"; break; case BeMCOperandKind_Immediate_Null: if (operand.mType != NULL) { mModule->ToString(str, operand.mType); str += " null"; } else str += "null"; return str; case BeMCOperandKind_Immediate_f32: return StrFormat("f32 %f", operand.mImmF32); case BeMCOperandKind_Immediate_f32_Packed128: return StrFormat("f32_packed %f", operand.mImmF32); case BeMCOperandKind_Immediate_f64: return StrFormat("f64 %f", operand.mImmF64); case BeMCOperandKind_Immediate_f64_Packed128: return StrFormat("f64_packed %f", operand.mImmF64); } //if (operand.mImmediate < 10) str += StrFormat("%lld", operand.mImmediate); /*else str += StrFormat("0x%llX", operand.mImmediate);*/ return str; } if (operand.mKind == BeMCOperandKind_Block) return "%" + operand.mBlock->mName; if (operand.mKind == BeMCOperandKind_Label) return StrFormat("%%label%d", operand.mLabelIdx); if (operand.mKind == BeMCOperandKind_CmpKind) return BeDumpContext::ToString(operand.mCmpKind); if (operand.mKind == BeMCOperandKind_CmpResult) { String result = StrFormat("%%CmpResult%d", operand.mCmpResultIdx); auto& cmpResult = mCmpResults[operand.mCmpResultIdx]; if (cmpResult.mResultVRegIdx != -1) result += StrFormat("", cmpResult.mResultVRegIdx); result += " "; result += BeDumpContext::ToString(cmpResult.mCmpKind); return result; } if (operand.mKind == BeMCOperandKind_NotResult) { auto mcResult = GetOperand(operand.mNotResult->mValue, true, true); String result = "NOT "; result += ToString(mcResult); return result; } if (operand.mKind == BeMCOperandKind_Symbol) return mCOFFObject->mSymbols[operand.mSymbolIdx]->mName; if (operand.mKind == BeMCOperandKind_SymbolAddr) return "&" + mCOFFObject->mSymbols[operand.mSymbolIdx]->mName; if (operand.mKind == BeMCOperandKind_MemSetInfo) return StrFormat("size=%d val=%d align=%d", operand.mMemSetInfo.mSize, operand.mMemSetInfo.mValue, operand.mMemSetInfo.mAlign); if (operand.mKind == BeMCOperandKind_MemCpyInfo) return StrFormat("size=%d align=%d", operand.mMemCpyInfo.mSize, operand.mMemCpyInfo.mAlign); if (operand.mKind == BeMCOperandKind_VRegPair) { String str = "("; str += ToString(BeMCOperand::FromEncoded(operand.mVRegPair.mVRegIdx0)); str += ", "; str += ToString(BeMCOperand::FromEncoded(operand.mVRegPair.mVRegIdx1)); str += ")"; return str; } if (operand.mKind == BeMCOperandKind_Phi) return StrFormat("%%PHI%d", operand.mPhi->mIdx); if (operand.mKind == BeMCOperandKind_PreserveFlag) { if (operand.mPreserveFlag == BeMCPreserveFlag_NoRestore) return "PreserveFlag:NoReturn"; } if (operand.mKind == BeMCOperandKind_ConstAgg) { BeDumpContext dumpContext; String str = "const "; str += dumpContext.ToString(operand.mConstant); return str; } return "???"; } BeMCOperand BeMCContext::GetOperand(BeValue* value, bool allowMetaResult, bool allowFail) { if (value == NULL) return BeMCOperand(); 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; vregInfo->mDisableR12 = true; vregInfo->mDisableR13 = true; mTLSVRegIdx = tlsVReg.mVRegIdx; } auto sym = mCOFFObject->GetSymbol(globalVar); if (sym != NULL) { BeMCOperand mcOperand; mcOperand.mKind = BeMCOperandKind_SymbolAddr; mcOperand.mSymbolIdx = sym->mIdx; return mcOperand; } } break; case BeCastConstant::TypeId: { auto constant = (BeCastConstant*)value; BeMCOperand mcOperand; auto relTo = GetOperand(constant->mTarget); if (relTo.mKind == BeMCOperandKind_Immediate_Null) { mcOperand.mKind = BeMCOperandKind_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: { auto constant = (BeConstant*)value; BeMCOperand mcOperand; switch (constant->mType->mTypeCode) { case BeTypeCode_Boolean: case BeTypeCode_Int8: mcOperand.mKind = BeMCOperandKind_Immediate_i8; break; case BeTypeCode_Int16: mcOperand.mKind = BeMCOperandKind_Immediate_i16; break; case BeTypeCode_Int32: mcOperand.mKind = BeMCOperandKind_Immediate_i32; break; case BeTypeCode_Int64: mcOperand.mKind = BeMCOperandKind_Immediate_i64; break; case BeTypeCode_Float: mcOperand.mImmF32 = constant->mDouble; mcOperand.mKind = BeMCOperandKind_Immediate_f32; return mcOperand; case BeTypeCode_Double: mcOperand.mImmF64 = constant->mDouble; mcOperand.mKind = BeMCOperandKind_Immediate_f64; return mcOperand; case BeTypeCode_Pointer: { if (constant->mTarget == NULL) { mcOperand.mKind = BeMCOperandKind_Immediate_Null; mcOperand.mType = constant->mType; return mcOperand; } else { auto relTo = GetOperand(constant->mTarget); if (relTo.mKind == BeMCOperandKind_Immediate_Null) { mcOperand.mKind = BeMCOperandKind_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 = BeMCOperandKind_Immediate_i64; break; default: Fail("Unhandled constant type"); } mcOperand.mImmediate = constant->mInt64; return mcOperand; } break; case BeStructConstant::TypeId: { auto structConstant = (BeStructConstant*)value; BeMCOperand mcOperand; mcOperand.mKind = BeMCOperandKind_ConstAgg; mcOperand.mConstant = structConstant; return mcOperand; } case BeGEP1Constant::TypeId: { auto gepConstant = (BeGEP1Constant*)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 = ptrType->mElementType; byteOffset += gepConstant->mIdx0 * ptrType->mElementType->GetStride(); result = AllocRelativeVirtualReg(ptrType, 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 = BeMCOperandKind_VReg; return result; } break; case BeGEP2Constant::TypeId: { auto gepConstant = (BeGEP2Constant*)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->GetStride(); if (ptrType->mElementType->mTypeCode == BeTypeCode_Struct) { BeStructType* structType = (BeStructType*)ptrType->mElementType; auto& structMember = structType->mMembers[gepConstant->mIdx1]; elementType = structMember.mType; byteOffset = structMember.mByteOffset; } else if (ptrType->mElementType->mTypeCode == BeTypeCode_SizedArray) { BEMC_ASSERT(ptrType->mElementType->mTypeCode == BeTypeCode_SizedArray); auto arrayType = (BeSizedArrayType*)ptrType->mElementType; elementType = arrayType->mElementType; byteOffset = gepConstant->mIdx1 * elementType->GetStride(); } else { BEMC_ASSERT(ptrType->mElementType->mTypeCode == BeTypeCode_Vector); auto arrayType = (BeVectorType*)ptrType->mElementType; elementType = arrayType->mElementType; byteOffset = gepConstant->mIdx1 * elementType->GetStride(); } 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 = BeMCOperandKind_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) { BeMCOperand mcOperand; mcOperand.mKind = BeMCOperandKind_SymbolAddr; mcOperand.mSymbolIdx = sym->mIdx; return mcOperand; } } break; case BeCallInst::TypeId: { auto callInst = (BeCallInst*)value; if (callInst->mInlineResult != NULL) return GetOperand(callInst->mInlineResult); } break; case BeDbgVariable::TypeId: { } } BeMCOperand* operandPtr = NULL; mValueToOperand.TryGetValue(value, &operandPtr); //auto itr = mValueToOperand.find(value); 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) { if (allowFail) return BeMCOperand(); BeMCOperand mcOperand; mcOperand.mKind = BeMCOperandKind_Immediate_i64; mcOperand.mImmediate = 0; return mcOperand; } auto operand = *operandPtr; if ((operand.mKind == BeMCOperandKind_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 prevBlock(mActiveBlock, mcBlock); SetAndRestoreValue 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); BeMCOperand falseLabel = BeMCOperand::FromLabel(mCurLabelIdx++); BeMCOperand trueLabel = BeMCOperand::FromLabel(mCurLabelIdx++); BeMCOperand endLabel = BeMCOperand::FromLabel(mCurLabelIdx++); CreateCondBr(mActiveBlock, operand, trueLabel, falseLabel); AllocInst(BeMCInstKind_Label, falseLabel); AllocInst(BeMCInstKind_Mov, result, BeMCOperand::FromImmediate(0)); AllocInst(BeMCInstKind_Br, endLabel); AllocInst(BeMCInstKind_Label, trueLabel); AllocInst(BeMCInstKind_Mov, result, BeMCOperand::FromImmediate(1)); AllocInst(BeMCInstKind_Label, endLabel); } else { // Attempt to find common ancestor to insert a 'def' at SizedArray 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 prevActiveBlock(mActiveBlock, lowestBlock); SetAndRestoreValue 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; } } BeMCOperand doneLabel = BeMCOperand::FromLabel(mCurLabelIdx++); CreatePhiAssign(mActiveBlock, operand, result, doneLabel); // Don't use an explicit dbgLoc SetAndRestoreValue prevDbgLoc(mCurDbgLoc, NULL); AllocInst(BeMCInstKind_Label, doneLabel); } return result; } if ((operand.mKind == BeMCOperandKind_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 = BeMCOperand::FromVReg(cmpResult.mResultVRegIdx); } if ((operand.mKind == BeMCOperandKind_NotResult) && (!allowMetaResult)) { auto mcValue = GetOperand(operand.mNotResult->mValue, false, allowFail); operand = AllocVirtualReg(GetType(mcValue)); CreateDefineVReg(operand); AllocInst(BeMCInstKind_Mov, operand, mcValue); BeMCOperand xorVal; xorVal.mKind = BeMCOperandKind_Immediate_i8; xorVal.mImmediate = 0x1; AllocInst(BeMCInstKind_Xor, operand, xorVal); } return operand; } BeMCOperand BeMCContext::TryToVector(BeValue* value) { auto operand = GetOperand(value); auto type = GetType(operand); if (!type->IsPointer()) return operand; return CreateLoad(operand); } BeType* BeMCContext::GetType(const BeMCOperand& operand) { if (operand.mKind == BeMCOperandKind_NativeReg) { if ((operand.mReg >= X64Reg_RAX) && (operand.mReg <= X64Reg_EFL)) return mModule->mContext->GetPrimitiveType(BeTypeCode_Int64); if ((operand.mReg >= X64Reg_EAX) && (operand.mReg <= X64Reg_R15D)) return mModule->mContext->GetPrimitiveType(BeTypeCode_Int32); if ((operand.mReg >= X64Reg_AX) && (operand.mReg <= X64Reg_R15W)) return mModule->mContext->GetPrimitiveType(BeTypeCode_Int16); if ((operand.mReg >= X64Reg_AL) && (operand.mReg <= X64Reg_R15B)) return mModule->mContext->GetPrimitiveType(BeTypeCode_Int8); if ((operand.mReg >= X64Reg_XMM0_f64) && (operand.mReg <= X64Reg_XMM15_f64)) return mModule->mContext->GetPrimitiveType(BeTypeCode_Double); if ((operand.mReg >= X64Reg_XMM0_f32) && (operand.mReg <= X64Reg_XMM15_f32)) return mModule->mContext->GetPrimitiveType(BeTypeCode_Float); if ((operand.mReg >= X64Reg_M128_XMM0) && (operand.mReg <= X64Reg_M128_XMM15)) return mModule->mContext->GetPrimitiveType(BeTypeCode_M128); } if (operand.mKind == BeMCOperandKind_VReg) return mVRegInfo[operand.mVRegIdx]->mType; if (operand.mKind == BeMCOperandKind_VRegAddr) return mModule->mContext->GetPointerTo(mVRegInfo[operand.mVRegIdx]->mType); if (operand.mKind == BeMCOperandKind_VRegLoad) { auto type = mVRegInfo[operand.mVRegIdx]->mType; BEMC_ASSERT(type->IsPointer()); return ((BePointerType*)type)->mElementType; } switch (operand.mKind) { case BeMCOperandKind_Immediate_i8: return mModule->mContext->GetPrimitiveType(BeTypeCode_Int8); break; case BeMCOperandKind_Immediate_i16: return mModule->mContext->GetPrimitiveType(BeTypeCode_Int16); break; case BeMCOperandKind_Immediate_i32: return mModule->mContext->GetPrimitiveType(BeTypeCode_Int32); break; case BeMCOperandKind_Immediate_i64: case BeMCOperandKind_Immediate_HomeSize: return mModule->mContext->GetPrimitiveType(BeTypeCode_Int64); break; case BeMCOperandKind_Immediate_Null: return operand.mType; break; case BeMCOperandKind_Immediate_f32: case BeMCOperandKind_Immediate_f32_Packed128: return mModule->mContext->GetPrimitiveType(BeTypeCode_Float); break; case BeMCOperandKind_Immediate_f64: case BeMCOperandKind_Immediate_f64_Packed128: return mModule->mContext->GetPrimitiveType(BeTypeCode_Double); break; } if (operand.mKind == BeMCOperandKind_SymbolAddr) { auto symbol = mCOFFObject->mSymbols[operand.mSymbolIdx]; if (symbol->mType == NULL) return NULL; return mModule->mContext->GetPointerTo(symbol->mType); } if (operand.mKind == BeMCOperandKind_Symbol) { auto symbol = mCOFFObject->mSymbols[operand.mSymbolIdx]; return symbol->mType; } if (operand.mKind == BeMCOperandKind_CmpResult) { return mModule->mContext->GetPrimitiveType(BeTypeCode_Boolean); } if (operand.mKind == BeMCOperandKind_ConstAgg) { return operand.mConstant->mType; } return NULL; } bool BeMCContext::AreTypesEquivalent(BeType* type0, BeType* type1) { if ((type0->IsFloat()) != (type1->IsFloat())) return false; return type0->mSize == type1->mSize; } void BeMCContext::AddRelRefs(BeMCOperand& operand, int refCount) { if (!operand.IsVRegAny()) return; auto vregInfo = mVRegInfo[operand.mVRegIdx]; vregInfo->mRefCount += refCount; if (vregInfo->mRelTo) AddRelRefs(vregInfo->mRelTo, vregInfo->mRefCount + refCount); if (vregInfo->mRelOffset) AddRelRefs(vregInfo->mRelOffset, vregInfo->mRefCount + refCount); } int BeMCContext::FindSafeInstInsertPos(int instIdx, bool forceSafeCheck) { auto inst = mActiveBlock->mInstructions[instIdx]; bool doSafeCheck = false; if (forceSafeCheck) doSafeCheck = true; if ((inst->mKind == BeMCInstKind_Call) || (inst->mKind == BeMCInstKind_MemSet) || (inst->mKind == BeMCInstKind_MemCpy)) doSafeCheck = true; if ((inst->IsMov()) && (inst->mArg0.IsNativeReg())) doSafeCheck = true; if (!doSafeCheck) return instIdx; // If we're in a CALL then move this Def before the param loads int bestIdx = instIdx; while (bestIdx > 0) { inst = mActiveBlock->mInstructions[bestIdx - 1]; bestIdx--; if (inst->mKind == BeMCInstKind_PreserveVolatiles) return bestIdx; } return bestIdx; } BeMCInst* BeMCContext::AllocInst(int insertIdx) { auto mcInst = mAlloc.Alloc(); mcInst->mKind = BeMCInstKind_None; mcInst->mDbgLoc = mCurDbgLoc; mcInst->mVRegsInitialized = mCurVRegsInit; mcInst->mLiveness = mCurVRegsLive; if ((insertIdx == -1) && (mInsertInstIdxRef != NULL)) insertIdx = (*mInsertInstIdxRef)++; if (insertIdx == -1) mActiveBlock->mInstructions.push_back(mcInst); else if ((insertIdx < mActiveBlock->mInstructions.mSize) && (mActiveBlock->mInstructions[insertIdx] == NULL)) mActiveBlock->mInstructions[insertIdx] = mcInst; else mActiveBlock->mInstructions.Insert(insertIdx, mcInst); return mcInst; } BeMCInst* BeMCContext::AllocInst(BeMCInstKind instKind, int insertIdx) { auto mcInst = AllocInst(insertIdx); mcInst->mKind = instKind; return mcInst; } BeMCInst* BeMCContext::AllocInst(BeMCInstKind instKind, const BeMCOperand& arg0, int insertIdx) { auto mcInst = AllocInst(insertIdx); mcInst->mKind = instKind; mcInst->mArg0 = arg0; return mcInst; } BeMCInst* BeMCContext::AllocInst(BeMCInstKind instKind, const BeMCOperand& arg0, const BeMCOperand& arg1, int insertIdx) { auto mcInst = AllocInst(insertIdx); mcInst->mKind = instKind; mcInst->mArg0 = arg0; mcInst->mArg1 = arg1; return mcInst; } void BeMCContext::MergeInstFlags(BeMCInst* prevInst, BeMCInst* inst, BeMCInst* nextInst) { if (prevInst->mVRegsInitialized == inst->mVRegsInitialized) return; if ((inst->mVRegsInitialized != NULL) && (nextInst->mVRegsInitialized != NULL)) nextInst->mVRegsInitialized = mVRegInitializedContext.MergeChanges(nextInst->mVRegsInitialized, inst->mVRegsInitialized); } void BeMCContext::RemoveInst(BeMCBlock* block, int instIdx, bool needChangesMerged, bool removeFromList) { // If neither the instruction before or after this one shares the vregsInitialized flags, then we need to // merge down our Changes to the next instruction auto inst = block->mInstructions[instIdx]; if (instIdx > 0) { auto prevInst = block->mInstructions[instIdx - 1]; if (prevInst->mVRegsInitialized == inst->mVRegsInitialized) needChangesMerged = false; } if (needChangesMerged) { if (instIdx < (int)block->mInstructions.size() - 1) { auto nextInst = block->mInstructions[instIdx + 1]; if ((inst->mVRegsInitialized != NULL) && (nextInst->mVRegsInitialized != NULL)) { nextInst->mVRegsInitialized = mVRegInitializedContext.MergeChanges(nextInst->mVRegsInitialized, inst->mVRegsInitialized); } } } if (removeFromList) block->mInstructions.RemoveAt(instIdx); } BeMCOperand BeMCContext::GetCallArgVReg(int argIdx, BeTypeCode typeCode) { int pIdx = argIdx; BeMCNativeTypeCode vregType = BeMCNativeTypeCode_Int64; switch (typeCode) { case BeTypeCode_Int8: vregType = BeMCNativeTypeCode_Int8; break; case BeTypeCode_Int16: vregType = BeMCNativeTypeCode_Int16; break; case BeTypeCode_Int32: vregType = BeMCNativeTypeCode_Int32; break; case BeTypeCode_Int64: vregType = BeMCNativeTypeCode_Int64; break; case BeTypeCode_Float: vregType = BeMCNativeTypeCode_Float; break; case BeTypeCode_Double: vregType = BeMCNativeTypeCode_Double; break; case BeTypeCode_M128: vregType = BeMCNativeTypeCode_M128; break; case BeTypeCode_M256: vregType = BeMCNativeTypeCode_M256; break; case BeTypeCode_M512: vregType = BeMCNativeTypeCode_M512; break; default: typeCode = BeTypeCode_Int64; vregType = BeMCNativeTypeCode_Int64; break; } Array& callArgVRegs = mCallArgVRegs[vregType]; while (pIdx >= (int)callArgVRegs.size()) callArgVRegs.push_back(-1); if (callArgVRegs[pIdx] == -1) { auto nativeType = mModule->mContext->GetPrimitiveType(typeCode); auto nativePtrType = mModule->mContext->GetPointerTo(nativeType); auto mcArg = AllocVirtualReg(nativePtrType); auto vregInfo = mVRegInfo[mcArg.mVRegIdx]; vregInfo->mMustExist = true; vregInfo->mIsExpr = true; vregInfo->mRelTo = BeMCOperand::FromReg(X64Reg_RSP); vregInfo->mRelOffset = BeMCOperand::FromImmediate(argIdx * 8); callArgVRegs[pIdx] = mcArg.mVRegIdx; mcArg.mKind = BeMCOperandKind_VRegLoad; return mcArg; } else { auto mcArg = BeMCOperand::FromVReg(callArgVRegs[pIdx]); mcArg.mKind = BeMCOperandKind_VRegLoad; return mcArg; } } BeMCOperand BeMCContext::CreateCall(const BeMCOperand &func, const SizedArrayImpl& args, BeType* retType, BfIRCallingConv callingConv, bool structRet, bool noReturn, bool isVarArg) { SizedArray opArgs; for (auto itr = args.begin(); itr != args.end(); ++itr) { auto& arg = *itr; opArgs.push_back(GetOperand(arg)); } return CreateCall(func, opArgs, retType, callingConv, structRet, noReturn, isVarArg); } BeMCOperand BeMCContext::CreateLoad(const BeMCOperand& mcTarget) { if (mcTarget.mKind == BeMCOperandKind_Immediate_Null) { auto fakeType = GetType(mcTarget); auto fakePtr = AllocVirtualReg(fakeType); CreateDefineVReg(fakePtr); AllocInst(BeMCInstKind_Mov, fakePtr, BeMCOperand::FromImmediate(0)); return CreateLoad(fakePtr); } BeMCOperand result; auto loadedTarget = BeMCOperand::ToLoad(mcTarget); auto targetType = GetType(loadedTarget); result = AllocVirtualReg(targetType); auto vregInfo = GetVRegInfo(result); vregInfo->mIsExpr = true; vregInfo->mRelTo = loadedTarget; result.mKind = BeMCOperandKind_VReg; AllocInst(BeMCInstKind_DefLoad, result); return result; } static bool NeedsDecompose(BeConstant* constant) { if (auto arrayConst = BeValueDynCast(constant)) { for (auto& val : arrayConst->mMemberValues) { if (NeedsDecompose(val)) return true; } return false; } if (auto globalVar = BeValueDynCast(constant)) { return true; } else if (auto castConst = BeValueDynCast(constant)) { return true; } else if (auto castConst = BeValueDynCast(constant)) { if (auto targetConstant = BeValueDynCast(castConst->mValue)) return NeedsDecompose(targetConstant); } else if (auto castConst = BeValueDynCast(constant)) { return NeedsDecompose(castConst->mTarget); } else if (auto castConst = BeValueDynCast(constant)) { return NeedsDecompose(castConst->mTarget); } return false; } void BeMCContext::CreateStore(BeMCInstKind instKind, const BeMCOperand& val, const BeMCOperand& ptr) { BeMCOperand mcVal = val; BeMCOperand mcPtr = ptr; if (mcVal.mKind == BeMCOperandKind_ConstAgg) { if (auto aggConst = BeValueDynCast(mcVal.mConstant)) { if (NeedsDecompose(mcVal.mConstant)) { int offset = 0; auto aggType = aggConst->GetType(); for (int memberIdx = 0; memberIdx < (int)aggConst->mMemberValues.size(); memberIdx++) { auto val = aggConst->mMemberValues[memberIdx]; BeType* elemType = NULL; if (aggType->IsSizedArray()) elemType = ((BeSizedArrayType*)aggType)->mElementType; else { auto& memberInfo = ((BeStructType*)aggType)->mMembers[memberIdx]; offset = memberInfo.mByteOffset; elemType = memberInfo.mType; } if (elemType->mSize == 0) continue; auto destOperand = AllocVirtualReg(mModule->mContext->GetPointerTo(elemType)); auto vregInfo = GetVRegInfo(destOperand); vregInfo->mDefOnFirstUse = true; vregInfo->mRelTo = mcPtr; vregInfo->mIsExpr = true; vregInfo->mRelOffset = BeMCOperand::FromImmediate(offset); //destOperand.mKind = BeMCOperandKind_VRegLoad; auto elementVal = GetOperand(val); //AllocInst(instKind, destOperand, elementVal); CreateStore(instKind, elementVal, destOperand); offset += elemType->GetStride(); } return; } } } // Addr mov infers something like a "int* intPtr = &intVal" if (mcVal.mKind == BeMCOperandKind_VRegAddr) { auto vregInfo = GetVRegInfo(mcVal); vregInfo->mForceMem = true; CheckForce(vregInfo); } if (mcPtr.mKind == BeMCOperandKind_VRegAddr) { mcPtr.mKind = BeMCOperandKind_VReg; AllocInst(instKind, mcPtr, mcVal); } else if (mcPtr.mKind == BeMCOperandKind_VReg) { mcPtr.mKind = BeMCOperandKind_VRegLoad; AllocInst(instKind, mcPtr, mcVal); } else if (mcPtr.mKind == BeMCOperandKind_SymbolAddr) { mcPtr.mKind = BeMCOperandKind_Symbol; AllocInst(instKind, mcPtr, mcVal); } else { mcPtr = CreateLoad(mcPtr); AllocInst(instKind, mcPtr, mcVal); } } BeMCOperand BeMCContext::CreateCall(const BeMCOperand& func, const SizedArrayImpl& args, BeType* retType, BfIRCallingConv callingConv, bool structRet, bool noReturn, bool isVarArg) { BeMCOperand mcResult; //TODO: Allow user to directly specify ret addr with "sret" attribute int argOfs = 0; X64CPURegister compositeRetReg = X64Reg_None; bool flipFirstRegs = (structRet) && (callingConv == BfIRCallingConv_ThisCall); if ((retType != NULL) && (retType->IsNonVectorComposite())) { mcResult = AllocVirtualReg(retType); auto vregInfo = GetVRegInfo(mcResult); vregInfo->mMustExist = true; CreateDefineVReg(mcResult); // 'this' always goes in RCX, so push compositeRetReg out by one compositeRetReg = (callingConv == BfIRCallingConv_ThisCall) ? X64Reg_RDX : X64Reg_RCX; AllocInst(BeMCInstKind_Mov, BeMCOperand::FromReg(compositeRetReg), BeMCOperand::FromVRegAddr(mcResult.mVRegIdx)); argOfs = 1; } bool didPreserveRegs = false; auto _AddPreserveRegs = [&]() { if (noReturn) AllocInst(BeMCInstKind_PreserveVolatiles, BeMCOperand::FromPreserveFlag(BeMCPreserveFlag_NoRestore)); else AllocInst(BeMCInstKind_PreserveVolatiles); didPreserveRegs = true; }; int argCount = (int)args.size() + argOfs; int dynStackSize = 0; if (dynStackSize > 0) AllocInst(BeMCInstKind_Sub, BeMCOperand::FromReg(X64Reg_RSP), BeMCOperand::FromImmediate(dynStackSize)); struct _ShadowReg { X64CPURegister mFReg; X64CPURegister mIReg; }; SizedArray<_ShadowReg, 8> shadowRegs; mMaxCallParamCount = BF_MAX(mMaxCallParamCount, argCount); for (int argIdx = args.size() - 1; argIdx >= 0; argIdx--) { if ((argIdx == 0) && (compositeRetReg == X64Reg_RDX)) argOfs = 0; auto mcValue = args[argIdx]; auto argType = GetType(mcValue); X64CPURegister useReg = X64Reg_None; int useArgIdx = argIdx + argOfs; if (argType->IsVector()) { switch (useArgIdx) { case 0: useReg = X64Reg_M128_XMM0; break; case 1: useReg = X64Reg_M128_XMM1; break; case 2: useReg = X64Reg_M128_XMM2; break; case 3: useReg = X64Reg_M128_XMM3; break; } } else if (argType->IsFloat()) { switch (useArgIdx) { case 0: useReg = (argType->mTypeCode == BeTypeCode_Float) ? X64Reg_XMM0_f32 : X64Reg_XMM0_f64; break; case 1: useReg = (argType->mTypeCode == BeTypeCode_Float) ? X64Reg_XMM1_f32 : X64Reg_XMM1_f64; break; case 2: useReg = (argType->mTypeCode == BeTypeCode_Float) ? X64Reg_XMM2_f32 : X64Reg_XMM2_f64; break; case 3: useReg = (argType->mTypeCode == BeTypeCode_Float) ? X64Reg_XMM3_f32 : X64Reg_XMM3_f64; break; } if (isVarArg) { X64CPURegister shadowReg = X64Reg_None; switch (useArgIdx) { case 0: shadowReg = !flipFirstRegs ? X64Reg_RCX : X64Reg_RDX; break; case 1: shadowReg = !flipFirstRegs ? X64Reg_RDX : X64Reg_RCX; break; case 2: shadowReg = X64Reg_R8; break; case 3: shadowReg = X64Reg_R9; break; } if ((shadowReg != X64Reg_None) && (useReg != X64Reg_None)) { shadowRegs.push_back(_ShadowReg{ useReg, shadowReg }); } } } else { switch (useArgIdx) { case 0: useReg = !flipFirstRegs ? X64Reg_RCX : X64Reg_RDX; break; case 1: useReg = !flipFirstRegs ? X64Reg_RDX : X64Reg_RCX; break; case 2: useReg = X64Reg_R8; break; case 3: useReg = X64Reg_R9; break; } } if ((!argType->IsNonVectorComposite()) && (!isVarArg)) // VarArg uses full 64 bits useReg = ResizeRegister(useReg, argType); if (mcValue.mKind == BeMCOperandKind_VRegAddr) { auto vregInfo = GetVRegInfo(mcValue); vregInfo->mForceMem = true; CheckForce(vregInfo); } if (useReg != X64Reg_None) { // We want to do the non-register params before the PreserveRegs call, // because those may be a memory-to-memory mov, which will result in // a temporary variable which will allocate a register which may // conflict with our param regs if (!didPreserveRegs) _AddPreserveRegs(); AllocInst(BeMCInstKind_Mov, BeMCOperand::FromReg(useReg), mcValue); } else { auto useTypeCode = argType->mTypeCode; if (isVarArg) { if (argType->IsFloat()) useTypeCode = BeTypeCode_Double; else useTypeCode = BeTypeCode_Int64; } auto callArgVReg = GetCallArgVReg(useArgIdx, useTypeCode); // Do a 'def' for every usage, to clear out the 'init' at all paths CreateDefineVReg(BeMCOperand::FromVReg(callArgVReg.mVRegIdx)); if (argType->IsNonVectorComposite()) { BEMC_ASSERT(mcValue.mKind == BeMCOperandKind_VReg); AllocInst(BeMCInstKind_Mov, callArgVReg, BeMCOperand::FromVRegAddr(mcValue.mVRegIdx)); } else AllocInst(BeMCInstKind_Mov, callArgVReg, mcValue); } } if (!didPreserveRegs) _AddPreserveRegs(); auto mcFunc = func; for (auto& shadowReg : shadowRegs) // Do float shadowing { AllocInst(BeMCInstKind_MovRaw, BeMCOperand::FromReg(shadowReg.mIReg), BeMCOperand::FromReg(shadowReg.mFReg)); } AllocInst(BeMCInstKind_Call, mcFunc); if (dynStackSize > 0) AllocInst(BeMCInstKind_Add, BeMCOperand::FromReg(X64Reg_RSP), BeMCOperand::FromImmediate(dynStackSize)); if ((!mcResult) && (retType != NULL) && (retType->mTypeCode != BeTypeCode_None)) { mcResult = AllocVirtualReg(retType); CreateDefineVReg(mcResult); X64CPURegister resultReg; if (retType->IsFloat()) { resultReg = ResizeRegister(X64Reg_M128_XMM0, retType->mSize); } else if (retType->IsVector()) { resultReg = X64Reg_M128_XMM0; } else { BEMC_ASSERT(retType->IsIntable()); resultReg = ResizeRegister(X64Reg_RAX, retType->mSize); } AllocInst(BeMCInstKind_Mov, mcResult, BeMCOperand::FromReg(resultReg)); } if (noReturn) AllocInst(BeMCInstKind_RestoreVolatiles, BeMCOperand::FromPreserveFlag(BeMCPreserveFlag_NoRestore)); else AllocInst(BeMCInstKind_RestoreVolatiles); return mcResult; } void BeMCContext::CreateMemSet(const BeMCOperand& addr, uint8 val, int size, int align) { if ((size == 8) || (size == 4) || (size == 2) || (size == 1)) { BeType* type = NULL; BeMCOperand zeroVal; zeroVal.mImmediate = 0; switch (size) { case 8: type = mModule->mContext->GetPrimitiveType(BeTypeCode_Int64); zeroVal.mKind = BeMCOperandKind_Immediate_i64; zeroVal.mImmediate = ((int64)val << 56) | ((int64)val << 48) | ((int64)val << 40) | ((int64)val << 32) | (val << 24) | (val << 16) | (val << 8) | val; break; case 4: type = mModule->mContext->GetPrimitiveType(BeTypeCode_Int32); zeroVal.mKind = BeMCOperandKind_Immediate_i32; zeroVal.mImmediate = (val << 24) | (val << 16) | (val << 8) | val; break; case 2: type = mModule->mContext->GetPrimitiveType(BeTypeCode_Int16); zeroVal.mKind = BeMCOperandKind_Immediate_i16; zeroVal.mImmediate = (val << 8) | val; break; case 1: type = mModule->mContext->GetPrimitiveType(BeTypeCode_Int8); zeroVal.mKind = BeMCOperandKind_Immediate_i8; zeroVal.mImmediate = val; break; } auto ptrType = mModule->mContext->GetPointerTo(type); auto result = AllocVirtualReg(ptrType); CreateDefineVReg(result); auto vregInfo = GetVRegInfo(result); vregInfo->mRelTo = addr; vregInfo->mIsExpr = true; BeMCOperand dest; dest.mKind = BeMCOperandKind_VRegLoad; dest.mVRegIdx = result.mVRegIdx; AllocInst(BeMCInstKind_Mov, dest, zeroVal); return; } if (addr.IsVRegAny()) { auto vregInfo = GetVRegInfo(addr); if (!vregInfo->mIsRetVal) vregInfo->mForceMem = true; CheckForce(vregInfo); } if (size <= 256) { auto temp = addr; BeMCOperand mcMemSetInfo; mcMemSetInfo.mKind = BeMCOperandKind_MemSetInfo; mcMemSetInfo.mMemSetInfo.mSize = size; mcMemSetInfo.mMemSetInfo.mAlign = align; mcMemSetInfo.mMemSetInfo.mValue = val; AllocInst(BeMCInstKind_PreserveVolatiles, BeMCOperand::FromReg(X64Reg_R11)); AllocInst(BeMCInstKind_MemSet, mcMemSetInfo, temp); AllocInst(BeMCInstKind_RestoreVolatiles, BeMCOperand::FromReg(X64Reg_R11)); return; } SizedArray args = { addr, BeMCOperand::FromImmediate(val), BeMCOperand::FromImmediate(size) }; auto mcFunc = BeMCOperand::FromSymbolAddr(mCOFFObject->GetSymbolRef("memset")->mIdx); CreateCall(mcFunc, args); } void BeMCContext::CreateMemCpy(const BeMCOperand& dest, const BeMCOperand& src, int size, int align) { auto destVRegInfo = GetVRegInfo(dest); auto srcVRegInfo = GetVRegInfo(src); if ((destVRegInfo != NULL) && (srcVRegInfo != NULL) && (destVRegInfo->mIsRetVal) && (srcVRegInfo->mIsRetVal)) return; if (size <= 256) { int destIdx = -1; if (!GetEncodedOperand(dest, destIdx)) { BeMCOperand tempDest = AllocVirtualReg(GetType(dest), 1); CreateDefineVReg(tempDest); AllocInst(BeMCInstKind_Mov, tempDest, dest); auto vregInfo = mVRegInfo[tempDest.mVRegIdx]; vregInfo->mForceReg = true; vregInfo->mDisableR11 = true; destIdx = tempDest.mVRegIdx; } int srcIdx = -1; if (!GetEncodedOperand(src, srcIdx)) { BeMCOperand tempSrc = AllocVirtualReg(GetType(src), 1); CreateDefineVReg(tempSrc); AllocInst(BeMCInstKind_Mov, tempSrc, src); auto vregInfo = mVRegInfo[tempSrc.mVRegIdx]; vregInfo->mForceReg = true; vregInfo->mDisableR11 = true; srcIdx = tempSrc.mVRegIdx; } BeMCOperand mcMemCpyInfo; mcMemCpyInfo.mKind = BeMCOperandKind_MemCpyInfo; mcMemCpyInfo.mMemCpyInfo.mSize = size; mcMemCpyInfo.mMemCpyInfo.mAlign = align; AllocInst(BeMCInstKind_PreserveVolatiles, BeMCOperand::FromReg(X64Reg_R11)); BeMCOperand mcVRegPair; mcVRegPair.mKind = BeMCOperandKind_VRegPair; mcVRegPair.mVRegPair.mVRegIdx0 = destIdx; mcVRegPair.mVRegPair.mVRegIdx1 = srcIdx; AllocInst(BeMCInstKind_MemCpy, mcMemCpyInfo, mcVRegPair); AllocInst(BeMCInstKind_RestoreVolatiles, BeMCOperand::FromReg(X64Reg_R11)); return; } SizedArray args = { dest, src, BeMCOperand::FromImmediate(size) }; auto mcFunc = BeMCOperand::FromSymbolAddr(mCOFFObject->GetSymbolRef("memcpy")->mIdx); CreateCall(mcFunc, args); } void BeMCContext::CreateTableSwitchSection(BeSwitchInst* switchInst, int startIdx, int endIdx) { auto& sect = mCOFFObject->mRDataSect; auto defaultBlock = GetOperand(switchInst->mDefaultBlock); auto int32Type = mModule->mContext->GetPrimitiveType(BeTypeCode_Int32); auto int32PtrType = mModule->mContext->GetPointerTo(int32Type); auto nativeType = mModule->mContext->GetPrimitiveType(BeTypeCode_Int64); auto nativePtrType = mModule->mContext->GetPointerTo(nativeType); int64 loVal = switchInst->mCases.front().mValue->mInt64; int64 hiVal = switchInst->mCases.back().mValue->mInt64; int numVals = switchInst->mCases.size(); BeMCSymbol* sym = mCOFFObject->mSymbols.Alloc(); sym->mType = int32Type; sym->mName = StrFormat("@jumpTab%d", mCOFFObject->mCurJumpTableIdx++); sym->mIsStatic = true; sym->mSymKind = BeMCSymbolKind_External; sym->mIdx = (int)mCOFFObject->mSymbols.size() - 1; mCOFFObject->MarkSectionUsed(sect); sym->mSectionNum = sect.mSectionIdx + 1; sect.mData.Align(4); sect.mAlign = BF_MAX(sect.mAlign, 4); sym->mValue = sect.mData.GetSize(); auto mcValue = GetOperand(switchInst->mValue); auto beType = GetType(mcValue); auto mcOfsValue = AllocVirtualReg(nativeType); CreateDefineVReg(mcOfsValue); if (beType->mSize < 8) AllocInst(BeMCInstKind_MovSX, mcOfsValue, mcValue); else AllocInst(BeMCInstKind_Mov, mcOfsValue, mcValue); AllocInst(BeMCInstKind_Sub, mcOfsValue, BeMCOperand::FromImmediate(loVal)); AllocInst(BeMCInstKind_CondBr, defaultBlock, BeMCOperand::FromCmpKind(BeCmpKind_SLT)); AllocInst(BeMCInstKind_Cmp, mcOfsValue, BeMCOperand::FromImmediate(hiVal - loVal)); AllocInst(BeMCInstKind_CondBr, defaultBlock, BeMCOperand::FromCmpKind(BeCmpKind_SGT)); auto jumpVReg = AllocVirtualReg(nativeType); CreateDefineVReg(jumpVReg); AllocInst(BeMCInstKind_Mov, jumpVReg, BeMCOperand::FromSymbolAddr(sym->mIdx)); auto jumpRelVReg = AllocVirtualReg(int32PtrType); CreateDefineVReg(jumpRelVReg); auto vregInfo = mVRegInfo[jumpRelVReg.mVRegIdx]; vregInfo->mIsExpr = true; vregInfo->mRelTo = jumpVReg; vregInfo->mRelOffset = mcOfsValue; vregInfo->mRelOffsetScale = 4; jumpRelVReg.mKind = BeMCOperandKind_VRegLoad; AllocInst(BeMCInstKind_Mov, jumpVReg, jumpRelVReg); auto imageBaseSym = mCOFFObject->GetSymbolRef("__ImageBase"); imageBaseSym->mType = nativeType; auto baseVReg = AllocVirtualReg(nativeType); CreateDefineVReg(baseVReg); AllocInst(BeMCInstKind_Mov, baseVReg, BeMCOperand::FromSymbolAddr(imageBaseSym->mIdx)); AllocInst(BeMCInstKind_Add, jumpVReg, baseVReg); AllocInst(BeMCInstKind_Br, jumpVReg); int64 lastVal = loVal - 1; defaultBlock.mBlock->AddPred(mActiveBlock); for (int caseIdx = 0; caseIdx < (int)switchInst->mCases.size(); caseIdx++) { auto& switchCase = switchInst->mCases[caseIdx]; int64 newVal = switchCase.mValue->mInt64; for (int64 val = lastVal + 1; val <= newVal; val++) { BeMCSwitchEntry switchEntry; switchEntry.mOfs = sect.mData.GetSize(); if (val == newVal) { auto switchBlock = GetOperand(switchCase.mBlock); switchBlock.mBlock->AddPred(mActiveBlock); switchEntry.mBlock = switchBlock.mBlock; } else { switchEntry.mBlock = defaultBlock.mBlock; } mSwitchEntries.push_back(switchEntry); // Placeholder sect.mData.Write((int32)0); } lastVal = newVal; } } void BeMCContext::CreateBinarySwitchSection(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); AllocInst(BeMCInstKind_Cmp, mcValue, GetOperand(switchCase.mValue)); AllocInst(BeMCInstKind_CondBr, switchBlock, BeMCOperand::FromCmpKind(BeCmpKind_EQ)); switchBlock.mBlock->AddPred(mActiveBlock); } } void BeMCContext::CreateCondBr(BeMCBlock* mcBlock, BeMCOperand& testVal, const BeMCOperand& trueBlock, const BeMCOperand& falseBlock) { if (testVal.IsImmediate()) { if (testVal.mImmediate != 0) AllocInst(BeMCInstKind_Br, trueBlock); else AllocInst(BeMCInstKind_Br, falseBlock); } else if (testVal.mKind == BeMCOperandKind_CmpResult) { // Beef-specific: assuming CMP results aren't stomped auto& cmpResult = mCmpResults[testVal.mCmpResultIdx]; AllocInst(BeMCInstKind_CondBr, trueBlock, BeMCOperand::FromCmpKind(cmpResult.mCmpKind)); AllocInst(BeMCInstKind_Br, falseBlock); } else if (testVal.mKind == BeMCOperandKind_Phi) { auto phi = testVal.mPhi; auto phiBlock = phi->mBlock; if ((phi->mBrTrue != NULL) && (phi->mBrFalse != NULL)) { // Redirect branches for (auto instIdx = 0; instIdx < phiBlock->mInstructions.size(); instIdx++) { auto inst = phiBlock->mInstructions[instIdx]; if (inst->mKind == BeMCInstKind_Br) { if (inst->mArg0 == phi->mBrTrue) inst->mArg0 = trueBlock; else if (inst->mArg0 == phi->mBrFalse) inst->mArg0 = falseBlock; } } phi->mBrTrue = trueBlock; phi->mBrFalse = falseBlock; return; } phi->mBrTrue = trueBlock; phi->mBrFalse = falseBlock; // Using a Phi for a CondBr in a different block is not supported if (phiBlock != mcBlock) { // Special case if our using block directly leads into us BEMC_ASSERT(mcBlock->mPreds.size() == 1); BEMC_ASSERT(mcBlock->mPreds[0] == phiBlock); } for (auto instIdx = 0; instIdx < phiBlock->mInstructions.size(); instIdx++) { auto inst = phiBlock->mInstructions[instIdx]; if (inst->mKind == BeMCInstKind_DefPhi) { BEMC_ASSERT(inst->mArg0.mPhi == phi); RemoveInst(phiBlock, instIdx); break; } } for (auto& phiVal : phi->mValues) { BeMCOperand landinglabel; if (phiVal.mValue.mKind != BeMCOperandKind_Phi) { landinglabel = BeMCOperand::FromLabel(mCurLabelIdx++); AllocInst(BeMCInstKind_Label, landinglabel); } int brInstIdx = -1; bool isFalseCmpResult = false; if (landinglabel) { bool found = false; auto _CheckBlock = [&](BeMCBlock* block) { for (int checkIdx = (int)block->mInstructions.size() - 1; checkIdx >= 0; checkIdx--) { auto checkInst = block->mInstructions[checkIdx]; if ((checkInst->mArg0.mKind == BeMCOperandKind_Block) && (checkInst->mArg0.mBlock == phi->mBlock)) { brInstIdx = checkIdx; checkInst->mArg0 = landinglabel; found = true; // Don't break, if we're are chained to another PHI then we need to modify all the labels isFalseCmpResult = false; if ((checkIdx >= 2) && (checkInst->mKind == BeMCInstKind_Br)) { auto prevInst = block->mInstructions[checkIdx - 1]; auto prevPrevInst = block->mInstructions[checkIdx - 2]; if ((prevPrevInst->mKind == BeMCInstKind_Cmp) && (prevPrevInst->mResult == phiVal.mValue) && (prevInst->mKind == BeMCInstKind_CondBr) && (prevInst->mArg1.mCmpKind == BeCmpKind_EQ)) { isFalseCmpResult = true; } } } } }; _CheckBlock(phiVal.mBlockFrom); BEMC_ASSERT(found); } if (isFalseCmpResult) { BEMC_ASSERT(phiVal.mValue.mKind == BeMCOperandKind_CmpResult); AllocInst(BeMCInstKind_Br, falseBlock); } else if ((phiVal.mValue.IsImmediate()) || (phiVal.mValue.mKind == BeMCOperandKind_CmpResult) || (phiVal.mValue.mKind == BeMCOperandKind_Phi) || (phiVal.mValue.mKind == BeMCOperandKind_NotResult)) { CreateCondBr(phiVal.mBlockFrom, phiVal.mValue, trueBlock, falseBlock); } else { // Do the 'test' in the preceding block. This is needed for liveness checking. Using a vreg in this // block would require the def to be hoisted SetAndRestoreValue prevActiveBlock(mActiveBlock, phiVal.mBlockFrom); BeMCOperand testImm; testImm.mKind = BeMCOperandKind_Immediate_i8; testImm.mImmediate = 1; auto mcInst = AllocInst(BeMCInstKind_Test, phiVal.mValue, testImm, brInstIdx); prevActiveBlock.Restore(); mcInst = AllocInst(BeMCInstKind_CondBr, trueBlock, BeMCOperand::FromCmpKind(BeCmpKind_NE)); mcInst = AllocInst(BeMCInstKind_Br, falseBlock); } } } else if (testVal.mKind == BeMCOperandKind_NotResult) { // Just get the original block and swap the true/false blocks auto invResult = GetOperand(testVal.mNotResult->mValue, true); if (invResult) { CreateCondBr(mcBlock, invResult, falseBlock, trueBlock); } } else { BeMCOperand testImm; testImm.mKind = BeMCOperandKind_Immediate_i8; testImm.mImmediate = 1; auto mcInst = AllocInst(BeMCInstKind_Test, testVal, testImm); mcInst = AllocInst(BeMCInstKind_CondBr, trueBlock, BeMCOperand::FromCmpKind(BeCmpKind_NE)); mcInst = AllocInst(BeMCInstKind_Br, falseBlock); } } void BeMCContext::CreatePhiAssign(BeMCBlock* mcBlock, const BeMCOperand& testVal, const BeMCOperand& result, const BeMCOperand& doneLabel) { if (testVal.mKind == BeMCOperandKind_Phi) { auto phi = testVal.mPhi; for (auto& phiVal : phi->mValues) { BeMCOperand landinglabel; if (phiVal.mValue.mKind == BeMCOperandKind_Phi) { // Remove PhiDef auto phi = phiVal.mValue.mPhi; auto phiBlock = phi->mBlock; for (auto instIdx = 0; instIdx < phiBlock->mInstructions.size(); instIdx++) { auto inst = phiBlock->mInstructions[instIdx]; if (inst->mKind == BeMCInstKind_DefPhi) { BEMC_ASSERT(inst->mArg0.mPhi == phi); RemoveInst(phiBlock, instIdx); break; } } CreatePhiAssign(phiVal.mBlockFrom, phiVal.mValue, result, doneLabel); } else { bool found = false; auto _CheckBlock = [&](BeMCBlock* block) { SetAndRestoreValue prevActiveBlock(mActiveBlock, block); for (int checkIdx = (int)block->mInstructions.size() - 1; checkIdx >= 0; checkIdx--) { auto checkInst = block->mInstructions[checkIdx]; if ((checkInst->mArg0.mKind == BeMCOperandKind_Block) && (checkInst->mArg0.mBlock == phi->mBlock)) { // Don't use an explicit dbgLoc SetAndRestoreValue prevDbgLoc(mCurDbgLoc, NULL); if (checkInst->mKind == BeMCInstKind_CondBr) { auto falseLabel = BeMCOperand::FromLabel(mCurLabelIdx++); auto prevDest = checkInst->mArg0; checkInst->mArg0 = falseLabel; checkInst->mArg1.mCmpKind = BeModule::InvertCmp(checkInst->mArg1.mCmpKind); int insertIdx = checkIdx + 1; SetAndRestoreValue prevInsertIdxRef(mInsertInstIdxRef, &insertIdx); CreateStore(BeMCInstKind_Mov, phiVal.mValue, OperandToAddr(result)); AllocInst(BeMCInstKind_Br, prevDest); AllocInst(BeMCInstKind_Label, falseLabel); } else { int insertIdx = checkIdx; SetAndRestoreValue prevInsertIdxRef(mInsertInstIdxRef, &insertIdx); CreateStore(BeMCInstKind_Mov, phiVal.mValue, OperandToAddr(result)); } found = true; } } }; _CheckBlock(phiVal.mBlockFrom); BEMC_ASSERT(found); } if (landinglabel) { AllocInst(BeMCInstKind_Br, doneLabel); } } } else { SoftFail("Unhandled CreatePhiAssign value"); } } BeMCOperand BeMCContext::GetImmediate(int64 val) { BeMCOperand operand; operand.mKind = BeMCOperandKind_Immediate_i64; operand.mImmediate = val; return operand; } BeMCOperand BeMCContext::OperandToAddr(const BeMCOperand& operand) { BeMCOperand loadedOperand = operand; if (loadedOperand.mKind == BeMCOperandKind_VRegLoad) loadedOperand.mKind = BeMCOperandKind_VReg; else if (loadedOperand.mKind == BeMCOperandKind_VReg) loadedOperand.mKind = BeMCOperandKind_VRegAddr; else if (loadedOperand.mKind == BeMCOperandKind_Symbol) loadedOperand.mKind = BeMCOperandKind_SymbolAddr; else Fail("Invalid operand in OperandToAddr"); return loadedOperand; } BeMCOperand BeMCContext::GetVReg(int regNum) { auto vregInfo = mVRegInfo[regNum]; if (vregInfo->mReg != X64Reg_None) return BeMCOperand::FromReg(vregInfo->mReg); BeMCOperand operand; operand.mKind = BeMCOperandKind_VReg; operand.mVRegIdx = regNum; return operand; } BeMCOperand BeMCContext::AllocVirtualReg(BeType* type, int refCount, bool mustBeReg) { BEMC_ASSERT(type->mTypeCode != BeTypeCode_Function); // We can only have pointers to these if (mustBeReg) { BEMC_ASSERT(!type->IsNonVectorComposite()); BEMC_ASSERT(type->mSize != 0); } int vregIdx = (int)mVRegInfo.size(); BeMCVRegInfo* vregInfo = mAlloc.Alloc(); vregInfo->mType = type; vregInfo->mAlign = type->mAlign; vregInfo->mRefCount = refCount; vregInfo->mForceReg = mustBeReg; mVRegInfo.push_back(vregInfo); BeMCOperand mcOperand; mcOperand.mKind = BeMCOperandKind_VReg; mcOperand.mVRegIdx = vregIdx++; if (mDebugging) { if (mcOperand.mVRegIdx == 8) { NOP; } } return mcOperand; } int BeMCContext::GetUnderlyingVReg(int vregIdx) { while (true) { auto vregInfo = mVRegInfo[vregIdx]; if (!vregInfo->mRelTo.IsVRegAny()) return vregIdx; if (vregInfo->mRelOffset) return vregIdx; vregIdx = vregInfo->mRelTo.mVRegIdx; } } bool BeMCContext::HasForceRegs(const BeMCOperand &operand) { if (!operand.IsVRegAny()) return false; auto vregInfo = mVRegInfo[operand.mVRegIdx]; if (vregInfo->mForceReg) return true; return HasForceRegs(vregInfo->mRelTo) || HasForceRegs(vregInfo->mRelOffset); } bool BeMCContext::OperandsEqual(const BeMCOperand& op0, const BeMCOperand& op1, bool exact) { bool isVReg0 = op0.mKind == BeMCOperandKind_VReg; bool isVReg1 = op1.mKind == BeMCOperandKind_VReg; if (!isVReg0 || !isVReg1) { return op0 == op1; } if (exact) { int vregIdx0 = op0.mVRegIdx; int vregIdx1 = op1.mVRegIdx; while (true) { auto vregInfo = mVRegInfo[vregIdx0]; if (!vregInfo->IsDirectRelTo()) break; if (vregInfo->mRelTo.mKind != BeMCOperandKind_VReg) return false; vregIdx0 = vregInfo->mRelTo.mVRegIdx; } while (true) { auto vregInfo = mVRegInfo[vregIdx1]; if (!vregInfo->IsDirectRelTo()) break; if (vregInfo->mRelTo.mKind != BeMCOperandKind_VReg) return false; vregIdx1 = vregInfo->mRelTo.mVRegIdx; } return vregIdx0 == vregIdx1; } int vregIdx0 = GetUnderlyingVReg(op0.mVRegIdx); int vregIdx1 = GetUnderlyingVReg(op1.mVRegIdx); return vregIdx0 == vregIdx1; } bool BeMCContext::ContainsNonOffsetRef(const BeMCOperand& checkOperand, const BeMCOperand& findOperand) { if (!checkOperand.IsVRegAny()) return false; if (checkOperand == findOperand) return true; auto vregInfo = GetVRegInfo(checkOperand); if (ContainsNonOffsetRef(vregInfo->mRelTo, findOperand)) return true; return false; } // For all values that we are certain we will immediately use, we directly do a Def preceding its first use. // For Allocas in the head, however, we may not use that memory for a long time so we imply the Def location // in DoDefPass. That allows us to limit how long that vreg will hold onto a register, reducing register // contention. BeMCInst* BeMCContext::CreateDefineVReg(const BeMCOperand& vreg, int insertIdx) { auto mcInst = AllocInst(insertIdx); mcInst->mKind = BeMCInstKind_Def; mcInst->mArg0 = vreg; return mcInst; } int BeMCContext::CreateLabel(int insertIdx, int labelIdx) { auto inst = AllocInst(insertIdx); inst->mKind = BeMCInstKind_Label; inst->mArg0.mKind = BeMCOperandKind_Label; if (labelIdx != -1) inst->mArg0.mLabelIdx = labelIdx; else inst->mArg0.mLabelIdx = mCurLabelIdx++; return inst->mArg0.mLabelIdx; } bool BeMCContext::FindTarget(const BeMCOperand& loc, BeMCBlock*& outBlock, int& outInstIdx) { if (loc.mKind == BeMCOperandKind_Block) { outBlock = loc.mBlock; outInstIdx = -1; return true; } if (loc.mKind == BeMCOperandKind_Label) { // Linear search :-( for (auto mcBlock : mBlocks) { for (int instIdx = 0; instIdx < (int)mcBlock->mInstructions.size(); instIdx++) { auto inst = mcBlock->mInstructions[instIdx]; if ((inst->mKind == BeMCInstKind_Label) && (inst->mArg0.mLabelIdx == loc.mLabelIdx)) { outBlock = mcBlock; outInstIdx = instIdx; return true; } } } } return false; } BeMCOperand BeMCContext::AllocRelativeVirtualReg(BeType* type, const BeMCOperand& relTo, const BeMCOperand& relOffset, int relScale) { if (!relTo.IsVRegAny()) { auto relToType = GetType(relTo); auto tempRelTo = AllocVirtualReg(relToType, 1); CreateDefineVReg(tempRelTo); AllocInst(BeMCInstKind_Mov, tempRelTo, relTo); tempRelTo.mKind = BeMCOperandKind_VReg; return AllocRelativeVirtualReg(type, tempRelTo, relOffset, relScale); } BEMC_ASSERT(relTo.IsVRegAny()); auto relToVRegInfo = GetVRegInfo(relTo); if ((relToVRegInfo->mRelTo) && (relToVRegInfo->mRelOffsetScale == 1) && (relToVRegInfo->mRelOffset.IsImmediate()) && (relOffset.IsImmediate()) && (relScale == 1)) { if (relTo.mKind == BeMCOperandKind_VReg) { int offset = (int)relOffset.mImmediate; if (relToVRegInfo->mRelOffset) offset += relToVRegInfo->mRelOffset.mImmediate; return AllocRelativeVirtualReg(type, relToVRegInfo->mRelTo, GetImmediate(offset), 1); } } if ((relTo.mKind == BeMCOperandKind_VRegAddr) && (!relToVRegInfo->mRelOffset) && (relOffset.IsZero()) && (relScale == 1)) { auto vregInfo = GetVRegInfo(relTo); if (vregInfo->IsDirectRelToAny()) { if (vregInfo->mRelTo.mKind == BeMCOperandKind_Symbol) { return AllocRelativeVirtualReg(type, BeMCOperand::FromSymbolAddr(vregInfo->mRelTo.mSymbolIdx), BeMCOperand(), 1); } } } auto mcVReg = AllocVirtualReg(type); auto vregInfo = GetVRegInfo(mcVReg); vregInfo->mIsExpr = true; vregInfo->mRelTo = relTo; if (!relOffset.IsZero()) vregInfo->mRelOffset = relOffset; vregInfo->mRelOffsetScale = relScale; mcVReg.mKind = BeMCOperandKind_VReg; return mcVReg; } BeMCVRegInfo* BeMCContext::GetVRegInfo(const BeMCOperand& operand) { if (!operand.IsVRegAny()) return NULL; return mVRegInfo[operand.mVRegIdx]; } bool BeMCContext::HasSymbolAddr(const BeMCOperand& operand) { if (operand.mKind == BeMCOperandKind_SymbolAddr) return true; auto vregInfo = GetVRegInfo(operand); if (vregInfo == NULL) return false; if ((vregInfo->mRelTo) && (HasSymbolAddr(vregInfo->mRelTo))) return true; if ((vregInfo->mRelOffset) && (HasSymbolAddr(vregInfo->mRelOffset))) return true; return false; } BeMCOperand BeMCContext::ReplaceWithNewVReg(BeMCOperand& operand, int& instIdx, bool isInput, bool mustBeReg, bool preserveDeref) { if ((isInput) && (operand.mKind == BeMCOperandKind_VRegLoad) && (preserveDeref)) { BeMCOperand addrOperand = OperandToAddr(operand); BeMCOperand scratchReg = AllocVirtualReg(GetType(addrOperand), 2, mustBeReg); CreateDefineVReg(scratchReg, instIdx++); AllocInst(BeMCInstKind_Mov, scratchReg, addrOperand, instIdx++); operand = BeMCOperand::ToLoad(scratchReg); return scratchReg; } BeMCOperand scratchReg = AllocVirtualReg(GetType(operand), 2, mustBeReg); CreateDefineVReg(scratchReg, instIdx++); if (isInput) AllocInst(BeMCInstKind_Mov, scratchReg, operand, instIdx++); else AllocInst(BeMCInstKind_Mov, operand, scratchReg, instIdx++ + 1); operand = scratchReg; return scratchReg; } BeMCOperand BeMCContext::RemapOperand(BeMCOperand& operand, BeMCRemapper& regRemaps) { if (!operand.IsVRegAny()) return operand; int regIdx = regRemaps.GetHead(operand.mVRegIdx); if (regIdx < 0) { BeMCOperand newOperand; newOperand.mKind = operand.mKind; if (newOperand.mKind == BeMCOperandKind_VRegLoad) newOperand.mKind = BeMCOperandKind_VReg; else NotImpl(); newOperand.mVRegIdx = -regIdx; return newOperand; } else { BeMCOperand newOperand; newOperand.mKind = operand.mKind; newOperand.mVRegIdx = regIdx; return newOperand; } } bool BeMCContext::IsLive(BeVTrackingList* liveRegs, int origVRegIdx, BeMCRemapper& regRemaps) { // Check the whole remap chain to determine liveness int vregIdx = regRemaps.GetHead(origVRegIdx); while (vregIdx != -1) { if (mLivenessContext.IsSet(liveRegs, vregIdx)) return true; vregIdx = regRemaps.GetNext(vregIdx); } return false; } void BeMCContext::AddRegRemap(int from, int to, BeMCRemapper& regRemaps, bool allowRemapToDbgVar) { auto vregInfoFrom = mVRegInfo[from]; auto vregInfoTo = mVRegInfo[to]; BEMC_ASSERT(vregInfoFrom->mDbgVariable == NULL); if (vregInfoTo->mDbgVariable != NULL) { // We can't do a direct remap due to lifetime issues, so do an indirect one vregInfoFrom->mIsExpr = true; if (!vregInfoFrom->mIsRetVal) vregInfoFrom->mForceReg = false; vregInfoFrom->mRelTo = BeMCOperand::FromVReg(to); vregInfoFrom->mRelOffset = BeMCOperand(); vregInfoFrom->mRelOffsetScale = 1; return; } regRemaps.Add(from, to); } bool BeMCContext::GetEncodedOperand(const BeMCOperand& operand, int& encodedVal) { if (operand.mKind == BeMCOperandKind_VReg) { auto vregInfo = mVRegInfo[operand.mVRegIdx]; encodedVal = operand.mVRegIdx; return true; } if (operand.mKind != BeMCOperandKind_VRegAddr) return false; auto vregInfo = mVRegInfo[operand.mVRegIdx]; if (vregInfo->IsDirectRelTo()) { if (!GetEncodedOperand(vregInfo->mRelTo, encodedVal)) return false; encodedVal |= BeMCOperand::EncodeFlag_Addr; return true; } if (vregInfo->mIsExpr) return false; encodedVal = operand.mVRegIdx | BeMCOperand::EncodeFlag_Addr; return true; } bool BeMCContext::HasPointerDeref(const BeMCOperand& operand) { if (operand.mKind == BeMCOperandKind_VRegLoad) return true; auto vregInfo = GetVRegInfo(operand); if (vregInfo == NULL) return false; if (!vregInfo->mRelTo) return false; if ((vregInfo->mRelTo.mKind == BeMCOperandKind_VRegLoad) || (vregInfo->mRelTo.mKind == BeMCOperandKind_VReg)) return true; return false; } bool BeMCContext::AreOperandsEquivalent(const BeMCOperand& origOp0, const BeMCOperand& origOp1, BeMCRemapper& regRemaps) { auto op0 = origOp0; RemapOperand(op0, regRemaps); auto op1 = origOp1; RemapOperand(op1, regRemaps); if (op0.mKind != op1.mKind) return false; if (op0.mKind == BeMCOperandKind_None) return true; if (op0.IsSymbol()) return op0.mSymbolIdx == op1.mSymbolIdx; if (op0.IsImmediate()) return op0.mImmediate == op1.mImmediate; if (op0.IsNativeReg()) return op0.mReg == op1.mReg; if (op0.IsVRegAny()) { if (op0.mVRegIdx == op1.mVRegIdx) return true; auto vregInfo0 = mVRegInfo[op0.mVRegIdx]; auto vregInfo1 = mVRegInfo[op1.mVRegIdx]; if (vregInfo0->mType != vregInfo1->mType) return false; if ((!vregInfo0->mIsExpr) || (!vregInfo1->mIsExpr)) return false; return (vregInfo0->mRelOffsetScale == vregInfo1->mRelOffsetScale) && (AreOperandsEquivalent(vregInfo0->mRelTo, vregInfo1->mRelTo, regRemaps)) && (AreOperandsEquivalent(vregInfo0->mRelOffset, vregInfo1->mRelOffset, regRemaps)); } return false; } bool BeMCContext::CouldBeReg(const BeMCOperand& operand) { if (operand.mKind != BeMCOperandKind_VReg) return false; auto vregInfo = GetVRegInfo(operand); if (vregInfo->mForceMem) return false; if (vregInfo->mIsExpr) { if (vregInfo->mRelOffset) return false; } return true; } // bool BeMCContext::CouldBeReg(const BeMCOperand& operand) // { // if (operand.mKind != BeMCOperandKind_VReg) // return false; // // auto vregInfo = GetVRegInfo(operand); // if ((vregInfo->mIsRetVal) && (mCompositeRetVRegIdx != -1) && (mCompositeRetVRegIdx != operand.mVRegIdx)) // { // return CouldBeReg(BeMCOperand::FromVReg(mCompositeRetVRegIdx)); // } // // if (vregInfo->mReg != X64Reg_None) // return true; // // if (vregInfo->mForceMem) // return false; // // if (vregInfo->mIsExpr) // { // if (vregInfo->mRelOffset) // return false; // return CouldBeReg(vregInfo->mRelTo); // } // // return !vregInfo->mType->IsNonVectorComposite(); // } bool BeMCContext::CheckForce(BeMCVRegInfo* vregInfo) { if (!vregInfo->mIsRetVal) { if (vregInfo->mForceMem && vregInfo->mForceReg) SoftFail("vreg forceMem/forceReg collision"); } return false; } void BeMCContext::MarkLive(BeVTrackingList* liveRegs, SizedArrayImpl& newRegs, BeVTrackingList*& vregsInitialized, const BeMCOperand& operand) { int vregIdx = operand.mVRegIdx; auto vregInfo = mVRegInfo[vregIdx]; if (mLivenessContext.IsSet(liveRegs, operand.mVRegIdx)) return; for (auto newReg : newRegs) if (newReg == operand.mVRegIdx) return; if (!mColorizer.mNodes.empty()) { // Is new for (int i = 0; i < liveRegs->mSize; i++) { int checkReg = liveRegs->mEntries[i]; if (checkReg >= mLivenessContext.mNumItems) continue; mColorizer.AddEdge(checkReg, operand.mVRegIdx); } for (auto checkReg : newRegs) mColorizer.AddEdge(checkReg, operand.mVRegIdx); } if (vregInfo->mHasDynLife) { if (!mVRegInitializedContext.IsSet(vregsInitialized, vregIdx)) { // This indicates that this is a 'def' usage, meaning the value wasn't actually set yet // so don't propagate this index upward return; } } if ((vregInfo->mRelTo) && (vregInfo->mRelTo.IsVRegAny())) MarkLive(liveRegs, newRegs, vregsInitialized, vregInfo->mRelTo); if ((vregInfo->mRelOffset) && (vregInfo->mRelOffset.IsVRegAny())) MarkLive(liveRegs, newRegs, vregsInitialized, vregInfo->mRelOffset); newRegs.push_back(operand.mVRegIdx); } BeVTrackingList* BeMCContext::MergeLiveRegs(BeVTrackingList* prevDestEntry, BeVTrackingList* mergeFrom) { int vregIdx = -1; SizedArray newNodes; SizedArray prevExclusiveNodes; // Take nodes that were exclusive to the new set and add edges to nodes that were exclusive the old set /*while (true) { vregIdx = mLivenessContext.GetNextDiffSetIdx(prevDestEntry.mBits, mergeFrom, vregIdx); if (vregIdx == -1) break; newNodes.push_back(vregIdx); if (!mColorizer.mNodes.empty()) { int checkReg = -1; while (true) { checkReg = mLivenessContext.GetNextDiffSetIdx(mergeFrom, prevDestEntry.mBits, checkReg); if (checkReg == -1) break; mColorizer.AddEdge(checkReg, vregIdx); } } }*/ auto prevItr = prevDestEntry->begin(); auto prevEnd = prevDestEntry->end(); auto mergeFromItr = mergeFrom->begin(); auto mergeFromEnd = mergeFrom->end(); while ((prevItr != prevEnd) && (mergeFromItr != mergeFromEnd)) { int prevIdx = *prevItr; int mergeIdx = *mergeFromItr; bool done = false; while (mergeIdx < prevIdx) { newNodes.push_back(mergeIdx); ++mergeFromItr; if (mergeFromItr == mergeFromEnd) { done = true; break; } mergeIdx = *mergeFromItr; } if (done) break; while (prevIdx < mergeIdx) { prevExclusiveNodes.push_back(prevIdx); ++prevItr; if (prevItr == prevEnd) { done = true; break; } prevIdx = *prevItr; } if (done) break; if (prevIdx == mergeIdx) { ++prevItr; ++mergeFromItr; } } while (prevItr != prevEnd) { prevExclusiveNodes.push_back(*prevItr); ++prevItr; } while (mergeFromItr != mergeFromEnd) { newNodes.push_back(*mergeFromItr); ++mergeFromItr; } if (!mColorizer.mNodes.empty()) { for (int newIdx : newNodes) { for (int prevExclusiveIdx : prevExclusiveNodes) { mColorizer.AddEdge(newIdx, prevExclusiveIdx); } } } return mLivenessContext.Add(prevDestEntry, newNodes, false); } static void DedupPushBack(SizedArrayImpl& vec, int val) { if (vec.Contains(val)) return; vec.push_back(val); } static int genLivenessIdx = 0; void BeMCContext::GenerateLiveness(BeMCBlock* block, BeVTrackingGenContext* genCtx, bool& modifiedBlockBefore, bool& modifiedBlockAfter) { genCtx->mBlocks[block->mBlockIdx].mGenerateCount++; modifiedBlockBefore = false; modifiedBlockAfter = false; genCtx->mCalls++; bool debugging = false; //if (mDebugging) debugging = true; //if (mBeFunction->mName == "?Draw@DarkEditWidgetContent@dark@theme@Beefy@@UEAAXPEAVGraphics@gfx@4@@Z") //debugging = true; //"?DrawEntry@DrawContext@PerfView@BeefPerf@@QEAAXPEAVGraphics@gfx@Beefy@@PEAVTrackNodeEntry@23@MM@Z") //debugging &= mcColorizer != NULL; if (debugging) { if (block->mBlockIdx == 220) { //BF_ASSERT(mLivenessContext.IsSet(succLiveRegs, 36)); } } if (debugging) { OutputDebugStrF("GenerateLiveness %s(%d)\n", ToString(BeMCOperand::FromBlock(block)).c_str(), block->mBlockIdx); } genCtx->mHandledCalls++; BeMDNode* curDbgScope = NULL; // Don't use 'ref' here, it's important that mSuccLiveRegs actually means 'successor', otherwise // we could miss a later-occurring liveness overlap on a different successor BeVTrackingList* liveRegs = block->mSuccLiveness; BeVTrackingList* vregsInitialized = block->mSuccVRegsInitialized; // When we process our first vregsInitialized (at the bottom of the block), there won't be Change entries // for us to use to determine the delta from mSuccVRegsInitialized to inst->mVRegsInitialized bool needsManualVRegInitDiff = true; for (int instIdx = (int)block->mInstructions.size() - 1; instIdx >= 0; instIdx--) { genLivenessIdx++; auto inst = block->mInstructions[instIdx]; auto prevLiveness = inst->mLiveness; genCtx->mInstructions++; SizedArray removeVec; SizedArray addVec; SizedArray filteredRemoveVec; SizedArray filteredAddVec; inst->mLiveness = liveRegs; if ((inst->mVRegsInitialized != NULL) && (inst->mVRegsInitialized != vregsInitialized)) { auto _VRegUninit = [&](int vregIdxEx) { int vregIdx = vregIdxEx % mVRegInitializedContext.mNumItems; auto vregInfo = mVRegInfo[vregIdx]; if (!vregInfo->mHasDynLife) return; bool doClear = false; if (vregInfo->mDoConservativeLife) { // Only do liveness clear when both 'init' and 'non-init' is set int otherVRegIdxEx = vregIdx; if (vregIdxEx < mVRegInitializedContext.mNumItems) otherVRegIdxEx += mVRegInitializedContext.mNumItems; if (!mVRegInitializedContext.IsSet(inst->mVRegsInitialized, otherVRegIdxEx)) doClear = true; } else { // Only do liveness clear on 'init' clearing out if (vregIdx < mVRegInitializedContext.mNumItems) doClear = true; } if (doClear) { DedupPushBack(removeVec, vregIdx); } }; if (needsManualVRegInitDiff) { BEMC_ASSERT(vregsInitialized == block->mSuccVRegsInitialized); // Manually compare auto vregsInit0 = vregsInitialized; auto vregsInit1 = inst->mVRegsInitialized; if (vregsInit0 != vregsInit1) { int idx0 = 0; int idx1 = 0; while ((idx0 != vregsInit0->mSize) && (idx1 != vregsInit1->mSize)) { int val0 = vregsInit0->mEntries[idx0]; int val1 = vregsInit1->mEntries[idx1]; if (val0 == val1) { idx0++; idx1++; continue; } bool done = false; while (val0 < val1) { _VRegUninit(val0); idx0++; if (idx0 >= vregsInit0->mSize) { done = true; break; } val0 = vregsInit0->mEntries[idx0]; } if (done) break; while (val1 < val0) { idx1++; if (idx1 >= vregsInit1->mSize) { done = true; break; } val1 = vregsInit1->mEntries[idx1]; } if (done) break; } while (idx0 < vregsInit0->mSize) { _VRegUninit(vregsInit0->mEntries[idx0++]); } } } else { for (int changeIdx = 0; changeIdx < vregsInitialized->mNumChanges; changeIdx++) { int vregIdxEx = vregsInitialized->GetChange(changeIdx); if (vregIdxEx < 0) continue; // It's possible this vregsInitialized is equivalent to the one in 'inst', but also merged with another 'inst' // during legalization. We need to avoid uninitializing vregs if that's the case. // the entry above this if (inst->mVRegsInitialized->ContainsChange(vregIdxEx)) continue; _VRegUninit(vregIdxEx); } } vregsInitialized = inst->mVRegsInitialized; } if (inst->mVRegsInitialized != NULL) needsManualVRegInitDiff = false; if (inst->mKind == BeMCInstKind_DbgDecl) { if (!mVRegInitializedContext.IsSet(inst->mVRegsInitialized, inst->mArg0.mVRegIdx)) { // There are some rare cases with conditional branches where one branch will have a vreg marked as // initialized, which causes the variable to be marked as live, which propagates upward into the block // containing a variable declaration, before the actual def point DedupPushBack(removeVec, inst->mArg0.mVRegIdx); } liveRegs = mLivenessContext.Modify(liveRegs, addVec, removeVec, filteredAddVec, filteredRemoveVec); continue; } // This is used for clearing out things like usage of inline return values, which will be accessed after their // lifetime end (the lifetime ends inside the inlined method but the value is used afterward, in the inlining // function. This will emit as a load of a dbgVar, so we need to drill down into the relTo values if (inst->mKind == BeMCInstKind_LifetimeStart) { BEMC_ASSERT(inst->mArg0.IsVRegAny()); int vregIdx = inst->mArg0.mVRegIdx; while (true) { DedupPushBack(removeVec, vregIdx); auto vregInfo = mVRegInfo[vregIdx]; if (!vregInfo->IsDirectRelTo()) break; BEMC_ASSERT(vregInfo->mRelTo.IsVReg()); vregIdx = vregInfo->mRelTo.mVRegIdx; } liveRegs = mLivenessContext.Modify(liveRegs, addVec, removeVec, filteredAddVec, filteredRemoveVec); continue; } if (inst->mKind == BeMCInstKind_LifetimeEnd) { auto vregInfo = GetVRegInfo(inst->mArg0); if (vregInfo->mDbgVariable == NULL) { // Only extend lifetime down through LifetimeEnd when we have a debug variable we want to keep alive, // otherwise constrict lifetime to actual usage liveRegs = mLivenessContext.Modify(liveRegs, addVec, removeVec, filteredAddVec, filteredRemoveVec); continue; } } if (inst->IsDef()) { DedupPushBack(removeVec, inst->mArg0.mVRegIdx); liveRegs = mLivenessContext.Modify(liveRegs, addVec, removeVec, filteredAddVec, filteredRemoveVec); continue; } auto operands = { &inst->mResult, &inst->mArg0, &inst->mArg1 }; for (auto operand : operands) { if (operand->IsSymbol()) { auto sym = mCOFFObject->mSymbols[operand->mSymbolIdx]; if (sym->mIsTLS) { MarkLive(liveRegs, addVec, vregsInitialized, BeMCOperand::FromVReg(mTLSVRegIdx)); } } if (operand->mKind == BeMCOperandKind_VRegPair) { auto mcOperand = BeMCOperand::FromEncoded(operand->mVRegPair.mVRegIdx0); if (mcOperand.IsVRegAny()) MarkLive(liveRegs, addVec, vregsInitialized, mcOperand); mcOperand = BeMCOperand::FromEncoded(operand->mVRegPair.mVRegIdx1); if (mcOperand.IsVRegAny()) MarkLive(liveRegs, addVec, vregsInitialized, mcOperand); } if (operand->IsVRegAny()) { MarkLive(liveRegs, addVec, vregsInitialized, *operand); } } liveRegs = mLivenessContext.Modify(liveRegs, addVec, removeVec, filteredAddVec, filteredRemoveVec); /*if ((!isFirstEntry) && (liveRegs == prevLiveness)) { // We've already been here before and nothing changed return; }*/ inst->mLiveness = liveRegs; } if (block == mBlocks[0]) { BEMC_ASSERT(block->mBlockIdx == 0); if (!mLivenessContext.IsEmpty(liveRegs)) { for (int vregIdx = 0; vregIdx < mLivenessContext.mNumEntries; vregIdx++) { if (mLivenessContext.IsSet(liveRegs, vregIdx)) { auto vregInfo = mVRegInfo[vregIdx]; // If we are still alive then the only valid reason is because we have mHasDynLife and our 'init' flag is still set if (vregInfo->mHasDynLife) { if (!mVRegInitializedContext.IsSet(vregsInitialized, vregIdx)) { if (vregInfo->mDoConservativeLife) BEMC_ASSERT(mVRegInitializedContext.IsSet(vregsInitialized, vregIdx, BeTrackKind_Uninitialized)); else SoftFail("VReg lifetime error"); } } else { SoftFail("VReg lifetime error"); } } } } } for (auto pred : block->mPreds) { auto& entry = genCtx->mBlocks[pred->mBlockIdx]; BEMC_ASSERT(pred == mBlocks[pred->mBlockIdx]); auto newSuccLiveness = MergeLiveRegs(pred->mSuccLiveness, liveRegs); if (newSuccLiveness == pred->mSuccLiveness) continue; pred->mSuccLiveness = newSuccLiveness; pred->mSuccVRegsInitialized = mVRegInitializedContext.Merge(pred->mSuccVRegsInitialized, vregsInitialized); if (pred->mBlockIdx > block->mBlockIdx) modifiedBlockAfter = true; else modifiedBlockBefore = true; entry.mGenerateQueued = true; } } void BeMCContext::GenerateLiveness() { //GenerateLiveness_OLD(mcColorizer); //return; mLivenessContext.mStats = BeVTrackingContext::Stats(); #ifdef _DEBUG // So we can Print() while generating liveness (the old values would have been unallocated) for (auto mcBlock : mBlocks) { for (auto mcInst : mcBlock->mInstructions) { mcInst->mLiveness = NULL; } } #endif BP_ZONE("BeMCContext::GenerateLiveness"); mLivenessContext.Clear(); mLivenessContext.Init((int)mVRegInfo.size()); auto emptyList = mLivenessContext.AllocEmptyList(); BeVTrackingGenContext genCtx; genCtx.mEmptyList = emptyList; genCtx.mBlocks.Resize(mBlocks.size()); for (auto mcBlock : mBlocks) { mcBlock->mSuccLiveness = emptyList; mcBlock->mSuccVRegsInitialized = emptyList; if (mTLSVRegIdx != -1) { // Keep TLS alive SizedArray vec = { mTLSVRegIdx }; mcBlock->mSuccLiveness = mLivenessContext.Add(mcBlock->mSuccLiveness, vec, false); } } while (true) { bool didWork = false; // Handle any outstanding pred entries for (int blockIdx = (int)mBlocks.size() - 1; blockIdx >= 0; blockIdx--) { auto& entry = genCtx.mBlocks[blockIdx]; if (entry.mGenerateQueued) { entry.mGenerateQueued = false; didWork = true; auto block = mBlocks[blockIdx]; bool modifiedBlockBefore; bool modifiedBlockAfter; GenerateLiveness(block, &genCtx, modifiedBlockBefore, modifiedBlockAfter); if (modifiedBlockAfter) break; } } // If no pred entries, find blocks that haven't been processed yet if (!didWork) { for (int blockIdx = (int)mBlocks.size() - 1; blockIdx >= 0; blockIdx--) { auto& entry = genCtx.mBlocks[blockIdx]; if (entry.mGenerateCount == 0) { didWork = true; auto block = mBlocks[blockIdx]; bool modifiedBlockBefore; bool modifiedBlockAfter; GenerateLiveness(block, &genCtx, modifiedBlockBefore, modifiedBlockAfter); if (modifiedBlockBefore || modifiedBlockAfter) break; } } } if (!didWork) break; } int instCount = 0; for (auto block : mBlocks) instCount += (int)block->mInstructions.size(); BpEvent("GenerateLiveness Results", StrFormat("Blocks: %d\nInstructions: %d\nVRegs: %d\nCalls: %d\nHandled Calls: %d\nProcessed Instructions: %d\nLiveness Bytes: %d\n" "Temp Bytes: %d\nBits Bytes: %d\nList Bytes: %d\nSucc Bytes: %d", mBlocks.size(), instCount, mVRegInfo.size(), genCtx.mCalls, genCtx.mHandledCalls, genCtx.mInstructions, mLivenessContext.mAlloc.GetAllocSize(), genCtx.mAlloc.GetAllocSize(), mLivenessContext.mStats.mBitsBytes, mLivenessContext.mStats.mListBytes, mLivenessContext.mStats.mSuccBytes).c_str()); } void BeMCContext::IntroduceVRegs(const BeMCOperand& newVReg, BeMCBlock* block, int startInstIdx, int lastInstIdx) { return; /*BF_ASSERT((block->mInstructions[startInstIdx]->mKind == BeMCInstKind_Def) && (block->mInstructions[startInstIdx]->mArg0 == newVReg)); BF_ASSERT(mColorizer.mNodes.size() == newVReg.mVRegIdx); mColorizer.mNodes.resize(mColorizer.mNodes.size() + 1); BeVTrackingBits* lastLiveness = NULL; for (int instIdx = startInstIdx + 1; instIdx <= lastInstIdx; instIdx++) { auto inst = block->mInstructions[instIdx]; if (lastLiveness == NULL) { for (int vregIdx : *inst->mLiveness) { if (vregIdx >= mLivenessContext.mNumItems) continue; mColorizer.AddEdge(newVReg.mVRegIdx, vregIdx); } } else { int vregIdx = -1; while (true) { vregIdx = mLivenessContext.GetNextDiffSetIdx(lastLiveness, inst->mLiveness, vregIdx); if (vregIdx == -1) break; mColorizer.AddEdge(newVReg.mVRegIdx, vregIdx); } } if (inst->mVRegsInitializedEx != NULL) { auto vregArray = (BeMCVRegArray*)mAlloc.AllocBytes(sizeof(int) * (inst->mVRegsInitializedEx->mSize + 1 + 1), sizeof(int)); vregArray->mSize = inst->mVRegsInitializedEx->mSize + 1; for (int listIdx = 0; listIdx < inst->mVRegsInitializedEx->mSize; listIdx++) { mColorizer.AddEdge(newVReg.mVRegIdx, inst->mVRegsInitializedEx->mVRegIndices[listIdx]); vregArray->mVRegIndices[listIdx] = inst->mVRegsInitializedEx->mVRegIndices[listIdx]; } vregArray->mVRegIndices[vregArray->mSize - 1] = newVReg.mVRegIdx; inst->mVRegsInitializedEx = vregArray; } else { auto vregArray = (BeMCVRegArray*)mAlloc.AllocBytes(sizeof(int) * 2, sizeof(int)); vregArray->mSize = 1; vregArray->mVRegIndices[0] = newVReg.mVRegIdx; inst->mVRegsInitializedEx = vregArray; } lastLiveness = inst->mLiveness; }*/ } bool BeMCContext::IsVolatileReg(X64CPURegister reg) { switch (ResizeRegister(reg, 8)) { case X64Reg_RAX: case X64Reg_RCX: case X64Reg_RDX: case X64Reg_R8: case X64Reg_R9: case X64Reg_R10: case X64Reg_R11: case X64Reg_XMM0_f64: case X64Reg_XMM1_f64: case X64Reg_XMM2_f64: case X64Reg_XMM3_f64: case X64Reg_XMM4_f64: case X64Reg_XMM5_f64: return true; default: return false; } } bool BeMCContext::IsXMMReg(X64CPURegister reg) { return (reg >= X64Reg_XMM0_f64) && (reg <= X64Reg_M128_XMM15); } X64CPURegister BeMCContext::ResizeRegister(X64CPURegister reg, int numBytes) { if (numBytes == 16) { switch (reg) { case X64Reg_XMM0_f32: case X64Reg_XMM0_f64: case X64Reg_M128_XMM0: return X64Reg_M128_XMM0; case X64Reg_XMM1_f32: case X64Reg_XMM1_f64: case X64Reg_M128_XMM1: return X64Reg_M128_XMM1; case X64Reg_XMM2_f32: case X64Reg_XMM2_f64: case X64Reg_M128_XMM2: return X64Reg_M128_XMM2; case X64Reg_XMM3_f32: case X64Reg_XMM3_f64: case X64Reg_M128_XMM3: return X64Reg_M128_XMM3; case X64Reg_XMM4_f32: case X64Reg_XMM4_f64: case X64Reg_M128_XMM4: return X64Reg_M128_XMM4; case X64Reg_XMM5_f32: case X64Reg_XMM5_f64: case X64Reg_M128_XMM5: return X64Reg_M128_XMM5; case X64Reg_XMM6_f32: case X64Reg_XMM6_f64: case X64Reg_M128_XMM6: return X64Reg_M128_XMM6; case X64Reg_XMM7_f32: case X64Reg_XMM7_f64: case X64Reg_M128_XMM7: return X64Reg_M128_XMM7; case X64Reg_XMM8_f32: case X64Reg_XMM8_f64: case X64Reg_M128_XMM8: return X64Reg_M128_XMM8; case X64Reg_XMM9_f32: case X64Reg_XMM9_f64: case X64Reg_M128_XMM9: return X64Reg_M128_XMM9; case X64Reg_XMM10_f32: case X64Reg_XMM10_f64: case X64Reg_M128_XMM10: return X64Reg_M128_XMM10; case X64Reg_XMM11_f32: case X64Reg_XMM11_f64: case X64Reg_M128_XMM11: return X64Reg_M128_XMM11; case X64Reg_XMM12_f32: case X64Reg_XMM12_f64: case X64Reg_M128_XMM12: return X64Reg_M128_XMM12; case X64Reg_XMM13_f32: case X64Reg_XMM13_f64: case X64Reg_M128_XMM13: return X64Reg_M128_XMM13; case X64Reg_XMM14_f32: case X64Reg_XMM14_f64: case X64Reg_M128_XMM14: return X64Reg_M128_XMM14; case X64Reg_XMM15_f32: case X64Reg_XMM15_f64: case X64Reg_M128_XMM15: return X64Reg_M128_XMM15; } } if (numBytes == 8) { switch (reg) { case X64Reg_DIL: case X64Reg_DI: case X64Reg_EDI: case X64Reg_RDI: return X64Reg_RDI; case X64Reg_SIL: case X64Reg_SI: case X64Reg_ESI: case X64Reg_RSI: return X64Reg_RSI; case X64Reg_AL: case X64Reg_AH: case X64Reg_AX: case X64Reg_EAX: case X64Reg_RAX: return X64Reg_RAX; case X64Reg_DL: case X64Reg_DH: case X64Reg_DX: case X64Reg_EDX: case X64Reg_RDX: return X64Reg_RDX; case X64Reg_CL: case X64Reg_CH: case X64Reg_CX: case X64Reg_ECX: case X64Reg_RCX: return X64Reg_RCX; case X64Reg_BL: case X64Reg_BH: case X64Reg_BX: case X64Reg_EBX: case X64Reg_RBX: return X64Reg_RBX; case X64Reg_R8B: case X64Reg_R8W: case X64Reg_R8D: case X64Reg_R8: return X64Reg_R8; case X64Reg_R9B: case X64Reg_R9W: case X64Reg_R9D: case X64Reg_R9: return X64Reg_R9; case X64Reg_R10B: case X64Reg_R10W: case X64Reg_R10D: case X64Reg_R10: return X64Reg_R10; case X64Reg_R11B: case X64Reg_R11W: case X64Reg_R11D: case X64Reg_R11: return X64Reg_R11; case X64Reg_R12B: case X64Reg_R12W: case X64Reg_R12D: case X64Reg_R12: return X64Reg_R12; case X64Reg_R13B: case X64Reg_R13W: case X64Reg_R13D: case X64Reg_R13: return X64Reg_R13; case X64Reg_R14B: case X64Reg_R14W: case X64Reg_R14D: case X64Reg_R14: return X64Reg_R14; case X64Reg_R15B: case X64Reg_R15W: case X64Reg_R15D: case X64Reg_R15: return X64Reg_R15; case X64Reg_XMM0_f32: case X64Reg_XMM0_f64: case X64Reg_M128_XMM0: return X64Reg_XMM0_f64; case X64Reg_XMM1_f32: case X64Reg_XMM1_f64: case X64Reg_M128_XMM1: return X64Reg_XMM1_f64; case X64Reg_XMM2_f32: case X64Reg_XMM2_f64: case X64Reg_M128_XMM2: return X64Reg_XMM2_f64; case X64Reg_XMM3_f32: case X64Reg_XMM3_f64: case X64Reg_M128_XMM3: return X64Reg_XMM3_f64; case X64Reg_XMM4_f32: case X64Reg_XMM4_f64: case X64Reg_M128_XMM4: return X64Reg_XMM4_f64; case X64Reg_XMM5_f32: case X64Reg_XMM5_f64: case X64Reg_M128_XMM5: return X64Reg_XMM5_f64; case X64Reg_XMM6_f32: case X64Reg_XMM6_f64: case X64Reg_M128_XMM6: return X64Reg_XMM6_f64; case X64Reg_XMM7_f32: case X64Reg_XMM7_f64: case X64Reg_M128_XMM7: return X64Reg_XMM7_f64; case X64Reg_XMM8_f32: case X64Reg_XMM8_f64: case X64Reg_M128_XMM8: return X64Reg_XMM8_f64; case X64Reg_XMM9_f32: case X64Reg_XMM9_f64: case X64Reg_M128_XMM9: return X64Reg_XMM9_f64; case X64Reg_XMM10_f32: case X64Reg_XMM10_f64: case X64Reg_M128_XMM10: return X64Reg_XMM10_f64; case X64Reg_XMM11_f32: case X64Reg_XMM11_f64: case X64Reg_M128_XMM11: return X64Reg_XMM11_f64; case X64Reg_XMM12_f32: case X64Reg_XMM12_f64: case X64Reg_M128_XMM12: return X64Reg_XMM12_f64; case X64Reg_XMM13_f32: case X64Reg_XMM13_f64: case X64Reg_M128_XMM13: return X64Reg_XMM13_f64; case X64Reg_XMM14_f32: case X64Reg_XMM14_f64: case X64Reg_M128_XMM14: return X64Reg_XMM14_f64; case X64Reg_XMM15_f32: case X64Reg_XMM15_f64: case X64Reg_M128_XMM15: return X64Reg_XMM15_f64; } return reg; } if (numBytes == 4) { switch (reg) { case X64Reg_DIL: case X64Reg_DI: case X64Reg_EDI: case X64Reg_RDI: return X64Reg_EDI; case X64Reg_SIL: case X64Reg_SI: case X64Reg_ESI: case X64Reg_RSI: return X64Reg_ESI; case X64Reg_AL: case X64Reg_AH: case X64Reg_AX: case X64Reg_EAX: case X64Reg_RAX: return X64Reg_EAX; case X64Reg_DL: case X64Reg_DH: case X64Reg_DX: case X64Reg_EDX: case X64Reg_RDX: return X64Reg_EDX; case X64Reg_CL: case X64Reg_CH: case X64Reg_CX: case X64Reg_ECX: case X64Reg_RCX: return X64Reg_ECX; case X64Reg_BL: case X64Reg_BH: case X64Reg_BX: case X64Reg_EBX: case X64Reg_RBX: return X64Reg_EBX; case X64Reg_R8B: case X64Reg_R8W: case X64Reg_R8D: case X64Reg_R8: return X64Reg_R8D; case X64Reg_R9B: case X64Reg_R9W: case X64Reg_R9D: case X64Reg_R9: return X64Reg_R9D; case X64Reg_R10B: case X64Reg_R10W: case X64Reg_R10D: case X64Reg_R10: return X64Reg_R10D; case X64Reg_R11B: case X64Reg_R11W: case X64Reg_R11D: case X64Reg_R11: return X64Reg_R11D; case X64Reg_R12B: case X64Reg_R12W: case X64Reg_R12D: case X64Reg_R12: return X64Reg_R12D; case X64Reg_R13B: case X64Reg_R13W: case X64Reg_R13D: case X64Reg_R13: return X64Reg_R13D; case X64Reg_R14B: case X64Reg_R14W: case X64Reg_R14D: case X64Reg_R14: return X64Reg_R14D; case X64Reg_R15B: case X64Reg_R15W: case X64Reg_R15D: case X64Reg_R15: return X64Reg_R15D; case X64Reg_XMM0_f32: case X64Reg_XMM0_f64: case X64Reg_M128_XMM0: return X64Reg_XMM0_f32; case X64Reg_XMM1_f32: case X64Reg_XMM1_f64: case X64Reg_M128_XMM1: return X64Reg_XMM1_f32; case X64Reg_XMM2_f32: case X64Reg_XMM2_f64: case X64Reg_M128_XMM2: return X64Reg_XMM2_f32; case X64Reg_XMM3_f32: case X64Reg_XMM3_f64: case X64Reg_M128_XMM3: return X64Reg_XMM3_f32; case X64Reg_XMM4_f32: case X64Reg_XMM4_f64: case X64Reg_M128_XMM4: return X64Reg_XMM4_f32; case X64Reg_XMM5_f32: case X64Reg_XMM5_f64: case X64Reg_M128_XMM5: return X64Reg_XMM5_f32; case X64Reg_XMM6_f32: case X64Reg_XMM6_f64: case X64Reg_M128_XMM6: return X64Reg_XMM6_f32; case X64Reg_XMM7_f32: case X64Reg_XMM7_f64: case X64Reg_M128_XMM7: return X64Reg_XMM7_f32; case X64Reg_XMM8_f32: case X64Reg_XMM8_f64: case X64Reg_M128_XMM8: return X64Reg_XMM8_f32; case X64Reg_XMM9_f32: case X64Reg_XMM9_f64: case X64Reg_M128_XMM9: return X64Reg_XMM9_f32; case X64Reg_XMM10_f32: case X64Reg_XMM10_f64: case X64Reg_M128_XMM10: return X64Reg_XMM10_f32; case X64Reg_XMM11_f32: case X64Reg_XMM11_f64: case X64Reg_M128_XMM11: return X64Reg_XMM11_f32; case X64Reg_XMM12_f32: case X64Reg_XMM12_f64: case X64Reg_M128_XMM12: return X64Reg_XMM12_f32; case X64Reg_XMM13_f32: case X64Reg_XMM13_f64: case X64Reg_M128_XMM13: return X64Reg_XMM13_f32; case X64Reg_XMM14_f32: case X64Reg_XMM14_f64: case X64Reg_M128_XMM14: return X64Reg_XMM14_f32; case X64Reg_XMM15_f32: case X64Reg_XMM15_f64: case X64Reg_M128_XMM15: return X64Reg_XMM15_f32; } } if (numBytes == 2) { switch (reg) { case X64Reg_DIL: case X64Reg_DI: case X64Reg_EDI: case X64Reg_RDI: return X64Reg_DI; case X64Reg_SIL: case X64Reg_SI: case X64Reg_ESI: case X64Reg_RSI: return X64Reg_SI; case X64Reg_AL: case X64Reg_AH: case X64Reg_AX: case X64Reg_EAX: case X64Reg_RAX: return X64Reg_AX; case X64Reg_DL: case X64Reg_DH: case X64Reg_DX: case X64Reg_EDX: case X64Reg_RDX: return X64Reg_DX; case X64Reg_CL: case X64Reg_CH: case X64Reg_CX: case X64Reg_ECX: case X64Reg_RCX: return X64Reg_CX; case X64Reg_BL: case X64Reg_BH: case X64Reg_BX: case X64Reg_EBX: case X64Reg_RBX: return X64Reg_BX; case X64Reg_R8B: case X64Reg_R8W: case X64Reg_R8D: case X64Reg_R8: return X64Reg_R8W; case X64Reg_R9B: case X64Reg_R9W: case X64Reg_R9D: case X64Reg_R9: return X64Reg_R9W; case X64Reg_R10B: case X64Reg_R10W: case X64Reg_R10D: case X64Reg_R10: return X64Reg_R10W; case X64Reg_R11B: case X64Reg_R11W: case X64Reg_R11D: case X64Reg_R11: return X64Reg_R11W; case X64Reg_R12B: case X64Reg_R12W: case X64Reg_R12D: case X64Reg_R12: return X64Reg_R12W; case X64Reg_R13B: case X64Reg_R13W: case X64Reg_R13D: case X64Reg_R13: return X64Reg_R13W; case X64Reg_R14B: case X64Reg_R14W: case X64Reg_R14D: case X64Reg_R14: return X64Reg_R14W; case X64Reg_R15B: case X64Reg_R15W: case X64Reg_R15D: case X64Reg_R15: return X64Reg_R15W; } } if (numBytes == 1) { switch (reg) { case X64Reg_DIL: case X64Reg_DI: case X64Reg_EDI: case X64Reg_RDI: return X64Reg_DIL; case X64Reg_SIL: case X64Reg_SI: case X64Reg_ESI: case X64Reg_RSI: return X64Reg_SIL; case X64Reg_AH: return X64Reg_AH; case X64Reg_AL: case X64Reg_AX: case X64Reg_EAX: case X64Reg_RAX: return X64Reg_AL; case X64Reg_DH: return X64Reg_DH; case X64Reg_DL: case X64Reg_DX: case X64Reg_EDX: case X64Reg_RDX: return X64Reg_DL; case X64Reg_CH: return X64Reg_CH; case X64Reg_CL: case X64Reg_CX: case X64Reg_ECX: case X64Reg_RCX: return X64Reg_CL; case X64Reg_BH: return X64Reg_BH; case X64Reg_BL: case X64Reg_BX: case X64Reg_EBX: case X64Reg_RBX: return X64Reg_BL; case X64Reg_R8B: case X64Reg_R8W: case X64Reg_R8D: case X64Reg_R8: return X64Reg_R8B; case X64Reg_R9B: case X64Reg_R9W: case X64Reg_R9D: case X64Reg_R9: return X64Reg_R9B; case X64Reg_R10B: case X64Reg_R10W: case X64Reg_R10D: case X64Reg_R10: return X64Reg_R10B; case X64Reg_R11B: case X64Reg_R11W: case X64Reg_R11D: case X64Reg_R11: return X64Reg_R11B; case X64Reg_R12B: case X64Reg_R12W: case X64Reg_R12D: case X64Reg_R12: return X64Reg_R12B; case X64Reg_R13B: case X64Reg_R13W: case X64Reg_R13D: case X64Reg_R13: return X64Reg_R13B; case X64Reg_R14B: case X64Reg_R14W: case X64Reg_R14D: case X64Reg_R14: return X64Reg_R14B; case X64Reg_R15B: case X64Reg_R15W: case X64Reg_R15D: case X64Reg_R15: return X64Reg_R15B; } } return X64Reg_None; } X64CPURegister BeMCContext::ResizeRegister(X64CPURegister reg, BeType* type) { if (type->IsVector()) return ResizeRegister(reg, 16); return ResizeRegister(reg, type->mSize); } X64CPURegister BeMCContext::GetFullRegister(X64CPURegister reg) { switch (reg) { case X64Reg_XMM0_f32: case X64Reg_XMM0_f64: case X64Reg_M128_XMM0: return X64Reg_M128_XMM0; case X64Reg_XMM1_f32: case X64Reg_XMM1_f64: case X64Reg_M128_XMM1: return X64Reg_M128_XMM1; case X64Reg_XMM2_f32: case X64Reg_XMM2_f64: case X64Reg_M128_XMM2: return X64Reg_M128_XMM2; case X64Reg_XMM3_f32: case X64Reg_XMM3_f64: case X64Reg_M128_XMM3: return X64Reg_M128_XMM3; case X64Reg_XMM4_f32: case X64Reg_XMM4_f64: case X64Reg_M128_XMM4: return X64Reg_M128_XMM4; case X64Reg_XMM5_f32: case X64Reg_XMM5_f64: case X64Reg_M128_XMM5: return X64Reg_M128_XMM5; case X64Reg_XMM6_f32: case X64Reg_XMM6_f64: case X64Reg_M128_XMM6: return X64Reg_M128_XMM6; case X64Reg_XMM7_f32: case X64Reg_XMM7_f64: case X64Reg_M128_XMM7: return X64Reg_M128_XMM7; case X64Reg_XMM8_f32: case X64Reg_XMM8_f64: case X64Reg_M128_XMM8: return X64Reg_M128_XMM8; case X64Reg_XMM9_f32: case X64Reg_XMM9_f64: case X64Reg_M128_XMM9: return X64Reg_M128_XMM9; case X64Reg_XMM10_f32: case X64Reg_XMM10_f64: case X64Reg_M128_XMM10: return X64Reg_M128_XMM10; case X64Reg_XMM11_f32: case X64Reg_XMM11_f64: case X64Reg_M128_XMM11: return X64Reg_M128_XMM11; case X64Reg_XMM12_f32: case X64Reg_XMM12_f64: case X64Reg_M128_XMM12: return X64Reg_M128_XMM12; case X64Reg_XMM13_f32: case X64Reg_XMM13_f64: case X64Reg_M128_XMM13: return X64Reg_M128_XMM13; case X64Reg_XMM14_f32: case X64Reg_XMM14_f64: case X64Reg_M128_XMM14: return X64Reg_M128_XMM14; case X64Reg_XMM15_f32: case X64Reg_XMM15_f64: case X64Reg_M128_XMM15: return X64Reg_M128_XMM15; } return ResizeRegister(reg, 8); } bool BeMCContext::IsAddress(BeMCOperand& operand) { if (operand.mKind == BeMCOperandKind_VRegAddr) return true; if (operand.mKind != BeMCOperandKind_VReg) return false; auto vregInfo = mVRegInfo[operand.mVRegIdx]; return IsAddress(vregInfo->mRelTo); } bool BeMCContext::IsAddressable(BeMCOperand& operand) { if (operand.mKind == BeMCOperandKind_Symbol) return true; if (operand.mKind == BeMCOperandKind_NativeReg) return true; if (operand.mKind == BeMCOperandKind_VRegAddr) return true; if (operand.mKind != BeMCOperandKind_VReg) return false; auto vregInfo = mVRegInfo[operand.mVRegIdx]; return IsAddressable(vregInfo->mRelTo); } bool BeMCContext::IsVRegExpr(BeMCOperand& operand) { if (!operand.IsVRegAny()) return false; auto vregInfo = mVRegInfo[operand.mVRegIdx]; return vregInfo->mIsExpr; } // Depth -1 is reserved explicitly for reg finalization, so we leave the vector type for emission void BeMCContext::FixOperand(BeMCOperand& operand, int depth) { // We don't want to check for VRegLoad, that would erase the dereference if ((operand.mKind != BeMCOperandKind_VReg) && (operand.mKind != BeMCOperandKind_VRegAddr)) return; auto vregInfo = mVRegInfo[operand.mVRegIdx]; if (vregInfo->mReg != X64Reg_None) { // For vectors we need the explicit type info if ((depth != -1) || (!vregInfo->mType->IsVector())) { operand.mKind = BeMCOperandKind_NativeReg; operand.mReg = vregInfo->mReg; return; } } if ((vregInfo->mIsRetVal) && (mCompositeRetVRegIdx != -1) && (mCompositeRetVRegIdx != operand.mVRegIdx)) { BF_ASSERT(mCompositeRetVRegIdx != -1); BeMCOperand origOperand = operand; operand = BeMCOperand::FromVReg(mCompositeRetVRegIdx); if ((origOperand.mKind == BeMCOperandKind_VReg) && (vregInfo->mType->IsNonVectorComposite())) operand.mKind = BeMCOperandKind_VRegLoad; FixOperand(operand, depth + 1); //auto retVRegInfo = mVRegInfo[mCompositeRetVRegIdx]; //operand = BeMCOperand::FromReg(retVRegInfo->mReg); } if (vregInfo->IsDirectRelToAny()) { auto checkOperand = vregInfo->mRelTo; if (checkOperand.IsVReg()) FixOperand(checkOperand, depth + 1); if (checkOperand.IsNativeReg()) { auto resizedReg = ResizeRegister(checkOperand.mReg, vregInfo->mType); if (resizedReg != X64Reg_None) operand = BeMCOperand::FromReg(resizedReg); } else if (checkOperand.mKind == BeMCOperandKind_Symbol) { auto symbol = mCOFFObject->mSymbols[checkOperand.mSymbolIdx]; if (AreTypesEquivalent(vregInfo->mType, symbol->mType)) { if (checkOperand.mKind == BeMCOperandKind_VRegAddr) { operand = OperandToAddr(checkOperand); } else operand = checkOperand; } } else if (checkOperand.mKind == BeMCOperandKind_SymbolAddr) { operand = checkOperand; } else if (checkOperand.IsImmediate()) { operand = checkOperand; } } } BeMCOperand BeMCContext::GetFixedOperand(const BeMCOperand& operand) { BeMCOperand copyOp = operand; FixOperand(copyOp); return copyOp; } uint8 BeMCContext::GetREX(const BeMCOperand& r, const BeMCOperand& rm, bool is64Bit) { //bool is64Bit = false; bool is64BitExR = false; bool is64BitExRM = false; bool forceRex = false; if (r.mKind == BeMCOperandKind_NativeReg) { /*if ((r.mReg >= X64Reg_RAX) && (r.mReg <= X64Reg_EFL)) BF_ASSERT(is64Bit); */ switch (r.mReg) { case X64Reg_SIL: case X64Reg_DIL: forceRex = true; break; case X64Reg_R8: case X64Reg_R9: case X64Reg_R10: case X64Reg_R11: case X64Reg_R12: case X64Reg_R13: case X64Reg_R14: case X64Reg_R15: case X64Reg_R8D: case X64Reg_R9D: case X64Reg_R10D: case X64Reg_R11D: case X64Reg_R12D: case X64Reg_R13D: case X64Reg_R14D: case X64Reg_R15D: case X64Reg_R8W: case X64Reg_R9W: case X64Reg_R10W: case X64Reg_R11W: case X64Reg_R12W: case X64Reg_R13W: case X64Reg_R14W: case X64Reg_R15W: case X64Reg_R8B: case X64Reg_R9B: case X64Reg_R10B: case X64Reg_R11B: case X64Reg_R12B: case X64Reg_R13B: case X64Reg_R14B: case X64Reg_R15B: case X64Reg_XMM8_f64: case X64Reg_XMM9_f64: case X64Reg_XMM10_f64: case X64Reg_XMM11_f64: case X64Reg_XMM12_f64: case X64Reg_XMM13_f64: case X64Reg_XMM14_f64: case X64Reg_XMM15_f64: case X64Reg_XMM8_f32: case X64Reg_XMM9_f32: case X64Reg_XMM10_f32: case X64Reg_XMM11_f32: case X64Reg_XMM12_f32: case X64Reg_XMM13_f32: case X64Reg_XMM14_f32: case X64Reg_XMM15_f32: case X64Reg_M128_XMM8: case X64Reg_M128_XMM9: case X64Reg_M128_XMM10: case X64Reg_M128_XMM11: case X64Reg_M128_XMM12: case X64Reg_M128_XMM13: case X64Reg_M128_XMM14: case X64Reg_M128_XMM15: is64BitExR = true; } } bool hasSibExRM = false; if (rm.mKind == BeMCOperandKind_NativeReg) { switch (rm.mReg) { case X64Reg_SIL: case X64Reg_DIL: forceRex = true; break; case X64Reg_R8: case X64Reg_R9: case X64Reg_R10: case X64Reg_R11: case X64Reg_R12: case X64Reg_R13: case X64Reg_R14: case X64Reg_R15: case X64Reg_R8D: case X64Reg_R9D: case X64Reg_R10D: case X64Reg_R11D: case X64Reg_R12D: case X64Reg_R13D: case X64Reg_R14D: case X64Reg_R15D: case X64Reg_R8W: case X64Reg_R9W: case X64Reg_R10W: case X64Reg_R11W: case X64Reg_R12W: case X64Reg_R13W: case X64Reg_R14W: case X64Reg_R15W: case X64Reg_R8B: case X64Reg_R9B: case X64Reg_R10B: case X64Reg_R11B: case X64Reg_R12B: case X64Reg_R13B: case X64Reg_R14B: case X64Reg_R15B: case X64Reg_XMM8_f64: case X64Reg_XMM9_f64: case X64Reg_XMM10_f64: case X64Reg_XMM11_f64: case X64Reg_XMM12_f64: case X64Reg_XMM13_f64: case X64Reg_XMM14_f64: case X64Reg_XMM15_f64: case X64Reg_XMM8_f32: case X64Reg_XMM9_f32: case X64Reg_XMM10_f32: case X64Reg_XMM11_f32: case X64Reg_XMM12_f32: case X64Reg_XMM13_f32: case X64Reg_XMM14_f32: case X64Reg_XMM15_f32: case X64Reg_M128_XMM8: case X64Reg_M128_XMM9: case X64Reg_M128_XMM10: case X64Reg_M128_XMM11: case X64Reg_M128_XMM12: case X64Reg_M128_XMM13: case X64Reg_M128_XMM14: case X64Reg_M128_XMM15: is64BitExRM = true; } } else if (rm.IsVRegAny()) { auto vregInfo = mVRegInfo[rm.mVRegIdx]; if (vregInfo->IsDirectRelTo()) return GetREX(r, vregInfo->mRelTo, is64Bit); BeRMParamsInfo rmInfo; GetRMParams(rm, rmInfo); is64BitExRM |= ((rmInfo.mRegA >= X64Reg_R8) && (rmInfo.mRegA <= X64Reg_R15)) || ((rmInfo.mRegA >= X64Reg_R8D) && (rmInfo.mRegA <= X64Reg_R15D)); hasSibExRM |= ((rmInfo.mRegB >= X64Reg_R8) && (rmInfo.mRegB <= X64Reg_R15)) || ((rmInfo.mRegB >= X64Reg_R8D) && (rmInfo.mRegB <= X64Reg_R15D)); } else if (rm.IsSymbol()) { auto sym = mCOFFObject->mSymbols[rm.mSymbolIdx]; if (sym->mIsTLS) { auto tlsReg = mVRegInfo[mTLSVRegIdx]->mReg; is64BitExRM |= (tlsReg >= X64Reg_R8) && (tlsReg <= X64Reg_R15); } } uint8 flags = 0; if (is64Bit) flags |= 8; if (is64BitExR) flags |= 4; if (hasSibExRM) flags |= 2; if (is64BitExRM) flags |= 1; if ((flags != 0) || (forceRex)) return (uint8)(0x40 | flags); return 0; } void BeMCContext::EmitREX(const BeMCOperand& r, const BeMCOperand& rm, bool is64Bit) { uint8 rex = GetREX(r, rm, is64Bit); if (rex != 0) mOut.Write(rex); } uint8 BeMCContext::EncodeRegNum(X64CPURegister regNum) { switch (regNum) { case X64Reg_AL: case X64Reg_AX: case X64Reg_EAX: case X64Reg_RAX: case X64Reg_R8: case X64Reg_R8D: case X64Reg_R8W: case X64Reg_R8B: case X64Reg_MM0: case X64Reg_XMM0_f32: case X64Reg_XMM0_f64: case X64Reg_M128_XMM0: case X64Reg_XMM8_f32: case X64Reg_XMM8_f64: case X64Reg_M128_XMM8: return 0; case X64Reg_CL: case X64Reg_CX: case X64Reg_ECX: case X64Reg_RCX: case X64Reg_R9: case X64Reg_R9D: case X64Reg_R9W: case X64Reg_R9B: case X64Reg_MM1: case X64Reg_XMM1_f32: case X64Reg_XMM1_f64: case X64Reg_M128_XMM1: case X64Reg_XMM9_f32: case X64Reg_XMM9_f64: case X64Reg_M128_XMM9: return 1; case X64Reg_DL: case X64Reg_DX: case X64Reg_EDX: case X64Reg_RDX: case X64Reg_R10: case X64Reg_R10D: case X64Reg_R10W: case X64Reg_R10B: case X64Reg_MM2: case X64Reg_XMM2_f32: case X64Reg_XMM2_f64: case X64Reg_M128_XMM2: case X64Reg_XMM10_f32: case X64Reg_XMM10_f64: case X64Reg_M128_XMM10: return 2; case X64Reg_BL: case X64Reg_BX: case X64Reg_EBX: case X64Reg_RBX: case X64Reg_R11: case X64Reg_R11D: case X64Reg_R11W: case X64Reg_R11B: case X64Reg_MM3: case X64Reg_XMM3_f32: case X64Reg_XMM3_f64: case X64Reg_M128_XMM3: case X64Reg_XMM11_f32: case X64Reg_XMM11_f64: case X64Reg_M128_XMM11: return 3; case X64Reg_None: // Useful for SIB/RM addr encodings case X64Reg_AH: //case X64Reg_SP: //case X64Reg_ESP: case X64Reg_RSP: case X64Reg_R12: case X64Reg_R12D: case X64Reg_R12W: case X64Reg_R12B: case X64Reg_MM4: case X64Reg_XMM4_f32: case X64Reg_XMM4_f64: case X64Reg_M128_XMM4: case X64Reg_XMM12_f32: case X64Reg_XMM12_f64: case X64Reg_M128_XMM12: return 4; case X64Reg_CH: //case X64Reg_BP: //case X64Reg_EBP: case X64Reg_RBP: case X64Reg_R13: case X64Reg_R13D: case X64Reg_R13W: case X64Reg_R13B: case X64Reg_MM5: case X64Reg_XMM5_f32: case X64Reg_XMM5_f64: case X64Reg_M128_XMM5: case X64Reg_XMM13_f32: case X64Reg_XMM13_f64: case X64Reg_M128_XMM13: return 5; case X64Reg_DH: case X64Reg_SIL: case X64Reg_SI: case X64Reg_ESI: case X64Reg_RSI: case X64Reg_R14: case X64Reg_R14D: case X64Reg_R14W: case X64Reg_R14B: case X64Reg_MM6: case X64Reg_XMM6_f32: case X64Reg_XMM6_f64: case X64Reg_M128_XMM6: case X64Reg_XMM14_f32: case X64Reg_XMM14_f64: case X64Reg_M128_XMM14: return 6; case X64Reg_BH: case X64Reg_DIL: case X64Reg_DI: case X64Reg_EDI: case X64Reg_RDI: case X64Reg_R15: case X64Reg_R15D: case X64Reg_R15W: case X64Reg_R15B: case X64Reg_MM7: case X64Reg_XMM7_f32: case X64Reg_XMM7_f64: case X64Reg_M128_XMM7: case X64Reg_XMM15_f32: case X64Reg_XMM15_f64: case X64Reg_M128_XMM15: return 7; } SoftFail("Invalid reg"); return -1; } int BeMCContext::GetRegSize(int regNum) { if ((regNum >= X64Reg_EAX) && (regNum <= X64Reg_EDI)) return 4; if ((regNum >= X64Reg_AX) && (regNum <= X64Reg_BX)) return 2; if ((regNum >= X64Reg_AL) && (regNum <= X64Reg_BH)) return 1; return 8; } void BeMCContext::ValidateRMResult(const BeMCOperand& operand, BeRMParamsInfo& rmInfo, bool doValidate) { if (!doValidate) return; if (rmInfo.mMode == BeMCRMMode_Invalid) return; //TODO: WTF- this previous version just seems to be wrong! Why did think this was true? the REX.X and REX.B flags fix these // in a SIB, the base can't be R13 (which is RBP+REX), and the scaled index can't be R12 (which is RSP+REX) //if ((regB != X64Reg_None) && // ((regA == X64Reg_R13) || (regB == X64Reg_R12))) //{ // // We can't just swap the regs if we have a scale applied // if (bScale != 1) // { // if (errorVReg != NULL) // *errorVReg = -2; // Scale error // return BeMCRMMode_Invalid; // } // BF_SWAP(regA, regB); //} // In a SIB, the base can't be RBP, and the scaled index can't be RSP if ((rmInfo.mRegB != X64Reg_None) && ((rmInfo.mRegA == X64Reg_RBP) || (rmInfo.mRegB == X64Reg_RSP))) { // We can't just swap the regs if we have a scale applied if (rmInfo.mBScale != 1) { rmInfo.mErrorVReg = -2; // Scale error rmInfo.mMode = BeMCRMMode_Invalid; return; } BF_SWAP(rmInfo.mRegA, rmInfo.mRegB); } return; } void BeMCContext::GetRMParams(const BeMCOperand& operand, BeRMParamsInfo& rmInfo, bool doValidate) { BeMCRMMode rmMode = BeMCRMMode_Invalid; if (operand.mKind == BeMCOperandKind_NativeReg) { if (rmInfo.mRegA == X64Reg_None) rmInfo.mRegA = operand.mReg; else if (rmInfo.mRegB == X64Reg_None) rmInfo.mRegB = operand.mReg; else { rmInfo.mMode = BeMCRMMode_Invalid; return; } rmInfo.mMode = BeMCRMMode_Direct; return ValidateRMResult(operand, rmInfo, doValidate); } else if (operand.IsImmediateInt()) { rmInfo.mDisp += (int)operand.mImmediate; rmInfo.mMode = BeMCRMMode_Direct; return ValidateRMResult(operand, rmInfo, doValidate); } if (operand.mKind == BeMCOperandKind_VReg) { auto vregInfo = mVRegInfo[operand.mVRegIdx]; if (!vregInfo->mIsExpr) { auto reg = vregInfo->mReg; if (reg != X64Reg_None) { if (rmInfo.mRegA == X64Reg_None) rmInfo.mRegA = reg; else if (rmInfo.mRegB == X64Reg_None) rmInfo.mRegB = reg; else { rmInfo.mMode = BeMCRMMode_Invalid; return; } rmInfo.mMode = BeMCRMMode_Direct; return ValidateRMResult(operand, rmInfo, doValidate); } GetRMParams(OperandToAddr(operand), rmInfo, doValidate); if (rmInfo.mMode == BeMCRMMode_Invalid) return; BF_ASSERT(rmInfo.mMode == BeMCRMMode_Direct); rmInfo.mMode = BeMCRMMode_Deref; return ValidateRMResult(OperandToAddr(operand), rmInfo, doValidate); } // Fall through } else if (operand.mKind == BeMCOperandKind_VRegAddr) { auto vregInfo = mVRegInfo[operand.mVRegIdx]; if (vregInfo->mIsExpr) { if (vregInfo->IsDirectRelToAny()) { if (vregInfo->mRelTo.mKind == BeMCOperandKind_VReg) return GetRMParams(BeMCOperand::FromVRegAddr(vregInfo->mRelTo.mVRegIdx), rmInfo, doValidate); else if (vregInfo->mRelTo.mKind == BeMCOperandKind_VRegLoad) return GetRMParams(BeMCOperand::FromVReg(vregInfo->mRelTo.mVRegIdx), rmInfo, doValidate); } rmInfo.mErrorVReg = operand.mVRegIdx; rmInfo.mMode = BeMCRMMode_Invalid; return; } BF_ASSERT(!vregInfo->mIsExpr); BF_ASSERT(vregInfo->mReg == X64Reg_None); X64CPURegister reg = X64Reg_None; if ((vregInfo->mIsRetVal) && (mCompositeRetVRegIdx != -1) && (mCompositeRetVRegIdx != operand.mVRegIdx)) { return GetRMParams(BeMCOperand::FromVReg(mCompositeRetVRegIdx), rmInfo, doValidate); } reg = mUseBP ? X64Reg_RBP : X64Reg_RSP; rmInfo.mDisp = mStackSize + vregInfo->mFrameOffset; if (rmInfo.mRegA == X64Reg_None) rmInfo.mRegA = reg; else if (rmInfo.mRegB == X64Reg_None) rmInfo.mRegB = reg; rmInfo.mMode = BeMCRMMode_Direct; return ValidateRMResult(operand, rmInfo, doValidate); } else if (operand.mKind == BeMCOperandKind_VRegLoad) { auto vregInfo = mVRegInfo[operand.mVRegIdx]; if (!vregInfo->mIsExpr) { auto reg = vregInfo->mReg; if (reg == X64Reg_None) { rmInfo.mErrorVReg = operand.mVRegIdx; rmInfo.mMode = BeMCRMMode_Invalid; return; } if (rmInfo.mRegA == X64Reg_None) rmInfo.mRegA = reg; else if (rmInfo.mRegB == X64Reg_None) rmInfo.mRegB = reg; rmInfo.mMode = BeMCRMMode_Deref; return ValidateRMResult(operand, rmInfo, doValidate); } } else { rmInfo.mMode = BeMCRMMode_Invalid; return; } auto vregInfo = mVRegInfo[operand.mVRegIdx]; BF_ASSERT(vregInfo->mIsExpr); if (vregInfo->mRelTo) { auto oldRegA = rmInfo.mRegA; GetRMParams(vregInfo->mRelTo, rmInfo, false); if (rmInfo.mMode == BeMCRMMode_Invalid) { if (rmInfo.mErrorVReg == -1) { rmInfo.mErrorVReg = operand.mVRegIdx; } return; } if (rmInfo.mMode == BeMCRMMode_Deref) { // A deref can only stand alone, and no double-derefs if ((vregInfo->mRelOffset) || (vregInfo->mRelOffsetScale != 1) || (operand.mKind == BeMCOperandKind_VRegLoad)) { BF_ASSERT(vregInfo->mRelTo.IsVRegAny()); rmInfo.mErrorVReg = vregInfo->mRelTo.mVRegIdx; // For some reason we had changed this to: //*errorVReg = operand.mVRegIdx; // This doesn't work, it's the deref that we want to isolate, otherwise we just end up creating another invalid expression rmInfo.mMode = BeMCRMMode_Invalid; return; } if (operand.mKind == BeMCOperandKind_VRegAddr) { rmInfo.mMode = BeMCRMMode_Direct; return ValidateRMResult(vregInfo->mRelTo, rmInfo, doValidate); } else if (operand.mKind == BeMCOperandKind_VReg) { rmInfo.mMode = BeMCRMMode_Deref; return ValidateRMResult(vregInfo->mRelTo, rmInfo, doValidate); } else NotImpl(); } } if (vregInfo->mRelOffset) { if (vregInfo->mRelOffsetScale != 1) rmInfo.mVRegWithScaledOffset = operand.mVRegIdx; bool relToComplicated = (rmInfo.mRegB != X64Reg_None) || (rmInfo.mBScale != 1); GetRMParams(vregInfo->mRelOffset, rmInfo, false); if (rmInfo.mMode == BeMCRMMode_Invalid) { // Pick the "most complicated" between relOffset and relTo? if (relToComplicated) { BF_ASSERT(vregInfo->mRelTo.IsVRegAny()); rmInfo.mErrorVReg = vregInfo->mRelTo.mVRegIdx; } else { BF_ASSERT(vregInfo->mRelOffset.IsVRegAny()); rmInfo.mErrorVReg = vregInfo->mRelOffset.mVRegIdx; } rmInfo.mMode = BeMCRMMode_Invalid; return; } if (rmInfo.mMode == BeMCRMMode_Deref) // Deref only allowed on relTo { BF_ASSERT(vregInfo->mRelOffset.IsVRegAny()); rmInfo.mErrorVReg = vregInfo->mRelOffset.mVRegIdx; rmInfo.mMode = BeMCRMMode_Invalid; return; } } bool success = true; if (vregInfo->mRelOffsetScale != 1) { if (rmInfo.mBScale != 1) success = false; rmInfo.mBScale = vregInfo->mRelOffsetScale; if ((rmInfo.mBScale != 2) && (rmInfo.mBScale != 4) && (rmInfo.mBScale != 8)) success = false; if (rmInfo.mRegB == X64Reg_None) { rmInfo.mRegB = rmInfo.mRegA; rmInfo.mRegA = X64Reg_None; } } if (!success) { if (rmInfo.mErrorVReg == -1) rmInfo.mErrorVReg = operand.mVRegIdx; } if (success) { if (operand.mKind == BeMCOperandKind_VRegLoad) { rmInfo.mMode = BeMCRMMode_Deref; return ValidateRMResult(vregInfo->mRelOffset, rmInfo, doValidate); } else { rmInfo.mMode = BeMCRMMode_Direct; return ValidateRMResult(vregInfo->mRelOffset, rmInfo, doValidate); } } else { rmInfo.mMode = BeMCRMMode_Invalid; return; } } void BeMCContext::DisableRegister(const BeMCOperand& operand, X64CPURegister reg) { auto vregInfo = GetVRegInfo(operand); if (vregInfo == NULL) return; switch (reg) { case X64Reg_RAX: vregInfo->mDisableRAX = true; break; case X64Reg_RDX: vregInfo->mDisableRDX = true; break; case X64Reg_SIL: vregInfo->mDisableEx = true; break; default: NotImpl(); } if (vregInfo->mRelTo) DisableRegister(vregInfo->mRelTo, reg); if (vregInfo->mRelOffset) DisableRegister(vregInfo->mRelOffset, reg); } void BeMCContext::MarkInvalidRMRegs(const BeMCOperand& operand) { if (!operand.IsVRegAny()) return; auto vregInfo = mVRegInfo[operand.mVRegIdx]; if (vregInfo->mReg == X64Reg_R12) // Can't allow a scaled R12, it's illegal in ModRM vregInfo->mDisableR12 = true; if (vregInfo->mReg == X64Reg_R13) // Can't allow a base R13, it's illegal in ModRM vregInfo->mDisableR13 = true; MarkInvalidRMRegs(vregInfo->mRelTo); MarkInvalidRMRegs(vregInfo->mRelOffset); } void BeMCContext::GetUsedRegs(const BeMCOperand& operand, X64CPURegister& regA, X64CPURegister& regB) { BeRMParamsInfo rmInfo; GetRMParams(operand, rmInfo); if (rmInfo.mRegA != X64Reg_None) regA = GetFullRegister(rmInfo.mRegA); if (rmInfo.mRegB != X64Reg_None) regB = GetFullRegister(rmInfo.mRegB); } BeMCRMMode BeMCContext::GetRMForm(const BeMCOperand& operand, bool& isMulti) { BeRMParamsInfo rmInfo; GetRMParams(operand, rmInfo); isMulti = (rmInfo.mRegB != X64Reg_None) || (rmInfo.mDisp != 0); return rmInfo.mMode; } void BeMCContext::GetValAddr(const BeMCOperand& operand, X64CPURegister& reg, int& offset) { if (operand.IsNativeReg()) { reg = operand.mReg; return; } BF_ASSERT(operand.IsVRegAny()); auto vregInfo = mVRegInfo[operand.mVRegIdx]; if (operand.mKind == BeMCOperandKind_VReg) { if (vregInfo->mRelTo) { GetValAddr(vregInfo->mRelTo, reg, offset); if (vregInfo->mRelOffset) { BF_ASSERT(vregInfo->mRelOffset.IsImmediateInt()); offset += vregInfo->mRelOffset.mImmediate; } return; } BF_ASSERT(vregInfo->mType->IsPointer()); BF_ASSERT(vregInfo->mReg != X64Reg_None); reg = vregInfo->mReg; return; } if ((vregInfo->mIsRetVal) && (mCompositeRetVRegIdx != -1) && (mCompositeRetVRegIdx != operand.mVRegIdx)) { BF_ASSERT(mCompositeRetVRegIdx != -1); GetValAddr(BeMCOperand::FromVRegAddr(mCompositeRetVRegIdx), reg, offset); return; } while (vregInfo->IsDirectRelTo()) { vregInfo = GetVRegInfo(vregInfo->mRelTo); } if ((mCompositeRetVRegIdx == operand.mVRegIdx) && (vregInfo->mReg != X64Reg_None)) { reg = vregInfo->mReg; return; } reg = mUseBP ? X64Reg_RBP : X64Reg_RSP; offset = mStackSize + vregInfo->mFrameOffset; } int BeMCContext::GetHighestVRegRef(const BeMCOperand& operand) { if (!operand.IsVRegAny()) return -1; int highestIdx = operand.mVRegIdx; auto vregInfo = mVRegInfo[operand.mVRegIdx]; if (vregInfo->mRelTo) highestIdx = std::max(highestIdx, GetHighestVRegRef(vregInfo->mRelTo)); if (vregInfo->mRelOffset) highestIdx = std::max(highestIdx, GetHighestVRegRef(vregInfo->mRelOffset)); return highestIdx; } uint8 BeMCContext::GetJumpOpCode(BeCmpKind cmpKind, bool isLong) { if (isLong) { switch (cmpKind) { case BeCmpKind_None: // JMP return 0xE9; case BeCmpKind_SLT: // JL return 0x8C; case BeCmpKind_ULT: // JB return 0x82; case BeCmpKind_SLE: // JLE return 0x8E; case BeCmpKind_ULE: // JBE return 0x86; case BeCmpKind_EQ: // JE return 0x84; case BeCmpKind_NE: // JNE return 0x85; case BeCmpKind_SGT: // JG return 0x8F; case BeCmpKind_UGT: // JA return 0x87; case BeCmpKind_SGE: // JGE return 0x8D; case BeCmpKind_UGE: // JAE return 0x83; case BeCmpKind_NB: // JNB return 0x83; case BeCmpKind_NO: // JNO return 0x81; } } else { switch (cmpKind) { case BeCmpKind_None: // JMP return 0xEB; case BeCmpKind_SLT: // JL return 0x7C; case BeCmpKind_ULT: // JB return 0x72; case BeCmpKind_SLE: // JLE return 0x7E; case BeCmpKind_ULE: // JBE return 0x76; case BeCmpKind_EQ: // JE return 0x74; case BeCmpKind_NE: // JNE return 0x75; case BeCmpKind_SGT: // JG return 0x7F; case BeCmpKind_UGT: // JA return 0x77; case BeCmpKind_SGE: // JGE return 0x7D; case BeCmpKind_UGE: // JAE return 0x73; case BeCmpKind_NB: // JNB return 0x73; case BeCmpKind_NO: // JNO return 0x71; } } return 0; } void BeMCContext::Emit(uint8 val) { mOut.Write(val); } void BeMCContext::EmitModRM(int mod, int reg, int rm) { mOut.Write((uint8)((mod << 6) | (reg << 3) | rm)); } void BeMCContext::EmitModRMRel(int rx, X64CPURegister regA, X64CPURegister regB, int bScale, int relOffset) { if (regB != X64Reg_None) { // We can't encode regA as RBP in the SIB base BF_ASSERT(regA != X64Reg_RBP); // We can't encode RegB as RSP in the SIB index BF_ASSERT(regB != X64Reg_RSP); } uint8 modRM = (rx << 3); if ((regB == X64Reg_None) && (regA != X64Reg_RSP) && (regA != X64Reg_R12)) // RSP/R12 can't be encoded without a SIB { if ((relOffset == 0) && (regA != X64Reg_RBP) && (regA != X64Reg_R13)) // RBP/R13 can't be encoded with Mod0 { modRM |= (0x0 << 6) | (EncodeRegNum(regA)); // [regA] mOut.Write(modRM); return; } else if ((relOffset >= -0x80) && (relOffset <= 0x7F)) { modRM |= (0x1 << 6) | (EncodeRegNum(regA)); // [regA]+disp8 mOut.Write(modRM); mOut.Write((uint8)relOffset); return; } else { modRM |= (0x2 << 6) | (EncodeRegNum(regA)); // [regA]+disp32 mOut.Write(modRM); mOut.Write((int32)relOffset); return; } } else if (regA == X64Reg_None) // The only option is disp32 { modRM |= (0x0 << 6) | (0x4); // [--][--]+disp32 mOut.Write(modRM); uint8 sib = ((bScale == 2) ? 1 : (bScale == 4) ? 2 : (bScale == 8) ? 3 : 0) << 6; sib |= (EncodeRegNum(regB) << 3) | 5; mOut.Write(sib); mOut.Write((int32)relOffset); return; } else { // Do no-offset version UNLESS we have a base of R13, which has its representation stolen by '[--][--]+disp32', // so we must use the longer +disp8 version in that case if ((relOffset == 0) && (regA != X64Reg_R13)) { modRM |= (0x0 << 6) | (0x4); // [--][--] mOut.Write(modRM); uint8 sib = ((bScale == 2) ? 1 : (bScale == 4) ? 2 : (bScale == 8) ? 3 : 0) << 6; sib |= (EncodeRegNum(regB) << 3) | EncodeRegNum(regA); mOut.Write(sib); return; } else if ((relOffset >= -0x80) && (relOffset <= 0x7F)) { modRM |= (0x1 << 6) | (0x4); // [--][--]+disp8 mOut.Write(modRM); uint8 sib = ((bScale == 2) ? 1 : (bScale == 4) ? 2 : (bScale == 8) ? 3 : 0) << 6; sib |= (EncodeRegNum(regB) << 3) | EncodeRegNum(regA); mOut.Write(sib); mOut.Write((uint8)relOffset); return; } else { modRM |= (0x2 << 6) | (0x4); // [--][--]+disp32 mOut.Write(modRM); uint8 sib = ((bScale == 2) ? 1 : (bScale == 4) ? 2 : (bScale == 8) ? 3 : 0) << 6; sib |= (EncodeRegNum(regB) << 3) | EncodeRegNum(regA); mOut.Write(sib); mOut.Write((int32)relOffset); return; } } } void BeMCContext::EmitModRMRelStack(int rx, int regOffset, int scale) { EmitModRMRel(rx, mUseBP ? X64Reg_RBP : X64Reg_RSP, X64Reg_None, scale, regOffset); } void BeMCContext::EmitModRM(int rx, BeMCOperand rm, int relocOfs) { uint8 modRM = (rx << 3); //int relocIdx = -1; if (rm.IsImmediateFloat()) { EmitModRM_XMM_IMM(rx, rm); return; } else if ((rm.mKind == BeMCOperandKind_Symbol) || (rm.mKind == BeMCOperandKind_SymbolAddr)) { BeMCRelocation reloc; auto sym = mCOFFObject->mSymbols[rm.mSymbolIdx]; if (sym->mIsTLS) { auto vregInfo = mVRegInfo[mTLSVRegIdx]; modRM |= (2 << 6) | EncodeRegNum(vregInfo->mReg); reloc.mKind = BeMCRelocationKind_SECREL; relocOfs = 0; } else { modRM |= 0x5; // RIP + reloc.mKind = BeMCRelocationKind_REL32; } Emit(modRM); reloc.mOffset = mOut.GetPos(); reloc.mSymTableIdx = rm.mSymbolIdx; mCOFFObject->mTextSect.mRelocs.push_back(reloc); mTextRelocs.push_back((int)mCOFFObject->mTextSect.mRelocs.size() - 1); //relocIdx = (int)mOut.GetSize(); mOut.Write((int32)relocOfs); return; } else if (rm.mKind == BeMCOperandKind_NativeReg) { modRM |= (0x3 << 6) | (EncodeRegNum(rm.mReg)); } else if (rm.IsVRegAny()) { auto vregInfo = mVRegInfo[rm.mVRegIdx]; if ((vregInfo->mRelTo.mKind == BeMCOperandKind_SymbolAddr) && (vregInfo->mRelOffset.IsImmediateInt()) && (vregInfo->mRelOffsetScale == 1)) { return EmitModRM(rx, vregInfo->mRelTo, relocOfs + vregInfo->mRelOffset.mImmediate); } if ((rm.IsVReg()) && (vregInfo->IsDirectRelToAny())) return EmitModRM(rx, vregInfo->mRelTo, relocOfs); BeRMParamsInfo rmInfo; GetRMParams(rm, rmInfo); //BF_ASSERT(resultType != BeMCRMMode_Invalid); BF_ASSERT(rmInfo.mMode == BeMCRMMode_Deref); EmitModRMRel(rx, rmInfo.mRegA, rmInfo.mRegB, rmInfo.mBScale, rmInfo.mDisp); return; } else { SoftFail("Invalid rm"); } mOut.Write(modRM); } void BeMCContext::EmitModRM(BeMCOperand r, BeMCOperand rm, int relocOfs) { uint8 modRM = 0; BF_ASSERT(r.mKind == BeMCOperandKind_NativeReg); EmitModRM(EncodeRegNum(r.mReg), rm, relocOfs); } void BeMCContext::EmitModRM_Addr(BeMCOperand r, BeMCOperand rm) { uint8 modRM = 0; BF_ASSERT(r.mKind == BeMCOperandKind_NativeReg); modRM = EncodeRegNum(r.mReg) << 3; if (rm.mKind == BeMCOperandKind_NativeReg) { modRM |= (0x0 << 6) | (EncodeRegNum(rm.mReg)); } else if ((rm.mKind == BeMCOperandKind_Symbol) || (rm.mKind == BeMCOperandKind_SymbolAddr)) { EmitModRM(r, rm); return; } else if (rm.mKind == BeMCOperandKind_VReg) { auto vregInfo = mVRegInfo[rm.mVRegIdx]; if (vregInfo->mIsExpr) { if (vregInfo->mRelOffsetScale == 1) { X64CPURegister relToReg = X64Reg_None; int regOffset = 0; if (vregInfo->mRelOffset) { BF_ASSERT(vregInfo->mRelOffset.IsImmediate()); regOffset = (int)vregInfo->mRelOffset.mImmediate; } if (vregInfo->mRelTo.IsNativeReg()) { relToReg = vregInfo->mRelTo.mReg; } else if (vregInfo->mRelTo.IsVRegAny()) { auto relVRegInfo = GetVRegInfo(vregInfo->mRelTo); if (relVRegInfo->mRelTo) { BeRMParamsInfo rmInfo; GetRMParams(rm, rmInfo); BF_ASSERT(rmInfo.mMode != BeMCRMMode_Invalid); EmitModRMRel(EncodeRegNum(r.mReg), rmInfo.mRegA, rmInfo.mRegB, rmInfo.mBScale, rmInfo.mDisp); return; } else NotImpl(); } else NotImpl(); EmitModRMRel(EncodeRegNum(r.mReg), relToReg, X64Reg_None, 1, regOffset); return; } else { if (vregInfo->mRelOffset) { BF_ASSERT(vregInfo->mRelOffset.IsImmediate()); int regOffset = vregInfo->mRelOffset.mImmediate; int scaleVal = 0; if (vregInfo->mRelOffsetScale == 2) scaleVal = 0x1; else if (vregInfo->mRelOffsetScale == 4) scaleVal = 0x2; else if (vregInfo->mRelOffsetScale == 8) scaleVal = 0x3; modRM |= (0x0 << 6) | (0x4); // [--][--] mOut.Write(modRM); uint8 sib = (scaleVal << 6) | (EncodeRegNum(vregInfo->mRelTo.mReg) << 3) | (0x5); // [* + imm32] mOut.Write(sib); mOut.Write((int32)regOffset); return; } } } else { SoftFail("Illegal expression in EmitModRM_Addr"); } } else { SoftFail("Invalid rm in EmitModRM_Addr"); } mOut.Write(modRM); } void BeMCContext::EmitModRM_XMM_IMM(int rx, BeMCOperand& imm) { Emit((rx << 3) | (0x5)); // RIP + BeMCSymbol* sym = NULL; if (imm.mKind == BeMCOperandKind_Immediate_f32) { String name = StrFormat("__real@%08x", *(int*)&imm.mImmF32); sym = mCOFFObject->GetCOMDAT(name, &imm.mImmF32, 4, 4); } else if (imm.mKind == BeMCOperandKind_Immediate_f64) { String name = StrFormat("__real@%016llx", *(int64*)&imm.mImmF64); sym = mCOFFObject->GetCOMDAT(name, &imm.mImmF64, 8, 8); } else if (imm.mKind == BeMCOperandKind_Immediate_f32_Packed128) { String name = StrFormat("__real@%08x_packed", *(int*)&imm.mImmF32); float data[4] = { imm.mImmF32, 0, 0, 0 }; sym = mCOFFObject->GetCOMDAT(name, data, 16, 16); } else if (imm.mKind == BeMCOperandKind_Immediate_f64_Packed128) { String name = StrFormat("__real@%016llx_packed", *(int64*)&imm.mImmF64); double data[2] = { imm.mImmF64, 0 }; sym = mCOFFObject->GetCOMDAT(name, data, 16, 16); } else if (imm.mKind == BeMCOperandKind_Immediate_int32x4) { String name = StrFormat("__real@%016llx_x4_packed", *(int*)&imm.mImmediate); int32 data[4] = { (int32)imm.mImmediate, (int32)imm.mImmediate, (int32)imm.mImmediate, (int32)imm.mImmediate }; sym = mCOFFObject->GetCOMDAT(name, data, 16, 16); } else SoftFail("Unhandled value type in EmitModRM_XMM_IMM"); BeMCRelocation reloc; reloc.mKind = BeMCRelocationKind_REL32; reloc.mOffset = mOut.GetPos(); reloc.mSymTableIdx = sym->mIdx; mCOFFObject->mTextSect.mRelocs.push_back(reloc); mTextRelocs.push_back((int)mCOFFObject->mTextSect.mRelocs.size() - 1); mOut.Write((int32)0); } void BeMCContext::VRegSetInitialized(BeMCBlock* mcBlock, BeMCInst* inst, const BeMCOperand& operand, SizedArrayImpl& addVec, SizedArrayImpl& removeVec, bool deepSet, bool doSet) { if (operand.IsSymbol()) { auto sym = mCOFFObject->mSymbols[operand.mSymbolIdx]; if (sym->mIsTLS) { VRegSetInitialized(mcBlock, inst, BeMCOperand::FromVReg(mTLSVRegIdx), addVec, removeVec, deepSet, doSet); return; } } if (operand.mKind == BeMCOperandKind_CmpResult) { auto& cmpResult = mCmpResults[operand.mCmpResultIdx]; if (cmpResult.mResultVRegIdx != -1) VRegSetInitialized(mcBlock, inst, BeMCOperand::FromVReg(cmpResult.mResultVRegIdx), addVec, removeVec, deepSet, doSet); return; } if (operand.mKind == BeMCOperandKind_VRegPair) { VRegSetInitialized(mcBlock, inst, BeMCOperand::FromVReg(operand.mVRegPair.mVRegIdx0), addVec, removeVec, deepSet, doSet); VRegSetInitialized(mcBlock, inst, BeMCOperand::FromVReg(operand.mVRegPair.mVRegIdx1), addVec, removeVec, deepSet, doSet); } if (!operand.IsVRegAny()) return; auto vregInfo = mVRegInfo[operand.mVRegIdx]; if (vregInfo->mDefOnFirstUse) { int insertIdx = FindSafeInstInsertPos(*mInsertInstIdxRef); AllocInst(BeMCInstKind_Def, operand, insertIdx); vregInfo->mDefOnFirstUse = false; if (insertIdx <= *mInsertInstIdxRef) (*mInsertInstIdxRef)++; } if (vregInfo->mRelTo) VRegSetInitialized(mcBlock, inst, vregInfo->mRelTo, addVec, removeVec, deepSet, doSet && deepSet); if (vregInfo->mRelOffset) VRegSetInitialized(mcBlock, inst, vregInfo->mRelOffset, addVec, removeVec, deepSet, doSet && deepSet); if (doSet) { if (!removeVec.Contains(operand.mVRegIdx)) addVec.push_back(operand.mVRegIdx); removeVec.push_back(mVRegInitializedContext.GetIdx(operand.mVRegIdx, BeTrackKind_Uninitialized)); } } BeMCInst* BeMCContext::FindSafePreBranchInst(BeMCBlock* mcBlock) { for (int instIdx = 0; instIdx < (int)mcBlock->mInstructions.size(); instIdx++) { auto inst = mcBlock->mInstructions[instIdx]; // We set the definition to just right before the first branch if ((inst->mKind == BeMCInstKind_Br) || (inst->mKind == BeMCInstKind_CondBr)) { // Don't separate out Def/Cmp/CmpToBool/Test calls while (instIdx > 0) { auto checkInst = mcBlock->mInstructions[instIdx - 1]; if ((checkInst->mKind != BeMCInstKind_Def) && (checkInst->mKind != BeMCInstKind_DefLoad) && (checkInst->mKind != BeMCInstKind_Test) && (checkInst->mKind != BeMCInstKind_Cmp) && (checkInst->mKind != BeMCInstKind_CmpToBool)) return inst; inst = checkInst; instIdx--; } break; } } return mcBlock->mInstructions[0]; } void BeMCContext::InitializedPassHelper(BeMCBlock* mcBlock, BeVTrackingGenContext* genCtx, bool& modifiedBlockBefore, bool& modifiedBlockAfter) { modifiedBlockBefore = false; modifiedBlockAfter = false; genCtx->mBlocks[mcBlock->mBlockIdx].mGenerateCount++; genCtx->mCalls++; genCtx->mHandledCalls++; BeMDNode* curDbgScope = NULL; BeVTrackingList* vregsInitialized = mcBlock->mPredVRegsInitialized; //OutputDebugStrF("InitializedPassHelper %@\n", vregsInitialized.mList); mActiveBlock = mcBlock; for (int instIdx = 0; instIdx < (int)mcBlock->mInstructions.size(); instIdx++) { genCtx->mInstructions++; mInsertInstIdxRef = &instIdx; auto inst = mcBlock->mInstructions[instIdx]; inst->mVRegsInitialized = vregsInitialized; bool deepSet = false; if (inst->mKind == BeMCInstKind_LifetimeStart) continue; SizedArray removeVec; SizedArray addVec; SizedArray filteredRemoveVec; SizedArray filteredAddVec; // { auto checkLastUseRecord = inst->mVRegLastUseRecord; while (checkLastUseRecord != NULL) { removeVec.Add(checkLastUseRecord->mVRegIdx); checkLastUseRecord = checkLastUseRecord->mNext; } } if ((inst->mKind == BeMCInstKind_ValueScopeSoftEnd) || (inst->mKind == BeMCInstKind_ValueScopeHardEnd)) { bool isSoft = inst->mKind == BeMCInstKind_ValueScopeSoftEnd; int startVRegIdx = (int)inst->mArg0.mImmediate; int endVRegIdx = (int)inst->mArg1.mImmediate; int listIdx = mVRegInitializedContext.FindIndex(vregsInitialized, startVRegIdx); if (listIdx < 0) listIdx = ~listIdx; for (; listIdx < vregsInitialized->mSize; listIdx++) { int vregIdx = vregsInitialized->mEntries[listIdx]; if (vregIdx >= endVRegIdx) break; auto vregInfo = mVRegInfo[vregIdx]; if (isSoft) { if (vregInfo->mValueScopeRetainedKind >= BeMCValueScopeRetainKind_Soft) continue; } else { if (vregInfo->mValueScopeRetainedKind >= BeMCValueScopeRetainKind_Hard) continue; } filteredRemoveVec.push_back(vregIdx); } } if ((inst->mResult) && (inst->mResult.mKind == BeMCOperandKind_CmpResult)) { auto& cmpResult = mCmpResults[inst->mResult.mCmpResultIdx]; if (cmpResult.mResultVRegIdx != -1) { auto cmpToBoolInst = AllocInst(BeMCInstKind_CmpToBool, BeMCOperand::FromCmpKind(cmpResult.mCmpKind), BeMCOperand(), instIdx + 1); cmpToBoolInst->mResult = BeMCOperand::FromVReg(cmpResult.mResultVRegIdx); } inst->mResult = BeMCOperand(); } if (inst->mKind == BeMCInstKind_DbgDecl) { if (!mVRegInitializedContext.IsSet(vregsInitialized, inst->mArg0.mVRegIdx)) { auto vregInfo = mVRegInfo[inst->mArg0.mVRegIdx]; if (vregInfo->mIsExpr) // For shadowed inlined params, set as initialized addVec.push_back(inst->mArg0.mVRegIdx); else { if ((vregInfo->mDbgVariable != NULL) && (vregInfo->mDbgVariable->mInitType == BfIRInitType_NotNeeded_AliveOnDecl)) addVec.push_back(inst->mArg0.mVRegIdx); else addVec.push_back(mVRegInitializedContext.GetIdx(inst->mArg0.mVRegIdx, BeTrackKind_Uninitialized)); } } vregsInitialized = mVRegInitializedContext.Modify(vregsInitialized, addVec, removeVec, filteredAddVec, filteredRemoveVec); continue; } if (inst->mKind == BeMCInstKind_Def) { //deepSet = true; // If we take a pointer to a local variable before initializing it, we need to consider it as being "ambiguously initialized" auto vregInfo = GetVRegInfo(inst->mArg0); if (vregInfo->mRelTo.mKind == BeMCOperandKind_VRegAddr) { auto relVRegInfo = GetVRegInfo(vregInfo->mRelTo); if ((relVRegInfo->mDbgVariable != NULL) && (!mVRegInitializedContext.IsSet(vregsInitialized, vregInfo->mRelTo.mVRegIdx))) relVRegInfo->mHasAmbiguousInitialization = true; } } if (inst->mKind == BeMCInstKind_LifetimeEnd) { // In some cases we can have a dbg variable that actually points to a global variable (due to macros/inlining/etc), so this check is for that case: if (inst->mArg0.IsVRegAny()) { DedupPushBack(removeVec, inst->mArg0.mVRegIdx); DedupPushBack(removeVec, mVRegInitializedContext.GetIdx(inst->mArg0.mVRegIdx, BeTrackKind_Uninitialized)); } vregsInitialized = mVRegInitializedContext.Modify(vregsInitialized, addVec, removeVec, filteredAddVec, filteredRemoveVec); continue; } if (inst->mKind == BeMCInstKind_DefLoad) { // This is also an address-taking operation, OR it's just a usage of an explicitly-undefined value auto loadRegInfo = GetVRegInfo(inst->mArg0); if (loadRegInfo->mRelTo.IsVRegAny()) { auto relVRegInfo = GetVRegInfo(loadRegInfo->mRelTo); if ((relVRegInfo->mDbgVariable != NULL) && (!mVRegInitializedContext.IsSet(vregsInitialized, loadRegInfo->mRelTo.mVRegIdx))) relVRegInfo->mHasAmbiguousInitialization = true; } } if (inst->IsMov()) { // This is also an address-taking operation, OR it's just a usage of an explicitly-undefined value if (inst->mArg1.IsVRegAny()) { auto srcRegInfo = GetVRegInfo(inst->mArg1); if ((srcRegInfo->mDbgVariable != NULL) && (!mVRegInitializedContext.IsSet(vregsInitialized, inst->mArg1.mVRegIdx))) srcRegInfo->mHasAmbiguousInitialization = true; } } int cpyDestVRegIdx = -1; if ((inst->mKind == BeMCInstKind_MemCpy) || (inst->mKind == BeMCInstKind_MemSet)) { cpyDestVRegIdx = inst->mArg1.mVRegPair.mVRegIdx0; } //deepSet = true; auto destArg = inst->GetDest(); auto operands = { &inst->mResult, &inst->mArg0, &inst->mArg1 }; for (auto op : operands) { VRegSetInitialized(mcBlock, inst, *op, addVec, removeVec, deepSet || (op == destArg), true); } for (int removeIdx = 0; removeIdx < (int)filteredRemoveVec.size(); removeIdx++) { int vregIdx = filteredRemoveVec[removeIdx]; if (vregIdx >= mVRegInitializedContext.mNumItems) continue; auto vregInfo = mVRegInfo[vregIdx]; if (vregInfo->mChainLifetimeEnd) { auto mcCheck = vregInfo->mRelTo; while (mcCheck.IsVRegAny()) { auto checkInfo = mVRegInfo[mcCheck.mVRegIdx]; removeVec.push_back(mcCheck.mVRegIdx); mcCheck = checkInfo->mRelTo; } } } vregsInitialized = mVRegInitializedContext.Modify(vregsInitialized, addVec, removeVec, filteredAddVec, filteredRemoveVec); } mInsertInstIdxRef = NULL; mActiveBlock = NULL; for (int succIdx = 0; succIdx < (int)mcBlock->mSuccs.size(); succIdx++) { BeMCBlock* succ = mcBlock->mSuccs[succIdx]; auto& entry = genCtx->mBlocks[succ->mBlockIdx]; bool forceRun = (entry.mGenerateCount == 0) && (!entry.mGenerateQueued); //if ((!mVRegInitializedContext.HasExtraBitsSet(succ->mPredVRegsInitialized, vregsInitialized)) && (!forceRun)) //continue; auto newPredVRegsInitialized = mVRegInitializedContext.Merge(succ->mPredVRegsInitialized, vregsInitialized); if ((newPredVRegsInitialized == succ->mPredVRegsInitialized) && (!forceRun)) continue; int vregIdx = -1; /*SizedArray newNodes; auto& prevDestEntry = succ->mPredVRegsInitialized; auto& mergeFrom = vregsInitialized.mBits; // Take nodes that were exclusive to the new set and add edges to nodes that were exclusive the old set while (true) { vregIdx = mVRegInitializedContext.GetNextDiffSetIdx(prevDestEntry.mBits, mergeFrom, vregIdx); if (vregIdx == -1) break; newNodes.push_back(vregIdx); } BF_ASSERT((!newNodes.empty()) || (forceRun));*/ /*if ((mDebugging) || (mBeFunction->mName == "?DrawEntry@DrawContext@PerfView@BeefPerf@@QEAAXPEAVGraphics@gfx@Beefy@@PEAVTrackNodeEntry@23@MM@Z")) { String str; str += StrFormat("%s -> %s (Gen=%d) ", ToString(BeMCOperand::FromBlock(mcBlock)).c_str(), ToString(BeMCOperand::FromBlock(succ)).c_str(), genCtx->mBlocks[succ->mBlockIdx].mGenerateCount); for (auto newNode : newNodes) str += StrFormat(" %d", newNode); str += "\r\n"; OutputDebugStringA(str.c_str()); }*/ succ->mPredVRegsInitialized = newPredVRegsInitialized; //TODO: This was a bad comparison // if (succ->mBlockIdx > succ->mBlockIdx) // modifiedBlockAfter = true; // else // modifiedBlockBefore = true; // What does this do? if (succ->mBlockIdx > mcBlock->mBlockIdx) modifiedBlockAfter = true; else modifiedBlockBefore = true; entry.mGenerateQueued = true; } } void BeMCContext::FixVRegInitFlags(BeMCInst* inst, const BeMCOperand& operand) { if (operand.IsVReg()) { SizedArray addVec = { operand.mVRegIdx }; inst->mVRegsInitialized = mVRegInitializedContext.Add(inst->mVRegsInitialized, addVec, true); } } void BeMCContext::SetVTrackingValue(BeMCOperand& operand, BeVTrackingValue& vTrackingValue) { if (!operand.IsVRegAny()) return; vTrackingValue.mEntry->Set(operand.mVRegIdx); auto vregInfo = mVRegInfo[operand.mVRegIdx]; SetVTrackingValue(vregInfo->mRelTo, vTrackingValue); SetVTrackingValue(vregInfo->mRelOffset, vTrackingValue); } void BeMCContext::DoInitInjectionPass() { BP_ZONE("BeMCContext::DoInitInjectionPass"); for (auto mcBlock : mBlocks) { mActiveBlock = mcBlock; for (int instIdx = 0; instIdx < (int)mcBlock->mInstructions.size(); instIdx++) { auto inst = mcBlock->mInstructions[instIdx]; BfIRInitType pendingInitKind = BfIRInitType_NotNeeded; if (inst->mKind == BeMCInstKind_DbgDecl) { int vregIdx = inst->mArg0.mVRegIdx; auto vregInfo = mVRegInfo[vregIdx]; auto dbgVar = vregInfo->mDbgVariable; if (!mVRegInitializedContext.IsSet(inst->mVRegsInitialized, vregIdx)) { if ((dbgVar->mPendingInitType != BfIRInitType_NotNeeded) && (dbgVar->mPendingInitType != BfIRInitType_NotNeeded_AliveOnDecl)) { pendingInitKind = dbgVar->mPendingInitType; // We don't need dynLife anymore since we're explicitly writing this //vregInfo->mHasDynLife = false; //AllocInst(BeMCInstKind_Def, inst->mArg0, instIdx); } } } if (pendingInitKind != BfIRInitType_NotNeeded) { int vregIdx = inst->mArg0.mVRegIdx; auto dbgVar = mVRegInfo[vregIdx]->mDbgVariable; auto varType = mVRegInfo[vregIdx]->mType; auto initType = dbgVar->mPendingInitType; if (varType->IsFloat()) { BeMCOperand zeroVal; if (varType->mTypeCode == BeTypeCode_Float) zeroVal.mKind = BeMCOperandKind_Immediate_f32; else zeroVal.mKind = BeMCOperandKind_Immediate_f64; if (initType == BfIRInitType_Uninitialized) { if (varType->mTypeCode == BeTypeCode_Float) zeroVal.mImmF32 = NAN; else zeroVal.mImmF64 = NAN; } else { if (varType->mTypeCode == BeTypeCode_Float) zeroVal.mImmF32 = 0; else zeroVal.mImmF64 = 0; } AllocInst(BeMCInstKind_Mov, BeMCOperand::FromVReg(vregIdx), zeroVal, instIdx + 1); } else if (varType->IsNonVectorComposite()) { SetAndRestoreValue insertPtr(mInsertInstIdxRef, &instIdx); instIdx++; uint8 val = (initType == BfIRInitType_Uninitialized) ? 0xCC : 0; CreateMemSet(BeMCOperand::FromVRegAddr(vregIdx), val, varType->mSize, varType->mAlign); instIdx--; } else { int64 val = (initType == BfIRInitType_Uninitialized) ? (int64)0xCCCCCCCCCCCCCCCCLL : 0; AllocInst(BeMCInstKind_Mov, BeMCOperand::FromVReg(vregIdx), BeMCOperand::FromImmediate(val), instIdx + 1); } dbgVar->mPendingInitType = BfIRInitType_NotNeeded; } } mActiveBlock = NULL; } SetCurrentInst(NULL); } void BeMCContext::ReplaceVRegsInit(BeMCBlock* mcBlock, int startInstIdx, BeVTrackingList* prevList, BeVTrackingList* newList) { for (int instIdx = startInstIdx; instIdx < (int)mcBlock->mInstructions.size(); instIdx++) { auto inst = mcBlock->mInstructions[instIdx]; if (inst->mVRegsInitialized != prevList) return; inst->mVRegsInitialized = newList; } } // This pass does bottom-up initialization for "simple" vregs (ie: not variables) void BeMCContext::SimpleInitializedPass() { } void BeMCContext::GenerateVRegInitFlags(BeVTrackingGenContext& genCtx) { BP_ZONE("BeMCContext::GenerateVRegInitFlags"); mVRegInitializedContext.Clear(); mVRegInitializedContext.Init((int)mVRegInfo.size()); auto emptyList = mVRegInitializedContext.AllocEmptyList(); genCtx.mEmptyList = emptyList; genCtx.mBlocks.Resize(mBlocks.size()); for (int blockIdx = 0; blockIdx < (int)mBlocks.size(); blockIdx++) { auto mcBlock = mBlocks[blockIdx]; mcBlock->mPredVRegsInitialized = emptyList; } SimpleInitializedPass(); bool modifiedBlockBefore; bool modifiedBlockAfter; InitializedPassHelper(mBlocks[0], &genCtx, modifiedBlockBefore, modifiedBlockAfter); while (true) { bool didWork = false; // Handle any outstanding pred entries for (int blockIdx = 0; blockIdx < (int)mBlocks.size(); blockIdx++) { auto& entry = genCtx.mBlocks[blockIdx]; if (entry.mGenerateQueued) { entry.mGenerateQueued = false; didWork = true; auto block = mBlocks[blockIdx]; bool modifiedBlockBefore; bool modifiedBlockAfter; InitializedPassHelper(block, &genCtx, modifiedBlockBefore, modifiedBlockAfter); if (modifiedBlockBefore) break; } } if (!didWork) break; } // Fix up the vregsInit changes to represent changes from the last instruction in one block to the first instruction on the next block for (int blockIdx = 0; blockIdx < (int)mBlocks.size() - 1; blockIdx++) { auto fromBlock = mBlocks[blockIdx]; auto toBlock = mBlocks[blockIdx + 1]; if ((fromBlock->mInstructions.empty()) || (toBlock->mInstructions.empty())) continue; BeMCInst* fromInst = NULL; // Last inst may not have vregsInit set for (int instIdx = (int)fromBlock->mInstructions.size() - 1; instIdx >= 0; instIdx--) { fromInst = fromBlock->mInstructions[instIdx]; if (fromInst->mVRegsInitialized != NULL) break; } auto toInst = toBlock->mInstructions.front(); if ((fromInst == NULL) || (fromInst->mVRegsInitialized == NULL) || (toInst == NULL) || (toInst->mVRegsInitialized == NULL)) continue; auto prevVRegsInit = toInst->mVRegsInitialized; auto newVRegsInit = mVRegInitializedContext.SetChanges(toInst->mVRegsInitialized, fromInst->mVRegsInitialized);; for (int instIdx = 0; instIdx < (int)toBlock->mInstructions.size(); instIdx++) { auto inst = toBlock->mInstructions[instIdx]; if (inst->mVRegsInitialized != prevVRegsInit) break; inst->mVRegsInitialized = newVRegsInit; } } int instCount = 0; for (auto block : mBlocks) instCount += (int)block->mInstructions.size(); BpEvent("GenerateVRegInitFlags Results", StrFormat("Blocks: %d\nInstructions: %d\nVRegs: %d\nCalls: %d\nHandled Calls: %d\nProcessed Instructions: %d\nVRegInit Bytes: %d\nTemp Bytes: %d", (int)mBlocks.size(), instCount, (int)mVRegInfo.size(), genCtx.mCalls, genCtx.mHandledCalls, genCtx.mInstructions, mVRegInitializedContext.mAlloc.GetAllocSize(), genCtx.mAlloc.GetAllocSize()).c_str()); } bool BeMCContext::DoInitializedPass() { BP_ZONE("BeMCContext::DoInitializedPass"); BeVTrackingGenContext genCtx; GenerateVRegInitFlags(genCtx); // Search blocks for instances of ambiguous initialization for (int blockIdx = 0; blockIdx < (int)mBlocks.size(); blockIdx++) { auto mcBlock = mBlocks[blockIdx]; // If mPredVRegsInitialized is NULL, this blocks is unreachable. It could still be in some other block's preds so we don't // completely erase it, just empty it if (genCtx.mBlocks[mcBlock->mBlockIdx].mGenerateCount == 0) { // Unused block - clear almost all instructions int newIdx = 0; // for (int instIdx = 0; instIdx < (int)mcBlock->mInstructions.size(); instIdx++) // { // auto inst = mcBlock->mInstructions[instIdx]; // if (inst->mKind == BeMCInstKind_ValueScopeHardEnd) // { // mcBlock->mInstructions[newIdx++] = inst; // } // } mcBlock->mInstructions.mSize = newIdx; continue; } for (int vregIdx : *mcBlock->mPredVRegsInitialized) { if (vregIdx >= mVRegInitializedContext.mNumItems) continue; if (mVRegInitializedContext.IsSet(mcBlock->mPredVRegsInitialized, vregIdx, BeTrackKind_Uninitialized)) { // If a register has both the "initialized" and "uninitialized" flags set, that means that there will be debugging range // where the variable could show an uninitialized value. We zero out the value at the def position to make // variable viewing less confusing. This state does not affect actual program execution because we've already statically // checked against usage of unassigned variables -- unless overridden with a "?" uninitialized expression, in which case // one could argue that 0xCC would be a more useful value, but we go with the argument for the "less confusing" zero in this case. auto vregInfo = mVRegInfo[vregIdx]; vregInfo->mHasAmbiguousInitialization = true; } } } bool needsReRun = false; bool hasInits = false; for (int vregIdx = 0; vregIdx < (int)mVRegInfo.size(); vregIdx++) { auto vregInfo = mVRegInfo[vregIdx]; if (vregInfo->mDbgVariable != NULL) { auto initType = vregInfo->mDbgVariable->mInitType; if (initType == BfIRInitType_NotSet) { if (vregInfo->mHasAmbiguousInitialization) initType = BfIRInitType_Zero; else initType = BfIRInitType_NotNeeded; } else if ((initType == BfIRInitType_Uninitialized) && (!vregInfo->mHasAmbiguousInitialization)) { // If we have an explicitly-uninitialized variable but it doesn't have ambiguous initialization // then we don't need to do the 0xCC, we can control visibility in the debugger through scope initType = BfIRInitType_NotNeeded; } if (vregInfo->mDbgVariable->mPendingInitType == initType) continue; if ((initType != BfIRInitType_NotNeeded) || (vregInfo->mDbgVariable->mPendingInitType == BfIRInitType_NotSet)) { vregInfo->mDbgVariable->mPendingInitType = initType; } if ((initType != BfIRInitType_NotNeeded) && (initType != BfIRInitType_NotNeeded_AliveOnDecl)) { vregInfo->mDbgVariable->mPendingInitDef = true; BF_ASSERT(vregInfo->mHasDynLife); vregInfo->mDoConservativeLife = true; hasInits = true; } } } if (hasInits) { DoInitInjectionPass(); // We need to re-execute the init flag generation BeVTrackingGenContext genCtx; GenerateVRegInitFlags(genCtx); } return !needsReRun; } void BeMCContext::DoTLSSetup() { // Perform at the end of the 'entry' block if (mTLSVRegIdx == -1) return; auto mcBlock = mBlocks[0]; mActiveBlock = mcBlock; for (int instIdx = 0; instIdx < (int)mcBlock->mInstructions.size(); instIdx++) { auto inst = mcBlock->mInstructions[instIdx]; // Skip past param setup if (inst->mKind == BeMCInstKind_Def) continue; if ((inst->mKind == BeMCInstKind_Mov) && (inst->mArg1.IsNativeReg())) continue; auto int32Type = mModule->mContext->GetPrimitiveType(BeTypeCode_Int32); auto intType = mModule->mContext->GetPrimitiveType(BeTypeCode_Int64); auto intPtrType = mModule->mContext->GetPointerTo(intType); auto mcTlsIndex = AllocVirtualReg(intType); CreateDefineVReg(mcTlsIndex, instIdx++); auto sym = mCOFFObject->GetSymbolRef("_tls_index"); if (sym->mType == NULL) sym->mType = int32Type; AllocInst(BeMCInstKind_Mov, mcTlsIndex, BeMCOperand::FromSymbol(sym->mIdx), instIdx++); AllocInst(BeMCInstKind_Def, BeMCOperand::FromVReg(mTLSVRegIdx), instIdx++); AllocInst(BeMCInstKind_TLSSetup, BeMCOperand::FromVReg(mTLSVRegIdx), BeMCOperand::FromImmediate(0), instIdx++); auto mcTlsVal = AllocVirtualReg(intPtrType); CreateDefineVReg(mcTlsVal, instIdx++); auto tlsValInfo = GetVRegInfo(mcTlsVal); tlsValInfo->mIsExpr = true; tlsValInfo->mRelTo = BeMCOperand::FromVReg(mTLSVRegIdx); tlsValInfo->mRelOffset = mcTlsIndex; tlsValInfo->mRelOffsetScale = 8; auto mcLoadVal = mcTlsVal; mcLoadVal.mKind = BeMCOperandKind_VRegLoad; AllocInst(BeMCInstKind_Mov, BeMCOperand::FromVReg(mTLSVRegIdx), mcLoadVal, instIdx++); break; } mActiveBlock = NULL; } void BeMCContext::DoChainedBlockMerge() { for (int blockIdx = 0; blockIdx < mBlocks.size() - 1; blockIdx++) { auto mcBlock = mBlocks[blockIdx]; auto nextMcBlock = mBlocks[blockIdx + 1]; // We only branch into one block, and the the next block only has current block as a predecessor? if ((mcBlock->mSuccs.size() == 1) && (nextMcBlock->mPreds.size() == 1) && (nextMcBlock->mPreds[0] == mcBlock)) { auto lastInst = mcBlock->mInstructions.back(); // Last instruction of current block is an unconditional branch to the next block if ((lastInst->mKind == BeMCInstKind_Br) && (lastInst->mArg0.mBlock == nextMcBlock)) { mcBlock->mSuccs.Remove(nextMcBlock); for (auto succ : nextMcBlock->mSuccs) { if (!mcBlock->mSuccs.Contains(succ)) { mcBlock->mSuccs.push_back(succ); succ->mPreds.push_back(mcBlock); } succ->mPreds.Remove(nextMcBlock); } mcBlock->mMaxDeclBlockId = nextMcBlock->mMaxDeclBlockId; mcBlock->mInstructions.pop_back(); for (auto inst : nextMcBlock->mInstructions) mcBlock->mInstructions.push_back(inst); mBlocks.RemoveAt(blockIdx + 1); blockIdx--; } } } for (int blockIdx = 0; blockIdx < mBlocks.size(); blockIdx++) mBlocks[blockIdx]->mBlockIdx = blockIdx; } void BeMCContext::DoSplitLargeBlocks() { Dictionary blockEndRemap; int splitSize = 4096; int maxBlockSize = splitSize + splitSize / 4; bool hadSplits = false; for (int blockIdx = 0; blockIdx < mBlocks.size(); blockIdx++) { int blockBreakIdx = 0; auto srcBlock = mBlocks[blockIdx]; if (srcBlock->mInstructions.size() < maxBlockSize) continue; hadSplits = true; int extensionCount = srcBlock->mInstructions.size() / splitSize; // Don't allow the last block to have too few instructions if (srcBlock->mInstructions.size() % splitSize < splitSize / 4) extensionCount--; if (extensionCount == 0) // Shouldn't happen, really continue; for (int extIdx = 0; extIdx < extensionCount; extIdx++) { BeMCBlock* extBlock = mMCBlockAlloc.Alloc(); extBlock->mName = srcBlock->mName + StrFormat("__EXT%d", extIdx); mBlocks.Insert(blockIdx + 1 + extIdx, extBlock); int startIdx = (extIdx + 1) * splitSize; int endIdx = BF_MIN((extIdx + 2) * splitSize, srcBlock->mInstructions.size()); if (extIdx == extensionCount - 1) endIdx = (int)srcBlock->mInstructions.size(); extBlock->mInstructions.Insert(0, &srcBlock->mInstructions[startIdx], endIdx - startIdx); extBlock->mPreds.Add(mBlocks[blockIdx + extIdx]); if (extIdx > 0) { auto prevBlock = mBlocks[blockIdx + extIdx]; mActiveBlock = prevBlock; AllocInst(BeMCInstKind_Br, BeMCOperand::FromBlock(extBlock)); mActiveBlock = NULL; mBlocks[blockIdx + extIdx]->mSuccs.Add(extBlock); } if (extIdx == extensionCount - 1) { blockEndRemap[blockIdx] = blockIdx + extIdx + 1; for (auto succ : srcBlock->mSuccs) { succ->mPreds.Remove(srcBlock); succ->mPreds.Add(extBlock); } extBlock->mSuccs = srcBlock->mSuccs; srcBlock->mSuccs.Clear(); srcBlock->mSuccs.Add(mBlocks[blockIdx + 1]); } } mActiveBlock = srcBlock; srcBlock->mInstructions.RemoveRange(splitSize, (int)srcBlock->mInstructions.size() - splitSize); AllocInst(BeMCInstKind_Br, BeMCOperand::FromBlock(mBlocks[blockIdx + 1])); mActiveBlock = NULL; } if (!hadSplits) return; // for (int blockIdx = 0; blockIdx < mBlocks.size(); blockIdx++) // { // auto mcBlock = mBlocks[blockIdx]; // for (auto inst : mcBlock->mInstructions) // { // if (inst->mResult.mKind == BeMCOperandKind_Phi) // { // inst->mResult.mP // } // } // } for (int blockIdx = 0; blockIdx < mBlocks.size(); blockIdx++) mBlocks[blockIdx]->mBlockIdx = blockIdx; for (auto phi : mPhiAlloc) { for (auto& phiValue : phi->mValues) { int remappedBlock = -1; if (blockEndRemap.TryGetValue(phiValue.mBlockFrom->mBlockIdx, &remappedBlock)) { phiValue.mBlockFrom = mBlocks[remappedBlock]; } } } } void BeMCContext::DetectLoops() { BP_ZONE("BeMCContext::DetectLoops"); BeMCLoopDetector loopDetector(this); loopDetector.DetectLoops(); } void BeMCContext::DoLastUsePassHelper(BeMCInst* inst, const BeMCOperand& operand) { if (operand.mKind == BeMCOperandKind_VRegPair) { auto mcOperand = BeMCOperand::FromEncoded(operand.mVRegPair.mVRegIdx0); if (mcOperand.IsVRegAny()) DoLastUsePassHelper(inst, mcOperand); mcOperand = BeMCOperand::FromEncoded(operand.mVRegPair.mVRegIdx1); if (mcOperand.IsVRegAny()) DoLastUsePassHelper(inst, mcOperand); return; } if (!operand.IsVRegAny()) return; int vregIdx = operand.mVRegIdx; auto vregInfo = mVRegInfo[vregIdx]; if ((vregInfo->mDbgVariable == NULL) && (!vregInfo->mHasDynLife) && (!vregInfo->mFoundLastUse)) { vregInfo->mFoundLastUse = true; auto lastUseRecord = mAlloc.Alloc(); lastUseRecord->mVRegIdx = vregIdx; lastUseRecord->mNext = inst->mVRegLastUseRecord; inst->mVRegLastUseRecord = lastUseRecord; } if (vregInfo->mRelTo) DoLastUsePassHelper(inst, vregInfo->mRelTo); if (vregInfo->mRelOffset) DoLastUsePassHelper(inst, vregInfo->mRelOffset); } void BeMCContext::DoLastUsePass() { for (int blockIdx = (int)mBlocks.size() - 1; blockIdx >= 0; blockIdx--) { auto mcBlock = mBlocks[blockIdx]; for (int instIdx = (int)mcBlock->mInstructions.size() - 1; instIdx >= 0; instIdx--) { auto inst = mcBlock->mInstructions[instIdx]; auto operands = { &inst->mResult, &inst->mArg0, &inst->mArg1 }; for (auto operand : operands) { DoLastUsePassHelper(inst, *operand); } } } } void BeMCContext::RefreshRefCounts() { for (auto vregInfo : mVRegInfo) { if (vregInfo->mMustExist) vregInfo->mRefCount = 1; else vregInfo->mRefCount = 0; vregInfo->mAssignCount = 0; } for (auto mcBlock : mBlocks) { for (auto& inst : mcBlock->mInstructions) { if (inst->IsDef()) continue; auto operands = { inst->mResult, inst->mArg0, inst->mArg1 }; for (auto op : operands) { if (op.IsVRegAny()) mVRegInfo[op.mVRegIdx]->mRefCount++; if (op.mKind == BeMCOperandKind_VRegPair) { auto vreg0 = BeMCOperand::FromEncoded(op.mVRegPair.mVRegIdx0); if (vreg0.IsVRegAny()) { mVRegInfo[vreg0.mVRegIdx]->mRefCount++; mVRegInfo[vreg0.mVRegIdx]->mAssignCount++; } auto vreg1 = BeMCOperand::FromEncoded(op.mVRegPair.mVRegIdx1); if (vreg1.IsVRegAny()) mVRegInfo[vreg1.mVRegIdx]->mRefCount++; } } if (inst->IsAssignment()) { if (inst->mArg0.IsVRegAny()) mVRegInfo[inst->mArg0.mVRegIdx]->mAssignCount++; } if (inst->mResult) { if (inst->mResult.IsVRegAny()) mVRegInfo[inst->mResult.mVRegIdx]->mAssignCount++; } } } for (int vregIdx = 0; vregIdx < (int)mVRegInfo.size(); vregIdx++) { auto vregInfo = mVRegInfo[vregIdx]; if (vregInfo->mRefCount > 0) AddRelRefs(BeMCOperand::FromVReg(vregIdx), 0); } } bool BeMCContext::CheckVRegEqualityRange(BeMCBlock* mcBlock, int instIdx, const BeMCOperand& vreg0, const BeMCOperand& vreg1, BeMCRemapper& regRemaps, bool onlyCheckFirstLifetime) { // The algorithm here is that we check to see if either of these registers are ever written to // up to the point that one of them dies. If not, we can map them to each other bool foundSplitPoint = false; bool hadPointerDeref = false; for (int checkInstIdx = instIdx + 1; checkInstIdx < (int)mcBlock->mInstructions.size(); checkInstIdx++) { auto checkInst = mcBlock->mInstructions[checkInstIdx]; if ((!IsLive(checkInst->mLiveness, vreg0.mVRegIdx, regRemaps)) || ((!onlyCheckFirstLifetime) && (!IsLive(checkInst->mLiveness, vreg1.mVRegIdx, regRemaps)))) { return true; } if ((checkInst->mKind == BeMCInstKind_LifetimeEnd) && ((checkInst->mArg0.mVRegIdx == vreg0.mVRegIdx) || (checkInst->mArg0.mVRegIdx == vreg1.mVRegIdx))) { if (checkInstIdx == (int)mcBlock->mInstructions.size() - 1) { // There are cases where this might be the last instruction in a block, so we won't see the mLiveness change // so this catches that case return true; } // We have cases like when have a "defer delete val;", val will get used by the dtor after the LifetimeEnd return false; } if (hadPointerDeref) { // Pointer deref is okay as long as liveness ends one instruction afterwards return false; } // Anything can happen during a call... if (checkInst->mKind == BeMCInstKind_Call) return false; auto mcDest = checkInst->GetDest(); if ((mcDest != NULL) && (mcDest->mKind == BeMCOperandKind_VReg)) { // Any assignment to either of these regs disqualifies them if ((mcDest->mVRegIdx == vreg0.mVRegIdx) || (mcDest->mVRegIdx == vreg1.mVRegIdx)) return false; // Any writing into memory could be aliased, so we don't replace across that boundary if (HasPointerDeref(*mcDest)) { // Delay the 'return false' until we determine if our liveness ends on the next instruction. // If it does, then this is okay because it didn't affect the source of any operations hadPointerDeref = true; } } } return false; } void BeMCContext::DoInstCombinePass() { BP_ZONE("BeMCContext::DoInstCombinePass"); BeMCRemapper regRemaps; regRemaps.Init((int)mVRegInfo.size()); BeVTrackingValue vTrackingVal((int)mVRegInfo.size()); HashSet wantUnwrapVRegs; for (auto mcBlock : mBlocks) { BeDbgLoc* curDbgLoc = NULL; int safeMergeLocStart = 0; bool lastDefDidMerge = false; for (int instIdx = 0; instIdx < (int)mcBlock->mInstructions.size(); instIdx++) { bool reprocessInst = false; auto inst = mcBlock->mInstructions[instIdx]; BeMCInst* prevInst = NULL; if (instIdx > 0) prevInst = mcBlock->mInstructions[instIdx - 1]; if ((inst->mDbgLoc != curDbgLoc) && (inst->mDbgLoc != NULL)) { safeMergeLocStart = instIdx; curDbgLoc = inst->mDbgLoc; } if ((inst->mKind == BeMCInstKind_Call) || (inst->mKind == BeMCInstKind_MemCpy) || (inst->mKind == BeMCInstKind_MemSet)) { // Don't merge anything across callsites or memory writes safeMergeLocStart = instIdx; } auto instDest = inst->GetDest(); if ((instDest != NULL) && (HasPointerDeref(*instDest))) { // Memory aliasing may invalidate any accesses, don't merge expressions across here safeMergeLocStart = instIdx + 1; } SetCurrentInst(inst); //TODO: Remove // if we've remapped other vregs onto this that aren't dead, then we can't end the value lifetime but we // must end the lifetime of the visible debug variable /*if (inst->mKind == BeMCInstKind_LifetimeEnd) { bool extendValue = false; int vregIdx = inst->mArg0.mVRegIdx; int checkIdx = regRemaps.GetHead(vregIdx); while (checkIdx != -1) { if ((checkIdx != vregIdx) && (mLivenessContext.IsSet(inst->mLiveness, checkIdx))) extendValue = true; checkIdx = regRemaps.GetNext(checkIdx); } if (extendValue) { //Throw error } //inst->mKind = BeMCInstKind_LifetimeDbgEnd; }*/ // Handle movs into .addrs if (inst->mKind == BeMCInstKind_Mov) { auto vregInfoDest = GetVRegInfo(inst->mArg0); if (vregInfoDest != NULL) { auto vregInfoSrc = GetVRegInfo(inst->mArg1); if ((vregInfoSrc != NULL) && (vregInfoSrc->mForceMerge)) { vregInfoSrc->mForceMerge = false; if ((vregInfoDest->mDbgVariable != NULL) && (vregInfoSrc->CanEliminate())) { BF_ASSERT(!mVRegInitializedContext.IsSet(inst->mVRegsInitialized, inst->mArg0.mVRegIdx)); AddRegRemap(inst->mArg1.mVRegIdx, inst->mArg0.mVRegIdx, regRemaps); // Remove any LifetimeStarts since our source may have been alive before us //if (inst->mArg1.mVRegIdx < inst->mArg0.mVRegIdx) //removeLifetimeStarts.insert(inst->mArg0.mVRegIdx); } } } } // The following code only applies when we aren't on the last instruction int nextIdx = instIdx + 1; BeMCInst* nextInst = NULL; while (true) { if (nextIdx >= (int)mcBlock->mInstructions.size()) break; nextInst = mcBlock->mInstructions[nextIdx]; if (!nextInst->IsDef()) break; nextIdx++; } if (nextInst == NULL) continue; // The following code only applies when we have at least 2 more instructions // Find a non-Def instruction int nextNextIdx = nextIdx + 1; BeMCInst* nextNextInst = NULL; while (true) { if (nextNextIdx >= (int)mcBlock->mInstructions.size()) break; nextNextInst = mcBlock->mInstructions[nextNextIdx]; if (!nextNextInst->IsDef()) break; nextNextIdx++; } auto result = RemapOperand(inst->mResult, regRemaps); auto arg0 = RemapOperand(inst->mArg0, regRemaps); auto arg1 = RemapOperand(inst->mArg1, regRemaps); if (inst->IsDef()) { auto vregInfo = mVRegInfo[inst->mArg0.mVRegIdx]; if (vregInfo->mIsExpr) { for (int checkInstIdx = safeMergeLocStart; checkInstIdx < instIdx; checkInstIdx++) { auto checkInst = mcBlock->mInstructions[checkInstIdx]; if (checkInst->IsDef()) { auto checkArg0 = RemapOperand(checkInst->mArg0, regRemaps); auto checkVRegInfo = mVRegInfo[checkArg0.mVRegIdx]; if (checkVRegInfo->mIsExpr) { // Remap to use the earlier definition of this expression if (AreOperandsEquivalent(inst->mArg0, checkArg0, regRemaps)) { vTrackingVal.Clear(); SetVTrackingValue(checkArg0, vTrackingVal); // Check to make sure we didn't do any writes to any of the values used in the expression. This happens // during "iVal++", for example. bool hadBadWrite = false; for (int verifyInstIdx = safeMergeLocStart; verifyInstIdx < instIdx; verifyInstIdx++) { auto verifyInst = mcBlock->mInstructions[verifyInstIdx]; auto verifyDest = verifyInst->GetDest(); if ((verifyDest != NULL) && (verifyDest->IsVRegAny()) && (vTrackingVal.mEntry->IsSet(verifyDest->mVRegIdx))) { hadBadWrite = true; break; } } if (!hadBadWrite) { auto vregInfoFrom = GetVRegInfo(inst->mArg0); if (vregInfoFrom->mDbgVariable == NULL) AddRegRemap(inst->mArg0.mVRegIdx, checkArg0.mVRegIdx, regRemaps, true); } break; } } } } } } BeMCOperand origNextDestOperand; if (nextInst->IsAssignment()) origNextDestOperand = nextInst->mArg0; else if (nextInst->mResult) origNextDestOperand = nextInst->mResult; BeMCOperand nextDestOperand = RemapOperand(origNextDestOperand, regRemaps); if (inst->mKind == BeMCInstKind_DefLoad) { auto vregInfoDest = GetVRegInfo(inst->mArg0); auto mcLoaded = inst->mArg0; auto mcAddr = vregInfoDest->mRelTo; // We can only do a regRemap replace if we aren't the product of a pointer dereference bool mapToDef = false; if (mcAddr.mKind == BeMCOperandKind_VReg) { auto vregInfo = mVRegInfo[inst->mArg0.mVRegIdx]; // We do 'onlyCheckFirstLifetime' because we have cases like when have a "defer delete val;", because val will get used by the dtor after the LiftimeEnd if ((vregInfo->IsDirectRelTo()) && (vregInfo->mDbgVariable == NULL) && (CheckVRegEqualityRange(mcBlock, instIdx, mcLoaded, mcAddr, regRemaps, true))) { // If the source value doesn't change in the lifetime of the loaded value then // we can just map directly to the source - no copy needed. inst->mKind = BeMCInstKind_Def; AddRegRemap(inst->mArg0.mVRegIdx, vregInfoDest->mRelTo.mVRegIdx, regRemaps, true); continue; } } else if (mcAddr.mKind == BeMCOperandKind_SymbolAddr) { // No actual load needed - just keep it as a Direct reference inst->mKind = BeMCInstKind_Def; //wantUnwrapVRegs.Add(inst->mArg0.mVRegIdx); continue; } else if (mcAddr.mKind == BeMCOperandKind_Symbol) { //TODO: We used to have this as a 'inst->mKind = BeMCInstKind_Def;' case also, but that messed up post-increment (ie: gVal++) values continue; } bool hadPointerDeref = false; for (int checkInstIdx = instIdx + 1; checkInstIdx < (int)mcBlock->mInstructions.size(); checkInstIdx++) { auto checkInst = mcBlock->mInstructions[checkInstIdx]; BF_ASSERT(mcLoaded.IsVRegAny()); if (!IsLive(checkInst->mLiveness, mcLoaded.mVRegIdx, regRemaps)) { break; } if ((mcAddr.IsVRegAny()) && (!IsLive(checkInst->mLiveness, mcAddr.mVRegIdx, regRemaps))) break; if (hadPointerDeref) { // Pointer deref is okay as long as liveness ends one instruction afterwards break; } auto mcDest = checkInst->GetDest(); if ((mcDest != NULL) && ((mcDest->mKind == BeMCOperandKind_VReg) || (mcDest->mKind == mcAddr.mKind))) { // Any assignment to either of these regs ends our remapping loop if ((mcDest->mVRegIdx == mcLoaded.mVRegIdx) || (mcDest->mVRegIdx == mcAddr.mVRegIdx)) { break; } // Any writing into memory could be aliased, so we don't replace across that boundary if (HasPointerDeref(*mcDest)) { // Delay the 'return false' until we determine if our liveness ends on the next instruction. // If it does, then this is okay because it didn't affect the source of any operations hadPointerDeref = true; } } if (checkInst->IsInformational()) continue; if (checkInst->mKind == BeMCInstKind_Call) // Anything can happen during a call break; if (checkInst->mDbgLoc != inst->mDbgLoc) break; auto operands = { &checkInst->mArg0, &checkInst->mArg1 }; for (auto& operand : operands) { // If we reference the loaded version during the range where the value hasn't changed, // then just refer back to the original value. This is useful in cases like i++, // where we want to be able to still do an INC on the source variable rather than // "loadedI = i; i = loadedI + 1; ... use loadedI ..." if ((operand->mKind == BeMCOperandKind_VReg) && (operand->mVRegIdx == mcLoaded.mVRegIdx)) { *operand = mcAddr; } if ((operand->mKind == BeMCOperandKind_VRegLoad) && (operand->mVRegIdx == mcLoaded.mVRegIdx)) { if (mcAddr.mKind == BeMCOperandKind_VReg) { operand->mVRegIdx = mcAddr.mVRegIdx; } } } } } // Check for the special case of a single move to a SRET dereference - if so, then make the source composite be the source value if ((inst->mKind == BeMCInstKind_Mov) && (inst->mArg0.mKind == BeMCOperandKind_VRegLoad) && (inst->mArg1.mKind == BeMCOperandKind_VReg)) { auto vregInfoDest = GetVRegInfo(inst->mArg0); auto vregInfoSrc = GetVRegInfo(inst->mArg1); if ((vregInfoDest->IsDirectRelTo()) && (vregInfoSrc->IsDirectRelTo())) { auto destRelTo = GetVRegInfo(vregInfoDest->mRelTo); auto srcRelTo = GetVRegInfo(vregInfoSrc->mRelTo); if ((destRelTo->mIsRetVal) && (vregInfoDest->mAssignCount == 1) && (!destRelTo->mIsExpr) && (!srcRelTo->mIsExpr)) { mCompositeRetVRegIdx = vregInfoDest->mRelTo.mVRegIdx; RemoveInst(mcBlock, instIdx); srcRelTo->SetRetVal(); instIdx--; continue; } } } // For the form: // @Def %vreg0 // [%result] = %vreg0, %vreg1 // Remap %vreg0 to %vreg1 if this is the only assignment, and thus an immutable definition, and vreg1 doesn't change if ((nextDestOperand) && (((inst->mKind == BeMCInstKind_Def) && (arg0 == nextDestOperand)) || ((inst->mKind == BeMCInstKind_LifetimeStart) && (BeMCOperand::ToLoad(arg0) == nextDestOperand)))) { lastDefDidMerge = false; auto vregInfoDest = GetVRegInfo(nextDestOperand); auto checkOperands = { &nextInst->mArg0, &nextInst->mArg1 }; bool checkArg1 = true; if (nextInst->mResult) { if ((nextInst->mKind == BeMCInstKind_Add) || (nextInst->mKind == BeMCInstKind_Mul)) { // These are commutative, so we can remap to either the LHS or RHS checkArg1 = true; } else { // We can only remap to LHS checkArg1 = false; } } for (int argCheckIdx = (nextInst->mResult ? 0 : 1); argCheckIdx < (checkArg1 ? 2 : 1); argCheckIdx++) { BeMCOperand origCheckOperand; if (argCheckIdx == 0) origCheckOperand = nextInst->mArg0; else origCheckOperand = nextInst->mArg1; auto checkOperand = RemapOperand(origCheckOperand, regRemaps); if (checkOperand.mKind != BeMCOperandKind_VReg) // Loads/Addrs not applicable continue; auto vregInfoCheck = GetVRegInfo(checkOperand); if (vregInfoCheck == NULL) continue; if ((vregInfoDest->mIsExpr) || (vregInfoCheck->mIsExpr)) continue; if ((vregInfoCheck->mForceMem) && (!vregInfoDest->mForceMem)) continue; if ((!vregInfoDest->mForceMerge) && (!vregInfoDest->mIsRetVal)) { // If we merge to a vreg that has a pointer ref, that could be a deoptimization by not allowing // either to bind to a register if (vregInfoDest->mForceMem != vregInfoCheck->mForceMem) { if (!GetType(checkOperand)->IsNonVectorComposite()) continue; } } // We have a special retVal case where we can allow the __returnVal to share an address with the // actual 'ret' param. This is most useful when returning structs by value, to avoid a copy. // The only downside is that editing the return value in the debugger will also show the value // of the ret param changing, which is normally not acceptable but in this case we allow it. bool allowCoexist = ((vregInfoDest->mIsRetVal) || (vregInfoCheck->CanEliminate()) || (vregInfoDest->CanEliminate())); // Only vregs of the same types can be merged if (!AreTypesEquivalent(vregInfoCheck->mType, vregInfoDest->mType)) continue; bool canMerge = false; if ((nextInst->IsMov()) && (allowCoexist)) { if ((vregInfoCheck->mForceMerge) || (vregInfoCheck->mIsRetVal)) canMerge = true; // This is to allow the incoming vregs to combine with the ".addr" debug variables else // Always use orig operands for liveness checking canMerge = CheckVRegEqualityRange(mcBlock, nextIdx, origCheckOperand, origNextDestOperand, regRemaps); } else { // If we're dealing with a non-Mov instruction, then the values of both of these vregs will // be different after the instruction, so we can only map them if the incoming vreg will be dead // afterwards. If both are dbgVariables then we can't allow them to coeexist because the user // could edit one of the values in the debugger and we don't want the other one changing. if ((nextNextInst != NULL) && (!IsLive(nextNextInst->mLiveness, origCheckOperand.mVRegIdx, regRemaps)) && (!IsLive(nextNextInst->mLiveness, checkOperand.mVRegIdx, regRemaps))) canMerge = true; } if (canMerge) { if (vregInfoCheck->mForceMerge) vregInfoCheck->mForceMerge = false; // Only allow one merge lastDefDidMerge = true; if (vregInfoDest->mIsRetVal) { // We don't do a remap for this return value optimization, because we want both vregs // to still show in the debugger - even though they will point to the same address // now (in the case of a struct return) vregInfoCheck->SetRetVal(); //BF_ASSERT(mCompositeRetVRegIdx != -1); //vregInfoCheck->mRelTo = BeMCOperand::FromVReg(mCompositeRetVRegIdx); break; } if ((vregInfoCheck->mDbgVariable != NULL) && (vregInfoDest->mDbgVariable != NULL)) { // We want separate vreg info so we can track the mDbgVariable separately, but we do want // the same location vregInfoDest->mIsExpr = true; vregInfoDest->mRelTo.mKind = BeMCOperandKind_VReg; vregInfoDest->mRelTo.mVRegIdx = checkOperand.mVRegIdx; } else { // Replace %vreg0 with %vreg1? if (vregInfoDest->CanEliminate()) { bool canReplace = true; if (canReplace) { AddRegRemap(nextDestOperand.mVRegIdx, checkOperand.mVRegIdx, regRemaps); break; } } // Or go the other way, and make %vreg1 use our variable directly since we're just handing off anyway if (vregInfoCheck->CanEliminate()) { if (vregInfoCheck->mIsRetVal) { vregInfoDest->SetRetVal(); //BF_ASSERT(mCompositeRetVRegIdx != -1); //vregInfoCheck->mRelTo = BeMCOperand::FromVReg(mCompositeRetVRegIdx); } AddRegRemap(checkOperand.mVRegIdx, nextDestOperand.mVRegIdx, regRemaps); if (vregInfoDest->mNaturalReg == X64Reg_None) vregInfoDest->mNaturalReg = vregInfoCheck->mNaturalReg; break; } } } } } //if (mDebugging) { // For the form // Def %vreg0 // Mov %vreg0, %vreg1 // , %vreg0 // Where vreg0 has no other references, convert to // , &vreg1 if ((inst->mKind == BeMCInstKind_Def) && (nextInst->mKind == BeMCInstKind_Mov) && (nextNextInst != NULL)) { auto vregInfo = GetVRegInfo(inst->mArg0); if ((vregInfo->mRefCount == 2) && (inst->mArg0 == nextInst->mArg0) && (!nextInst->mArg1.IsNativeReg()) && (nextNextInst->mArg0 != nextInst->mArg0) && (nextNextInst->mArg1 == nextInst->mArg0) && (AreTypesEquivalent(GetType(inst->mArg0), GetType(nextInst->mArg1)))) { nextNextInst->mArg1 = nextInst->mArg1; RemoveInst(mcBlock, instIdx); RemoveInst(mcBlock, instIdx); instIdx--; continue; } } } // For the form // %vreg0, %vreg1 // Test %vreg0, 1 // Remove test, because the BinOp will already set the correct flags if ((nextInst->mKind == BeMCInstKind_Test) && (nextInst->mArg1.IsImmediateInt()) && (nextInst->mArg1.mImmediate == 1) && (nextInst->mArg0 == inst->mArg0)) { if ((inst->mKind == BeMCInstKind_Xor) || (inst->mKind == BeMCInstKind_Or) || (inst->mKind == BeMCInstKind_And)) { BF_ASSERT(!inst->mResult); RemoveInst(mcBlock, instIdx + 1); instIdx--; continue; } } if (inst->mResult) { auto nextArg0 = RemapOperand(nextInst->mArg0, regRemaps); auto nextArg1 = RemapOperand(nextInst->mArg1, regRemaps); // For the form: // %vreg2 = %vreg0, %vreg1 // Mov %vreg3, %vreg2 // If %vreg2 has no other references, combine into // %vreg3 = %vreg0, %vreg1 if ((nextInst->IsMov()) && (result == nextArg1)) { // We purposely check inst->mResult here rather than 'result', because that ref count is our // indication of whether the result was actually used again auto vregInfo = GetVRegInfo(inst->mResult); if ((vregInfo != NULL) && (vregInfo->mRefCount == 2) && (vregInfo->CanEliminate())) { // Make sure the MOV isn't a conversion auto movArg0Type = GetType(nextInst->mArg0); auto movArg1Type = GetType(nextInst->mArg1); if (AreTypesEquivalent(movArg0Type, movArg1Type)) { // We actually eliminate the current inst incase there was a Def between the two, // otherwise the Def would occur AFTER the assignment which would be bad nextInst->mResult = nextInst->mArg0; nextInst->mKind = inst->mKind; nextInst->mArg0 = inst->mArg0; nextInst->mArg1 = inst->mArg1; RemoveInst(mcBlock, instIdx); instIdx--; continue; } } } if (inst->IsCommutable()) { if ((nextInst->IsMov()) && (arg1 == nextArg0) && (result == nextArg1)) { break; } } // For the form: // %vreg2 = %vreg0, %vreg1 // If %vreg0 ends its life on this instruction, // Replace all instances of %vreg2 with %vreg0 if ((inst->mResult.mKind == BeMCOperandKind_VReg) && (inst->mArg0.mKind == BeMCOperandKind_VReg) && (inst->mResult != inst->mArg0)) { // Check liveness against both the orig arg0 and the remap //if ((!nextInst->mLiveness->IsSet(inst->mArg0.mVRegIdx)) && (!nextInst->mLiveness->IsSet(arg0.mVRegIdx))) if (!IsLive(nextInst->mLiveness, inst->mArg0.mVRegIdx, regRemaps)) { // Only do a full regRemap if this is the def initialization if ((prevInst != NULL) && (prevInst->mKind == BeMCInstKind_Def) && (prevInst->mArg0.mVRegIdx == inst->mResult.mVRegIdx)) { auto vregInfoResult = GetVRegInfo(result); auto vregInfo0 = GetVRegInfo(arg0); if ((vregInfoResult->CanEliminate()) && (!vregInfo0->mIsExpr)) { AddRegRemap(inst->mResult.mVRegIdx, arg0.mVRegIdx, regRemaps); } } } } } if (inst->mKind == BeMCInstKind_RestoreVolatiles) { if (mcBlock == mBlocks.back()) { bool hadInvalidInst = false; for (int checkInstIdx = instIdx + 1; checkInstIdx < (int)mcBlock->mInstructions.size(); checkInstIdx++) { auto checkInst = mcBlock->mInstructions[checkInstIdx]; if ((checkInst->mKind != BeMCInstKind_DbgDecl) && (checkInst->mKind != BeMCInstKind_EnsureInstructionAt) && (checkInst->mKind != BeMCInstKind_Ret)) { hadInvalidInst = true; break; } } if (!hadInvalidInst) { // We return after this, we don't need to restore volatiles inst->mArg1 = BeMCOperand::FromPreserveFlag(BeMCPreserveFlag_NoRestore); continue; } } } if (nextNextInst == NULL) { if (reprocessInst) instIdx--; continue; } // For the form // Xor &vreg0, 1 // CondBr