void CodeGen::genDoneAddressableFloat(GenTreePtr tree, regMaskTP addrRegInt, regMaskTP addrRegFlt, RegSet::KeepReg keptReg) { assert(!(addrRegInt && addrRegFlt)); if (addrRegInt) { return genDoneAddressable(tree, addrRegInt, keptReg); } else if (addrRegFlt) { if (keptReg == RegSet::KEEP_REG) { for (regNumber r = REG_FP_FIRST; r != REG_NA; r = regNextOfType(r, tree->TypeGet())) { regMaskTP mask = genRegMaskFloat(r, tree->TypeGet()); // some masks take up more than one bit if ((mask & addrRegFlt) == mask) { regSet.SetUsedRegFloat(tree, false); } } } } }
bool RegSet::IsLockedRegFloat(GenTreePtr tree) { /* The value must be sitting in a register */ assert(tree); assert(tree->gtFlags & GTF_REG_VAL); assert(varTypeIsFloating(tree->TypeGet())); regMaskTP regMask = genRegMaskFloat(tree->gtRegNum, tree->TypeGet()); return (rsGetMaskLock() & regMask) == regMask; }
void CodeGen::genKeepAddressableFloat(GenTreePtr tree, regMaskTP * regMaskIntPtr, regMaskTP * regMaskFltPtr) { regMaskTP regMaskInt, regMaskFlt; regMaskInt = *regMaskIntPtr; regMaskFlt = *regMaskFltPtr; *regMaskIntPtr = *regMaskFltPtr = 0; switch (tree->OperGet()) { case GT_REG_VAR: // If register has been spilled, unspill it if (tree->gtFlags & GTF_SPILLED) { UnspillFloat(&compiler->lvaTable[tree->gtLclVarCommon.gtLclNum]); } break; case GT_CNS_DBL: if (tree->gtFlags & GTF_SPILLED) { UnspillFloat(tree); } *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet()); break; case GT_LCL_FLD: case GT_LCL_VAR: case GT_CLS_VAR: break; case GT_IND: if (regMaskFlt == RBM_NONE) { *regMaskIntPtr = genKeepAddressable(tree, regMaskInt, 0); *regMaskFltPtr = 0; return; } __fallthrough; default: *regMaskIntPtr = 0; if (tree->gtFlags & GTF_SPILLED) { UnspillFloat(tree); } *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet()); break; } }
//------------------------------------------------------------------------ // DecomposeInd: Decompose GT_IND. // // Arguments: // use - the LIR::Use object for the def that needs to be decomposed. // // Return Value: // The next node to process. // GenTree* DecomposeLongs::DecomposeInd(LIR::Use& use) { GenTree* indLow = use.Def(); LIR::Use address(Range(), &indLow->gtOp.gtOp1, indLow); address.ReplaceWithLclVar(m_compiler, m_blockWeight); JITDUMP("[DecomposeInd]: Saving addr tree to a temp var:\n"); DISPTREERANGE(Range(), address.Def()); // Change the type of lower ind. indLow->gtType = TYP_INT; // Create tree of ind(addr+4) GenTreePtr addrBase = indLow->gtGetOp1(); GenTreePtr addrBaseHigh = new (m_compiler, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, addrBase->TypeGet(), addrBase->AsLclVarCommon()->GetLclNum(), BAD_IL_OFFSET); GenTreePtr addrHigh = new (m_compiler, GT_LEA) GenTreeAddrMode(TYP_REF, addrBaseHigh, nullptr, 0, genTypeSize(TYP_INT)); GenTreePtr indHigh = new (m_compiler, GT_IND) GenTreeIndir(GT_IND, TYP_INT, addrHigh, nullptr); m_compiler->lvaIncRefCnts(addrBaseHigh); Range().InsertAfter(indLow, addrBaseHigh, addrHigh, indHigh); return FinalizeDecomposition(use, indLow, indHigh); }
//------------------------------------------------------------------------ // TreeNodeInfoInitPutArgStk: Set the NodeInfo for a GT_PUTARG_STK node // // Arguments: // argNode - a GT_PUTARG_STK node // // Return Value: // None. // // Notes: // Set the child node(s) to be contained when we have a multireg arg // void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info) { assert(argNode->gtOper == GT_PUTARG_STK); GenTreePtr putArgChild = argNode->gtOp.gtOp1; // Initialize 'argNode' as not contained, as this is both the default case // and how MakeSrcContained expects to find things setup. // argNode->gtLsraInfo.srcCount = 1; argNode->gtLsraInfo.dstCount = 0; // Do we have a TYP_STRUCT argument (or a GT_FIELD_LIST), if so it must be a multireg pass-by-value struct if ((putArgChild->TypeGet() == TYP_STRUCT) || (putArgChild->OperGet() == GT_FIELD_LIST)) { // We will use store instructions that each write a register sized value if (putArgChild->OperGet() == GT_FIELD_LIST) { // We consume all of the items in the GT_FIELD_LIST argNode->gtLsraInfo.srcCount = info->numSlots; putArgChild->SetContained(); } else { #ifdef _TARGET_ARM64_ // We could use a ldp/stp sequence so we need two internal registers argNode->gtLsraInfo.internalIntCount = 2; #else // _TARGET_ARM_ // We could use a ldr/str sequence so we need a internal register argNode->gtLsraInfo.internalIntCount = 1; #endif // _TARGET_ARM_ if (putArgChild->OperGet() == GT_OBJ) { GenTreePtr objChild = putArgChild->gtOp.gtOp1; if (objChild->OperGet() == GT_LCL_VAR_ADDR) { // We will generate all of the code for the GT_PUTARG_STK, the GT_OBJ and the GT_LCL_VAR_ADDR // as one contained operation // MakeSrcContained(putArgChild, objChild); } } // We will generate all of the code for the GT_PUTARG_STK and it's child node // as one contained operation // MakeSrcContained(argNode, putArgChild); } } else { // We must not have a multi-reg struct assert(info->numSlots == 1); } }
GenTreePtr CodeGen::genMakeAddressableFloat(GenTreePtr tree, regMaskTP * regMaskIntPtr, regMaskTP * regMaskFltPtr, bool bCollapseConstantDoubles) { *regMaskIntPtr = *regMaskFltPtr = 0; switch (tree->OperGet()) { case GT_LCL_VAR: genMarkLclVar(tree); __fallthrough; case GT_REG_VAR: case GT_LCL_FLD: case GT_CLS_VAR: return tree; case GT_IND: // Try to make the address directly addressable if (genMakeIndAddrMode(tree->gtOp.gtOp1, tree, false, RBM_ALLFLOAT, RegSet::KEEP_REG, regMaskIntPtr, false)) { genUpdateLife(tree); return tree; } else { GenTreePtr addr = tree; tree = tree->gtOp.gtOp1; genCodeForTree(tree, 0); regSet.rsMarkRegUsed(tree, addr); *regMaskIntPtr = genRegMask(tree->gtRegNum); return addr; } // fall through default: genCodeForTreeFloat(tree); regSet.SetUsedRegFloat(tree, true); // update mask *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet()); return tree; break; } }
void GCInfo::gcMarkRegPtrVal(GenTreePtr tree) { if (varTypeIsGC(tree->TypeGet())) { if (tree->gtOper == GT_LCL_VAR) compiler->codeGen->genMarkLclVar(tree); if (tree->InReg()) { gcMarkRegSetNpt(genRegMask(tree->gtRegNum)); } } }
//------------------------------------------------------------------------ // ContainCheckCast: determine whether the source of a CAST node should be contained. // // Arguments: // node - pointer to the node // void Lowering::ContainCheckCast(GenTreeCast* node) { #ifdef _TARGET_ARM_ GenTreePtr castOp = node->CastOp(); var_types castToType = node->CastToType(); var_types srcType = castOp->TypeGet(); if (varTypeIsLong(castOp)) { assert(castOp->OperGet() == GT_LONG); MakeSrcContained(node, castOp); } #endif // _TARGET_ARM_ }
void CodeGen::genComputeAddressableFloat(GenTreePtr tree, regMaskTP addrRegInt, regMaskTP addrRegFlt, RegSet::KeepReg keptReg, regMaskTP needReg, RegSet::KeepReg keepReg, bool freeOnly /* = false */) { noway_assert(genStillAddressable(tree)); noway_assert(varTypeIsFloating(tree->TypeGet())); genDoneAddressableFloat(tree, addrRegInt, addrRegFlt, keptReg); regNumber reg; if (tree->gtFlags & GTF_REG_VAL) { reg = tree->gtRegNum; if (freeOnly && !(genRegMaskFloat(reg, tree->TypeGet()) & regSet.RegFreeFloat())) { goto LOAD_REG; } } else { LOAD_REG: RegSet::RegisterPreference pref(needReg, RBM_NONE); reg = regSet.PickRegFloat(tree->TypeGet(), &pref); genLoadFloat(tree, reg); } genMarkTreeInReg(tree, reg); if (keepReg == RegSet::KEEP_REG) { regSet.SetUsedRegFloat(tree, true); } }
//------------------------------------------------------------------------ // DecomposeInd: Decompose GT_IND. // // Arguments: // tree - the tree to decompose // // Return Value: // None. // void DecomposeLongs::DecomposeInd(GenTree** ppTree, Compiler::fgWalkData* data) { GenTreePtr indLow = *ppTree; GenTreeStmt* addrStmt = CreateTemporary(&indLow->gtOp.gtOp1); JITDUMP("[DecomposeInd]: Saving addr tree to a temp var:\n"); DISPTREE(addrStmt); // Change the type of lower ind. indLow->gtType = TYP_INT; // Create tree of ind(addr+4) GenTreePtr addrBase = indLow->gtGetOp1(); GenTreePtr addrBaseHigh = new(m_compiler, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, addrBase->TypeGet(), addrBase->AsLclVarCommon()->GetLclNum(), BAD_IL_OFFSET); GenTreePtr addrHigh = new(m_compiler, GT_LEA) GenTreeAddrMode(TYP_REF, addrBaseHigh, nullptr, 0, genTypeSize(TYP_INT)); GenTreePtr indHigh = new (m_compiler, GT_IND) GenTreeIndir(GT_IND, TYP_INT, addrHigh, nullptr); // Connect linear links SimpleLinkNodeAfter(addrBaseHigh, addrHigh); SimpleLinkNodeAfter(addrHigh, indHigh); FinalizeDecomposition(ppTree, data, indLow, indHigh); }
/************************************************************************************** * * Perform copy propagation on a given tree as we walk the graph and if it is a local * variable, then look up all currently live definitions and check if any of those * definitions share the same value number. If so, then we can make the replacement. * */ void Compiler::optCopyProp(BasicBlock* block, GenTreePtr stmt, GenTreePtr tree, LclNumToGenTreePtrStack* curSsaName) { // TODO-Review: EH successor/predecessor iteration seems broken. if (block->bbCatchTyp == BBCT_FINALLY || block->bbCatchTyp == BBCT_FAULT) { return; } // If not local nothing to do. if (!tree->IsLocal()) { return; } if (tree->OperGet() == GT_PHI_ARG || tree->OperGet() == GT_LCL_FLD) { return; } // Propagate only on uses. if (tree->gtFlags & GTF_VAR_DEF) { return; } unsigned lclNum = tree->AsLclVarCommon()->GetLclNum(); // Skip address exposed variables. if (fgExcludeFromSsa(lclNum)) { return; } assert(tree->gtVNPair.GetConservative() != ValueNumStore::NoVN); for (LclNumToGenTreePtrStack::KeyIterator iter = curSsaName->Begin(); !iter.Equal(curSsaName->End()); ++iter) { unsigned newLclNum = iter.Get(); GenTreePtr op = iter.GetValue()->Index(0); // Nothing to do if same. if (lclNum == newLclNum) { continue; } // Skip variables with assignments embedded in the statement (i.e., with a comma). Because we // are not currently updating their SSA names as live in the copy-prop pass of the stmt. if (VarSetOps::IsMember(this, optCopyPropKillSet, lvaTable[newLclNum].lvVarIndex)) { continue; } if (op->gtFlags & GTF_VAR_CAST) { continue; } if (gsShadowVarInfo != nullptr && lvaTable[newLclNum].lvIsParam && gsShadowVarInfo[newLclNum].shadowCopy == lclNum) { continue; } ValueNum opVN = GetUseAsgDefVNOrTreeVN(op); if (opVN == ValueNumStore::NoVN) { continue; } if (op->TypeGet() != tree->TypeGet()) { continue; } if (opVN != tree->gtVNPair.GetConservative()) { continue; } if (optCopyProp_LclVarScore(&lvaTable[lclNum], &lvaTable[newLclNum], true) <= 0) { continue; } // Check whether the newLclNum is live before being substituted. Otherwise, we could end // up in a situation where there must've been a phi node that got pruned because the variable // is not live anymore. For example, // if // x0 = 1 // else // x1 = 2 // print(c) <-- x is not live here. Let's say 'c' shares the value number with "x0." // // If we simply substituted 'c' with "x0", we would be wrong. Ideally, there would be a phi // node x2 = phi(x0, x1) which can then be used to substitute 'c' with. But because of pruning // there would be no such phi node. To solve this we'll check if 'x' is live, before replacing // 'c' with 'x.' if (!lvaTable[newLclNum].lvVerTypeInfo.IsThisPtr()) { if (lvaTable[newLclNum].lvAddrExposed) { continue; } // We compute liveness only on tracked variables. So skip untracked locals. if (!lvaTable[newLclNum].lvTracked) { continue; } // Because of this dependence on live variable analysis, CopyProp phase is immediately // after Liveness, SSA and VN. if (!VarSetOps::IsMember(this, compCurLife, lvaTable[newLclNum].lvVarIndex)) { continue; } } unsigned newSsaNum = SsaConfig::RESERVED_SSA_NUM; if (op->gtFlags & GTF_VAR_DEF) { newSsaNum = GetSsaNumForLocalVarDef(op); } else // parameters, this pointer etc. { newSsaNum = op->AsLclVarCommon()->GetSsaNum(); } if (newSsaNum == SsaConfig::RESERVED_SSA_NUM) { continue; } #ifdef DEBUG if (verbose) { JITDUMP("VN based copy assertion for "); printTreeID(tree); printf(" V%02d @%08X by ", lclNum, tree->GetVN(VNK_Conservative)); printTreeID(op); printf(" V%02d @%08X.\n", newLclNum, op->GetVN(VNK_Conservative)); gtDispTree(tree, nullptr, nullptr, true); } #endif lvaTable[lclNum].decRefCnts(block->getBBWeight(this), this); lvaTable[newLclNum].incRefCnts(block->getBBWeight(this), this); tree->gtLclVarCommon.SetLclNum(newLclNum); tree->AsLclVarCommon()->SetSsaNum(newSsaNum); #ifdef DEBUG if (verbose) { printf("copy propagated to:\n"); gtDispTree(tree, nullptr, nullptr, true); } #endif break; } return; }
void RegSet::SetUsedRegFloat(GenTreePtr tree, bool bValue) { /* The value must be sitting in a register */ assert(tree); assert(tree->gtFlags & GTF_REG_VAL); var_types type = tree->TypeGet(); #ifdef _TARGET_ARM_ if (type == TYP_STRUCT) { assert(m_rsCompiler->IsHfa(tree)); type = TYP_FLOAT; } #endif regNumber regNum = tree->gtRegNum; regMaskTP regMask = genRegMaskFloat(regNum, type); if (bValue) { // Mark as used #ifdef DEBUG if (m_rsCompiler->verbose) { printf("\t\t\t\t\t\t\tThe register %s currently holds ", getRegNameFloat(regNum, type)); Compiler::printTreeID(tree); printf("\n"); } #endif assert((rsGetMaskLock() & regMask) == 0); #if FEATURE_STACK_FP_X87 assert((rsGetMaskUsed() & regMask) == 0); #else /* Is the register used by two different values simultaneously? */ if (regMask & rsGetMaskUsed()) { /* Save the preceding use information */ rsRecMultiReg(regNum, type); } #endif /* Set the register's bit in the 'used' bitset */ rsSetMaskUsed( (rsGetMaskUsed() | regMask) ); // Assign slot rsSetUsedTree(regNum, tree); } else { // Mark as free #ifdef DEBUG if (m_rsCompiler->verbose) { printf("\t\t\t\t\t\t\tThe register %s no longer holds ", getRegNameFloat(regNum, type)); Compiler::printTreeID(tree); printf("\n"); } #endif assert((rsGetMaskUsed() & regMask) == regMask); // Are we freeing a multi-use registers? if (regMask & rsGetMaskMult()) { // Free any multi-use registers rsMultRegFree(regMask); return; } rsSetMaskUsed( (rsGetMaskUsed() & ~regMask) ); // Free slot rsFreeUsedTree(regNum, tree); } }
//------------------------------------------------------------------------ // TreeNodeInfoInitPutArgSplit: Set the NodeInfo for a GT_PUTARG_SPLIT node // // Arguments: // argNode - a GT_PUTARG_SPLIT node // // Return Value: // None. // // Notes: // Set the child node(s) to be contained // void Lowering::TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* argNode, TreeNodeInfo& info, fgArgTabEntryPtr argInfo) { assert(argNode->gtOper == GT_PUTARG_SPLIT); GenTreePtr putArgChild = argNode->gtOp.gtOp1; // Registers for split argument corresponds to source argNode->gtLsraInfo.dstCount = argInfo->numRegs; info.srcCount += argInfo->numRegs; regNumber argReg = argInfo->regNum; regMaskTP argMask = RBM_NONE; for (unsigned i = 0; i < argInfo->numRegs; i++) { argMask |= genRegMask((regNumber)((unsigned)argReg + i)); } argNode->gtLsraInfo.setDstCandidates(m_lsra, argMask); if (putArgChild->OperGet() == GT_FIELD_LIST) { // Generated code: // 1. Consume all of the items in the GT_FIELD_LIST (source) // 2. Store to target slot and move to target registers (destination) from source // argNode->gtLsraInfo.srcCount = argInfo->numRegs + argInfo->numSlots; // To avoid redundant moves, have the argument operand computed in the // register in which the argument is passed to the call. GenTreeFieldList* fieldListPtr = putArgChild->AsFieldList(); for (unsigned idx = 0; fieldListPtr != nullptr; fieldListPtr = fieldListPtr->Rest(), idx++) { if (idx < argInfo->numRegs) { GenTreePtr node = fieldListPtr->gtGetOp1(); node->gtLsraInfo.setSrcCandidates(m_lsra, genRegMask((regNumber)((unsigned)argReg + idx))); } } putArgChild->SetContained(); } else { assert(putArgChild->TypeGet() == TYP_STRUCT); assert(putArgChild->OperGet() == GT_OBJ); // We could use a ldr/str sequence so we need a internal register argNode->gtLsraInfo.srcCount = 1; argNode->gtLsraInfo.internalIntCount = 1; regMaskTP internalMask = RBM_ALLINT & ~argMask; argNode->gtLsraInfo.setInternalCandidates(m_lsra, internalMask); GenTreePtr objChild = putArgChild->gtOp.gtOp1; if (objChild->OperGet() == GT_LCL_VAR_ADDR) { // We will generate all of the code for the GT_PUTARG_SPLIT, the GT_OBJ and the GT_LCL_VAR_ADDR // as one contained operation // MakeSrcContained(putArgChild, objChild); putArgChild->gtLsraInfo.srcCount--; } argNode->gtLsraInfo.srcCount = putArgChild->gtLsraInfo.srcCount; MakeSrcContained(argNode, putArgChild); } }
void CodeGen::genLoadFloat(GenTreePtr tree, regNumber reg) { if (tree->IsRegVar()) { // if it has been spilled, unspill it.% LclVarDsc * varDsc = &compiler->lvaTable[tree->gtLclVarCommon.gtLclNum]; if (varDsc->lvSpilled) { UnspillFloat(varDsc); } inst_RV_RV(ins_FloatCopy(tree->TypeGet()), reg, tree->gtRegNum, tree->TypeGet()); } else { bool unalignedLoad = false; switch (tree->OperGet()) { case GT_IND: case GT_CLS_VAR: if (tree->gtFlags & GTF_IND_UNALIGNED) unalignedLoad = true; break; case GT_LCL_FLD: // Check for a misalignment on a Floating Point field // if (varTypeIsFloating(tree->TypeGet())) { if ((tree->gtLclFld.gtLclOffs % emitTypeSize(tree->TypeGet())) != 0) { unalignedLoad = true; } } break; default: break; } if (unalignedLoad) { // Make the target addressable // regMaskTP addrReg = genMakeAddressable(tree, 0, RegSet::KEEP_REG, true); regSet.rsLockUsedReg(addrReg); // Must prevent regSet.rsGrabReg from choosing an addrReg var_types loadType = tree->TypeGet(); assert(loadType == TYP_DOUBLE || loadType == TYP_FLOAT); // Unaligned Floating-Point Loads must be loaded into integer register(s) // and then moved over to the Floating-Point register regNumber intRegLo = regSet.rsGrabReg(RBM_ALLINT); regNumber intRegHi = REG_NA; regMaskTP tmpLockMask = genRegMask(intRegLo); if (loadType == TYP_DOUBLE) { intRegHi = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(intRegLo)); tmpLockMask |= genRegMask(intRegHi); } regSet.rsLockReg(tmpLockMask); // Temporarily lock the intRegs tree->gtType = TYP_INT; // Temporarily change the type to TYP_INT inst_RV_TT(ins_Load(TYP_INT), intRegLo, tree); regTracker.rsTrackRegTrash(intRegLo); if (loadType == TYP_DOUBLE) { inst_RV_TT(ins_Load(TYP_INT), intRegHi, tree, 4); regTracker.rsTrackRegTrash(intRegHi); } tree->gtType = loadType; // Change the type back to the floating point type regSet.rsUnlockReg(tmpLockMask); // Unlock the intRegs // move the integer register(s) over to the FP register // if (loadType == TYP_DOUBLE) getEmitter()->emitIns_R_R_R(INS_vmov_i2d, EA_8BYTE, reg, intRegLo, intRegHi); else getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, reg, intRegLo); // Free up anything that was tied up by genMakeAddressable // regSet.rsUnlockUsedReg(addrReg); genDoneAddressable(tree, addrReg, RegSet::KEEP_REG); } else { inst_RV_TT(ins_FloatLoad(tree->TypeGet()), reg, tree); } if (((tree->OperGet() == GT_CLS_VAR) || (tree->OperGet() == GT_IND)) && (tree->gtFlags & GTF_IND_VOLATILE)) { // Emit a memory barrier instruction after the load instGen_MemoryBarrier(); } } }
//------------------------------------------------------------------------ // TreeNodeInfoInitIndir: Specify register requirements for address expression // of an indirection operation. // // Arguments: // indirTree - GT_IND, GT_STOREIND, block node or GT_NULLCHECK gentree node // void Lowering::TreeNodeInfoInitIndir(GenTreePtr indirTree) { assert(indirTree->OperIsIndir()); // If this is the rhs of a block copy (i.e. non-enregisterable struct), // it has no register requirements. if (indirTree->TypeGet() == TYP_STRUCT) { return; } GenTreePtr addr = indirTree->gtGetOp1(); TreeNodeInfo* info = &(indirTree->gtLsraInfo); GenTreePtr base = nullptr; GenTreePtr index = nullptr; unsigned cns = 0; unsigned mul; bool rev; bool modifiedSources = false; bool makeContained = true; if ((addr->OperGet() == GT_LEA) && IsSafeToContainMem(indirTree, addr)) { GenTreeAddrMode* lea = addr->AsAddrMode(); base = lea->Base(); index = lea->Index(); cns = lea->gtOffset; #ifdef _TARGET_ARM_ // ARM floating-point load/store doesn't support a form similar to integer // ldr Rdst, [Rbase + Roffset] with offset in a register. The only supported // form is vldr Rdst, [Rbase + imm] with a more limited constraint on the imm. if (lea->HasIndex() || !emitter::emitIns_valid_imm_for_vldst_offset(cns)) { if (indirTree->OperGet() == GT_STOREIND) { if (varTypeIsFloating(indirTree->AsStoreInd()->Data())) { makeContained = false; } } else if (indirTree->OperGet() == GT_IND) { if (varTypeIsFloating(indirTree)) { makeContained = false; } } } #endif if (makeContained) { m_lsra->clearOperandCounts(addr); addr->SetContained(); // The srcCount is decremented because addr is now "contained", // then we account for the base and index below, if they are non-null. info->srcCount--; } } else if (comp->codeGen->genCreateAddrMode(addr, -1, true, 0, &rev, &base, &index, &mul, &cns, true /*nogen*/) && !(modifiedSources = AreSourcesPossiblyModifiedLocals(indirTree, base, index))) { // An addressing mode will be constructed that may cause some // nodes to not need a register, and cause others' lifetimes to be extended // to the GT_IND or even its parent if it's an assignment assert(base != addr); m_lsra->clearOperandCounts(addr); addr->SetContained(); // Traverse the computation below GT_IND to find the operands // for the addressing mode, marking the various constants and // intermediate results as not consuming/producing. // If the traversal were more complex, we might consider using // a traversal function, but the addressing mode is only made // up of simple arithmetic operators, and the code generator // only traverses one leg of each node. bool foundBase = (base == nullptr); bool foundIndex = (index == nullptr); GenTreePtr nextChild = nullptr; for (GenTreePtr child = addr; child != nullptr && !child->OperIsLeaf(); child = nextChild) { nextChild = nullptr; GenTreePtr op1 = child->gtOp.gtOp1; GenTreePtr op2 = (child->OperIsBinary()) ? child->gtOp.gtOp2 : nullptr; if (op1 == base) { foundBase = true; } else if (op1 == index) { foundIndex = true; } else { m_lsra->clearOperandCounts(op1); op1->SetContained(); if (!op1->OperIsLeaf()) { nextChild = op1; } } if (op2 != nullptr) { if (op2 == base) { foundBase = true; } else if (op2 == index) { foundIndex = true; } else { m_lsra->clearOperandCounts(op2); op2->SetContained(); if (!op2->OperIsLeaf()) { assert(nextChild == nullptr); nextChild = op2; } } } } assert(foundBase && foundIndex); info->srcCount--; // it gets incremented below. } else if (addr->gtOper == GT_ARR_ELEM) { // The GT_ARR_ELEM consumes all the indices and produces the offset. // The array object lives until the mem access. // We also consume the target register to which the address is // computed info->srcCount++; assert(addr->gtLsraInfo.srcCount >= 2); addr->gtLsraInfo.srcCount -= 1; } else { // it is nothing but a plain indir info->srcCount--; // base gets added in below base = addr; } if (!makeContained) { return; } if (base != nullptr) { info->srcCount++; } if (index != nullptr && !modifiedSources) { info->srcCount++; } // On ARM we may need a single internal register // (when both conditions are true then we still only need a single internal register) if ((index != nullptr) && (cns != 0)) { // ARM does not support both Index and offset so we need an internal register info->internalIntCount = 1; } else if (!emitter::emitIns_valid_imm_for_ldst_offset(cns, emitTypeSize(indirTree))) { // This offset can't be contained in the ldr/str instruction, so we need an internal register info->internalIntCount = 1; } }
GCInfo::WriteBarrierForm GCInfo::gcWriteBarrierFormFromTargetAddress(GenTreePtr tgtAddr) { GCInfo::WriteBarrierForm result = GCInfo::WBF_BarrierUnknown; // Default case, we have no information. // If we store through an int to a GC_REF field, we'll assume that needs to use a checked barriers. if (tgtAddr->TypeGet() == TYP_I_IMPL) { return GCInfo::WBF_BarrierChecked; // Why isn't this GCInfo::WBF_BarrierUnknown? } // Otherwise... assert(tgtAddr->TypeGet() == TYP_BYREF); bool simplifiedExpr = true; while (simplifiedExpr) { simplifiedExpr = false; tgtAddr = tgtAddr->gtSkipReloadOrCopy(); while (tgtAddr->OperGet() == GT_ADDR && tgtAddr->gtOp.gtOp1->OperGet() == GT_IND) { tgtAddr = tgtAddr->gtOp.gtOp1->gtOp.gtOp1; simplifiedExpr = true; assert(tgtAddr->TypeGet() == TYP_BYREF); } // For additions, one of the operands is a byref or a ref (and the other is not). Follow this down to its // source. while (tgtAddr->OperGet() == GT_ADD || tgtAddr->OperGet() == GT_LEA) { if (tgtAddr->OperGet() == GT_ADD) { if (tgtAddr->gtOp.gtOp1->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp1->TypeGet() == TYP_REF) { assert(!(tgtAddr->gtOp.gtOp2->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp2->TypeGet() == TYP_REF)); tgtAddr = tgtAddr->gtOp.gtOp1; simplifiedExpr = true; } else if (tgtAddr->gtOp.gtOp2->TypeGet() == TYP_BYREF || tgtAddr->gtOp.gtOp2->TypeGet() == TYP_REF) { tgtAddr = tgtAddr->gtOp.gtOp2; simplifiedExpr = true; } else { // We might have a native int. For example: // const int 0 // + byref // lclVar int V06 loc5 // this is a local declared "valuetype VType*" return GCInfo::WBF_BarrierUnknown; } } else { // Must be an LEA (i.e., an AddrMode) assert(tgtAddr->OperGet() == GT_LEA); tgtAddr = tgtAddr->AsAddrMode()->Base(); if (tgtAddr->TypeGet() == TYP_BYREF || tgtAddr->TypeGet() == TYP_REF) { simplifiedExpr = true; } else { // We might have a native int. return GCInfo::WBF_BarrierUnknown; } } } } if (tgtAddr->IsLocalAddrExpr() != nullptr) { // No need for a GC barrier when writing to a local variable. return GCInfo::WBF_NoBarrier; } if (tgtAddr->OperGet() == GT_LCL_VAR || tgtAddr->OperGet() == GT_REG_VAR) { unsigned lclNum = 0; if (tgtAddr->gtOper == GT_LCL_VAR) { lclNum = tgtAddr->gtLclVar.gtLclNum; } else { assert(tgtAddr->gtOper == GT_REG_VAR); lclNum = tgtAddr->gtRegVar.gtLclNum; } LclVarDsc* varDsc = &compiler->lvaTable[lclNum]; // Instead of marking LclVar with 'lvStackByref', // Consider decomposing the Value Number given to this LclVar to see if it was // created using a GT_ADDR(GT_LCLVAR) or a GT_ADD( GT_ADDR(GT_LCLVAR), Constant) // We may have an internal compiler temp created in fgMorphCopyBlock() that we know // points at one of our stack local variables, it will have lvStackByref set to true. // if (varDsc->lvStackByref) { assert(varDsc->TypeGet() == TYP_BYREF); return GCInfo::WBF_NoBarrier; } // We don't eliminate for inlined methods, where we (can) know where the "retBuff" points. if (!compiler->compIsForInlining() && lclNum == compiler->info.compRetBuffArg) { assert(compiler->info.compRetType == TYP_STRUCT); // Else shouldn't have a ret buff. // Are we assured that the ret buff pointer points into the stack of a caller? if (compiler->info.compRetBuffDefStack) { #if 0 // This is an optional debugging mode. If the #if 0 above is changed to #if 1, // every barrier we remove for stores to GC ref fields of a retbuff use a special // helper that asserts that the target is not in the heap. #ifdef DEBUG return WBF_NoBarrier_CheckNotHeapInDebug; #else return WBF_NoBarrier; #endif #else // 0 return GCInfo::WBF_NoBarrier; #endif // 0 } } } if (tgtAddr->TypeGet() == TYP_REF) { return GCInfo::WBF_BarrierUnchecked; } // Otherwise, we have no information. return GCInfo::WBF_BarrierUnknown; }
GCInfo::WriteBarrierForm GCInfo::gcIsWriteBarrierCandidate(GenTreePtr tgt, GenTreePtr assignVal) { #if FEATURE_WRITE_BARRIER /* Are we storing a GC ptr? */ if (!varTypeIsGC(tgt->TypeGet())) { return WBF_NoBarrier; } /* Ignore any assignments of NULL */ // 'assignVal' can be the constant Null or something else (LclVar, etc..) // that is known to be null via Value Numbering. if (assignVal->GetVN(VNK_Liberal) == ValueNumStore::VNForNull()) { return WBF_NoBarrier; } if (assignVal->gtOper == GT_CNS_INT && assignVal->gtIntCon.gtIconVal == 0) { return WBF_NoBarrier; } /* Where are we storing into? */ tgt = tgt->gtEffectiveVal(); switch (tgt->gtOper) { #ifndef LEGACY_BACKEND case GT_STOREIND: #endif // !LEGACY_BACKEND case GT_IND: /* Could be the managed heap */ if (tgt->TypeGet() == TYP_BYREF) { // Byref values cannot be in managed heap. // This case occurs for Span<T>. return WBF_NoBarrier; } return gcWriteBarrierFormFromTargetAddress(tgt->gtOp.gtOp1); case GT_LEA: return gcWriteBarrierFormFromTargetAddress(tgt->AsAddrMode()->Base()); case GT_ARR_ELEM: /* Definitely in the managed heap */ case GT_CLS_VAR: return WBF_BarrierUnchecked; case GT_REG_VAR: /* Definitely not in the managed heap */ case GT_LCL_VAR: case GT_LCL_FLD: case GT_STORE_LCL_VAR: case GT_STORE_LCL_FLD: return WBF_NoBarrier; default: break; } assert(!"Missing case in gcIsWriteBarrierCandidate"); #endif return WBF_NoBarrier; }
void CodeGen::genFloatArith (GenTreePtr tree, RegSet::RegisterPreference *tgtPref) { var_types type = tree->TypeGet(); genTreeOps oper = tree->OperGet(); GenTreePtr op1 = tree->gtGetOp1(); GenTreePtr op2 = tree->gtGetOp2(); regNumber tgtReg; unsigned varNum; LclVarDsc * varDsc; VARSET_TP varBit; assert(oper == GT_ADD || oper == GT_SUB || oper == GT_MUL || oper == GT_DIV); RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE); if (tgtPref == NULL) { tgtPref = &defaultPref; } // Is the op2 (RHS)more complex than op1 (LHS)? // if (tree->gtFlags & GTF_REVERSE_OPS) { regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op1->gtRsvdRegs); RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs); // Evaluate op2 into a floating point register // genCodeForTreeFloat(op2, &pref); regSet.SetUsedRegFloat(op2, true); // Evaluate op1 into any floating point register // genCodeForTreeFloat(op1); regSet.SetUsedRegFloat(op1, true); regNumber op1Reg = op1->gtRegNum; regMaskTP op1Mask = genRegMaskFloat(op1Reg, type); // Fix 388445 ARM JitStress WP7 regSet.rsLockUsedReg(op1Mask); genRecoverReg(op2, RBM_ALLFLOAT, RegSet::KEEP_REG); noway_assert(op2->gtFlags & GTF_REG_VAL); regSet.rsUnlockUsedReg(op1Mask); regSet.SetUsedRegFloat(op1, false); regSet.SetUsedRegFloat(op2, false); } else { regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op2->gtRsvdRegs); RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs); // Evaluate op1 into a floating point register // genCodeForTreeFloat(op1, &pref); regSet.SetUsedRegFloat(op1, true); // Evaluate op2 into any floating point register // genCodeForTreeFloat(op2); regSet.SetUsedRegFloat(op2, true); regNumber op2Reg = op2->gtRegNum; regMaskTP op2Mask = genRegMaskFloat(op2Reg, type); // Fix 388445 ARM JitStress WP7 regSet.rsLockUsedReg(op2Mask); genRecoverReg(op1, RBM_ALLFLOAT, RegSet::KEEP_REG); noway_assert(op1->gtFlags & GTF_REG_VAL); regSet.rsUnlockUsedReg(op2Mask); regSet.SetUsedRegFloat(op2, false); regSet.SetUsedRegFloat(op1, false); } tgtReg = regSet.PickRegFloat(type, tgtPref, true); noway_assert(op1->gtFlags & GTF_REG_VAL); noway_assert(op2->gtFlags & GTF_REG_VAL); inst_RV_RV_RV(ins_MathOp(oper, type), tgtReg, op1->gtRegNum, op2->gtRegNum, emitActualTypeSize(type)); genCodeForTreeFloat_DONE(tree, tgtReg); }