//------------------------------------------------------------------------ // TreeNodeInfoInitPutArgReg: Set the NodeInfo for a PUTARG_REG. // // Arguments: // node - The PUTARG_REG node. // argReg - The register in which to pass the argument. // info - The info for the node's using call. // isVarArgs - True if the call uses a varargs calling convention. // callHasFloatRegArgs - Set to true if this PUTARG_REG uses an FP register. // // Return Value: // None. // void Lowering::TreeNodeInfoInitPutArgReg( GenTreeUnOp* node, regNumber argReg, TreeNodeInfo& info, bool isVarArgs, bool* callHasFloatRegArgs) { assert(node != nullptr); assert(node->OperIsPutArgReg()); assert(argReg != REG_NA); // Each register argument corresponds to one source. info.srcCount++; // Set the register requirements for the node. regMaskTP argMask = genRegMask(argReg); #ifdef ARM_SOFTFP // If type of node is `long` then it is actually `double`. // The actual `long` types must have been transformed as a field list with two fields. if (node->TypeGet() == TYP_LONG) { info.srcCount++; assert(genRegArgNext(argReg) == REG_NEXT(argReg)); argMask |= genRegMask(REG_NEXT(argReg)); } #endif // ARM_SOFTFP node->gtLsraInfo.setDstCandidates(m_lsra, argMask); node->gtLsraInfo.setSrcCandidates(m_lsra, argMask); // To avoid redundant moves, have the argument operand computed in the // register in which the argument is passed to the call. node->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(m_lsra, m_lsra->getUseCandidates(node)); *callHasFloatRegArgs |= varTypeIsFloating(node->TypeGet()); }
void Compiler::unwindPushPopMaskCFI(regMaskTP regMask, bool isFloat) { regMaskTP regBit = isFloat ? genRegMask(REG_FP_FIRST) : 1; for (regNumber regNum = isFloat ? REG_FP_FIRST : REG_FIRST; regNum < REG_COUNT; regNum = REG_NEXT(regNum), regBit <<= 1) { if (regBit > regMask) { break; } if (regBit & regMask) { unwindPushPopCFI(regNum); } } }
// generate code for ckfinite tree/instruction void CodeGen::genFloatCheckFinite(GenTree *tree, RegSet::RegisterPreference *pref) { TempDsc * temp; int offs; GenTreePtr op1 = tree->gtOp.gtOp1; // Offset of the DWord containing the exponent offs = (op1->gtType == TYP_FLOAT) ? 0 : sizeof(int); // get tree into a register genCodeForTreeFloat(op1, pref); regNumber reg = regSet.rsPickReg(); int expMask; if (op1->gtType == TYP_FLOAT) { getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, reg, op1->gtRegNum); expMask = 0x7F800000; } else // double { assert(op1->gtType == TYP_DOUBLE); getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, reg, REG_NEXT(op1->gtRegNum)); // the high 32 bits of the double register expMask = 0x7FF00000; } regTracker.rsTrackRegTrash(reg); // Check if the exponent is all ones inst_RV_IV(INS_and, reg, expMask, EA_4BYTE); inst_RV_IV(INS_cmp, reg, expMask, EA_4BYTE); // If exponent was all 1's, we need to throw ArithExcep emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED); genJumpToThrowHlpBlk(jmpEqual, SCK_ARITH_EXCPN); genCodeForTreeFloat_DONE(tree, op1->gtRegNum); }
void dspRegMask(regMaskTP regMask, size_t minSiz) { const char* sep = ""; printf("["); bool inRegRange = false; regNumber regPrev = REG_NA; regNumber regHead = REG_NA; // When we start a range, remember the first register of the range, so we don't use range notation if the range contains just a single register. for (regNumber regNum = REG_INT_FIRST; regNum <= REG_INT_LAST; regNum = REG_NEXT(regNum)) { regMaskTP regBit = genRegMask(regNum); if ((regMask & regBit) != 0) { // We have a register to display. It gets displayed now if: // 1. This is the first register to display of a new range of registers (possibly because // no register has ever been displayed). // 2. This is the last register of an acceptable range (either the last integer register, // or the last of a range that is displayed with range notation). if (!inRegRange) { // It's the first register of a potential range. const char* nam = getRegName(regNum); printf("%s%s", sep, nam); minSiz -= strlen(sep) + strlen(nam); // By default, we're not starting a potential register range. sep = " "; // What kind of separator should we use for this range (if it is indeed going to be a range)? #if defined(_TARGET_AMD64_) // For AMD64, create ranges for int registers R8 through R15, but not the "old" registers. if (regNum >= REG_R8) { regHead = regNum; inRegRange = true; sep = "-"; } #elif defined(_TARGET_ARM64_) // R17 and R28 can't be the start of a range, since the range would include TEB or FP if ((regNum < REG_R17) || ((REG_R19 <= regNum) && (regNum < REG_R28))) { regHead = regNum; inRegRange = true; sep = "-"; } #elif defined(_TARGET_ARM_) if (regNum < REG_R12) { regHead = regNum; inRegRange = true; sep = "-"; } #elif defined(_TARGET_X86_) // No register ranges #else // _TARGET_* #error Unsupported or unset target architecture #endif // _TARGET_* } // We've already printed a register. Is this the end of a range? #if defined(_TARGET_ARM64_) else if ((regNum == REG_INT_LAST) || (regNum == REG_R17) // last register before TEB || (regNum == REG_R28)) // last register before FP #else // _TARGET_ARM64_ else if (regNum == REG_INT_LAST) #endif // _TARGET_ARM64_ { const char* nam = getRegName(regNum); printf("%s%s", sep, nam); minSiz -= strlen(sep) + strlen(nam); inRegRange = false; // No longer in the middle of a register range regHead = REG_NA; sep = " "; } } else // ((regMask & regBit) == 0) { if (inRegRange) { assert(regHead != REG_NA); if (regPrev != regHead) { // Close out the previous range, if it included more than one register. const char* nam = getRegName(regPrev); printf("%s%s", sep, nam); minSiz -= strlen(sep) + strlen(nam); } sep = " "; inRegRange = false; regHead = REG_NA; } } if (regBit > regMask) break; regPrev = regNum; }
void CodeGen::genFloatAssign(GenTree *tree) { var_types type = tree->TypeGet(); GenTreePtr op1 = tree->gtGetOp1(); GenTreePtr op2 = tree->gtGetOp2(); regMaskTP needRegOp1 = RBM_ALLINT; regMaskTP addrReg = RBM_NONE; bool volat = false; // Is this a volatile store bool unaligned = false; // Is this an unaligned store regNumber op2reg = REG_NA; #ifdef DEBUGGING_SUPPORT unsigned lclVarNum = compiler->lvaCount; unsigned lclILoffs = DUMMY_INIT(0); #endif noway_assert(tree->OperGet() == GT_ASG); // Is the target a floating-point local variable? // possibly even an enregistered floating-point local variable? // switch (op1->gtOper) { unsigned varNum; LclVarDsc * varDsc; case GT_LCL_FLD: // Check for a misalignment on a Floating Point field // if (varTypeIsFloating(op1->TypeGet())) { if ((op1->gtLclFld.gtLclOffs % emitTypeSize(op1->TypeGet())) != 0) { unaligned = true; } } break; case GT_LCL_VAR: varNum = op1->gtLclVarCommon.gtLclNum; noway_assert(varNum < compiler->lvaCount); varDsc = compiler->lvaTable + varNum; #ifdef DEBUGGING_SUPPORT // For non-debuggable code, every definition of a lcl-var has // to be checked to see if we need to open a new scope for it. // Remember the local var info to call siCheckVarScope // AFTER code generation of the assignment. // if (compiler->opts.compScopeInfo && !compiler->opts.compDbgCode && (compiler->info.compVarScopesCount > 0)) { lclVarNum = varNum; lclILoffs = op1->gtLclVar.gtLclILoffs; } #endif // Dead Store assert (with min opts we may have dead stores) // noway_assert(!varDsc->lvTracked || compiler->opts.MinOpts() || !(op1->gtFlags & GTF_VAR_DEATH)); // Does this variable live in a register? // if (genMarkLclVar(op1)) { noway_assert(!compiler->opts.compDbgCode); // We don't enregister any floats with debug codegen // Get hold of the target register // regNumber op1Reg = op1->gtRegVar.gtRegNum; // the variable being assigned should be dead in op2 assert(!varDsc->lvTracked || !VarSetOps::IsMember(compiler, genUpdateLiveSetForward(op2), varDsc->lvVarIndex)); // Setup register preferencing, so that we try to target the op1 enregistered variable // regMaskTP bestMask = genRegMask(op1Reg); if (type==TYP_DOUBLE) { assert((bestMask & RBM_DBL_REGS) != 0); bestMask |= genRegMask(REG_NEXT(op1Reg)); } RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestMask); // Evaluate op2 into a floating point register // genCodeForTreeFloat(op2, &pref); noway_assert(op2->gtFlags & GTF_REG_VAL); // Make sure the value ends up in the right place ... // For example if op2 is a call that returns a result // in REG_F0, we will need to do a move instruction here // if ((op2->gtRegNum != op1Reg) || (op2->TypeGet() != type)) { regMaskTP spillRegs = regSet.rsMaskUsed & genRegMaskFloat(op1Reg, op1->TypeGet()); if (spillRegs != 0) regSet.rsSpillRegs(spillRegs); assert(type == op1->TypeGet()); inst_RV_RV(ins_FloatConv(type, op2->TypeGet()), op1Reg, op2->gtRegNum, type); } genUpdateLife(op1); goto DONE_ASG; } break; case GT_CLS_VAR: case GT_IND: // Check for a volatile/unaligned store // assert((op1->OperGet() == GT_CLS_VAR) || (op1->OperGet() == GT_IND)); // Required for GTF_IND_VOLATILE flag to be valid if (op1->gtFlags & GTF_IND_VOLATILE) volat = true; if (op1->gtFlags & GTF_IND_UNALIGNED) unaligned = true; break; default: break; } // Is the value being assigned an enregistered floating-point local variable? // switch (op2->gtOper) { case GT_LCL_VAR: if (!genMarkLclVar(op2)) break; __fallthrough; case GT_REG_VAR: // We must honor the order evalauation in case op1 reassigns our op2 register // if (tree->gtFlags & GTF_REVERSE_OPS) break; // Is there an implicit conversion that we have to insert? // Handle this case with the normal cases below. // if (type != op2->TypeGet()) break; // Make the target addressable // addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true); noway_assert(op2->gtFlags & GTF_REG_VAL); noway_assert(op2->IsRegVar()); op2reg = op2->gtRegVar.gtRegNum; genUpdateLife(op2); goto CHK_VOLAT_UNALIGN; default: break; } // 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); // Generate op2 (RHS) into a floating point register // genCodeForTreeFloat(op2, &pref); regSet.SetUsedRegFloat(op2, true); // Make the target addressable // addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true); genRecoverReg(op2, RBM_ALLFLOAT, RegSet::KEEP_REG); noway_assert(op2->gtFlags & GTF_REG_VAL); regSet.SetUsedRegFloat(op2, false); } else { needRegOp1 = regSet.rsNarrowHint(needRegOp1, ~op2->gtRsvdRegs); // Make the target addressable // addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true); // Generate the RHS into any floating point register genCodeForTreeFloat(op2); } noway_assert(op2->gtFlags & GTF_REG_VAL); op2reg = op2->gtRegNum; // Is there an implicit conversion that we have to insert? // if (type != op2->TypeGet()) { regMaskTP bestMask = genRegMask(op2reg); if (type==TYP_DOUBLE) { if (bestMask & RBM_DBL_REGS) { bestMask |= genRegMask(REG_NEXT(op2reg)); } else { bestMask |= genRegMask(REG_PREV(op2reg)); } } RegSet::RegisterPreference op2Pref(RBM_ALLFLOAT, bestMask); op2reg = regSet.PickRegFloat(type, &op2Pref); inst_RV_RV(ins_FloatConv(type, op2->TypeGet()), op2reg, op2->gtRegNum, type); } // Make sure the LHS is still addressable // addrReg = genKeepAddressable(op1, addrReg); CHK_VOLAT_UNALIGN: regSet.rsLockUsedReg(addrReg); // Must prevent unaligned regSet.rsGrabReg from choosing an addrReg if (volat) { // Emit a memory barrier instruction before the store instGen_MemoryBarrier(); } if (unaligned) { var_types storeType = op1->TypeGet(); assert(storeType == TYP_DOUBLE || storeType == TYP_FLOAT); // Unaligned Floating-Point Stores must be done using the integer register(s) regNumber intRegLo = regSet.rsGrabReg(RBM_ALLINT); regNumber intRegHi = REG_NA; regMaskTP tmpLockMask = genRegMask(intRegLo); if (storeType == TYP_DOUBLE) { intRegHi = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(intRegLo)); tmpLockMask |= genRegMask(intRegHi); } // move the FP register over to the integer register(s) // if (storeType == TYP_DOUBLE) { getEmitter()->emitIns_R_R_R(INS_vmov_d2i, EA_8BYTE, intRegLo, intRegHi, op2reg); regTracker.rsTrackRegTrash(intRegHi); } else { getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, intRegLo, op2reg); } regTracker.rsTrackRegTrash(intRegLo); regSet.rsLockReg(tmpLockMask); // Temporarily lock the intRegs op1->gtType = TYP_INT; // Temporarily change the type to TYP_INT inst_TT_RV(ins_Store(TYP_INT), op1, intRegLo); if (storeType == TYP_DOUBLE) { inst_TT_RV(ins_Store(TYP_INT), op1, intRegHi, 4); } op1->gtType = storeType; // Change the type back to the floating point type regSet.rsUnlockReg(tmpLockMask); // Unlock the intRegs } else { // Move the value into the target // inst_TT_RV(ins_Store(op1->TypeGet()), op1, op2reg); } // Free up anything that was tied up by the LHS // regSet.rsUnlockUsedReg(addrReg); genDoneAddressable(op1, addrReg, RegSet::KEEP_REG); DONE_ASG: genUpdateLife(tree); #ifdef DEBUGGING_SUPPORT /* For non-debuggable code, every definition of a lcl-var has * to be checked to see if we need to open a new scope for it. */ if (lclVarNum < compiler->lvaCount) siCheckVarScope(lclVarNum, lclILoffs); #endif }
void CodeGen::genFloatSimple(GenTree *tree, RegSet::RegisterPreference *pref) { assert(tree->OperKind() & GTK_SMPOP); var_types type = tree->TypeGet(); RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE); if (pref == NULL) { pref = &defaultPref; } switch (tree->OperGet()) { // Assignment case GT_ASG: { genFloatAssign(tree); break; } // Arithmetic binops case GT_ADD: case GT_SUB: case GT_MUL: case GT_DIV: { genFloatArith(tree, pref); break; } case GT_NEG: { GenTreePtr op1 = tree->gtOp.gtOp1; // get the tree into a register genCodeForTreeFloat(op1, pref); // change the sign regNumber reg = regSet.PickRegFloat(type, pref); genMarkTreeInReg(tree, reg); inst_RV_RV(ins_MathOp(tree->OperGet(), type), reg, op1->gtRegNum, type); // mark register that holds tree genCodeForTreeFloat_DONE(tree, reg); return; } case GT_IND: { regMaskTP addrReg; // Make sure the address value is 'addressable' */ addrReg = genMakeAddressable(tree, 0, RegSet::FREE_REG); // Load the value onto the FP stack regNumber reg = regSet.PickRegFloat(type, pref); genLoadFloat(tree, reg); genDoneAddressable(tree, addrReg, RegSet::FREE_REG); genCodeForTreeFloat_DONE(tree, reg); break; } case GT_CAST: { genCodeForTreeCastFloat(tree, pref); break; } // Asg-Arithmetic ops case GT_ASG_ADD: case GT_ASG_SUB: case GT_ASG_MUL: case GT_ASG_DIV: { genFloatAsgArith(tree); break; } case GT_INTRINSIC: genFloatMath(tree, pref); break; case GT_RETURN: { GenTreePtr op1 = tree->gtOp.gtOp1; assert(op1); pref->best = (type==TYP_DOUBLE) ? RBM_DOUBLERET : RBM_FLOATRET; // Compute the result genCodeForTreeFloat(op1, pref); inst_RV_TT(ins_FloatConv(tree->TypeGet(), op1->TypeGet()), REG_FLOATRET, op1); if (compiler->info.compIsVarArgs) { if (tree->TypeGet() == TYP_FLOAT) { inst_RV_RV(INS_vmov_f2i, REG_INTRET, REG_FLOATRET, TYP_FLOAT, EA_4BYTE); } else { assert(tree->TypeGet() == TYP_DOUBLE); inst_RV_RV_RV(INS_vmov_d2i, REG_INTRET, REG_NEXT(REG_INTRET), REG_FLOATRET, EA_8BYTE); } } break; } case GT_ARGPLACE: break; case GT_COMMA: { GenTreePtr op1 = tree->gtOp.gtOp1; GenTreePtr op2 = tree->gtGetOp2(); if (tree->gtFlags & GTF_REVERSE_OPS) { genCodeForTreeFloat(op2, pref); regSet.SetUsedRegFloat(op2, true); genEvalSideEffects(op1); regSet.SetUsedRegFloat(op2, false); } else { genEvalSideEffects(op1); genCodeForTreeFloat(op2, pref); } genCodeForTreeFloat_DONE(tree, op2->gtRegNum); break; } case GT_CKFINITE: genFloatCheckFinite(tree, pref); break; default: NYI("Unhandled register FP codegen"); } }