INS_OPTS_NONE, emitTypeSize(type0));
}
- // Emit two store instructions to store the two registers into the outgoing argument area
- emit->emitIns_S_R(ins_Store(type0), emitTypeSize(type0), loReg, varNumOut, argOffsetOut);
- emit->emitIns_S_R(ins_Store(type1), emitTypeSize(type1), hiReg, varNumOut,
- argOffsetOut + TARGET_POINTER_SIZE);
+ // Emit stp instruction to store the two registers into the outgoing argument area
+ emit->emitIns_S_S_R_R(INS_stp, emitTypeSize(type0), emitTypeSize(type1), loReg, hiReg, varNumOut,
+ argOffsetOut);
argOffsetOut += (2 * TARGET_POINTER_SIZE); // We stored 16-bytes of the struct
assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
void checkSizes();
union idAddrUnion {
- // TODO-Cleanup: We should really add a DEBUG-only tag to this union so we can add asserts
- // about reading what we think is here, to avoid unexpected corruption issues.
+// TODO-Cleanup: We should really add a DEBUG-only tag to this union so we can add asserts
+// about reading what we think is here, to avoid unexpected corruption issues.
+#ifndef _TARGET_ARM64_
emitLclVarAddr iiaLclVar;
- BasicBlock* iiaBBlabel;
- insGroup* iiaIGlabel;
- BYTE* iiaAddr;
- emitAddrMode iiaAddrMode;
+#endif
+ BasicBlock* iiaBBlabel;
+ insGroup* iiaIGlabel;
+ BYTE* iiaAddr;
+ emitAddrMode iiaAddrMode;
CORINFO_FIELD_HANDLE iiaFieldHnd; // iiaFieldHandle is also used to encode
// an offset into the JIT data constant area
struct
{
- regNumber _idReg3 : REGNUM_BITS;
- regNumber _idReg4 : REGNUM_BITS;
#ifdef _TARGET_ARM64_
- unsigned _idReg3Scaled : 1; // Reg3 is scaled by idOpSize bits
+ emitLclVarAddr iiaLclVar;
+ unsigned _idReg3Scaled : 1; // Reg3 is scaled by idOpSize bits
#endif
+ regNumber _idReg3 : REGNUM_BITS;
+ regNumber _idReg4 : REGNUM_BITS;
};
#elif defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
struct
// Returns true if the instruction may write to more than one register.
bool emitInsMayWriteMultipleRegs(instrDesc* id);
+
+ // Returns "true" if instruction "id->idIns()" writes to a LclVar stack slot pair.
+ bool emitInsWritesToLclVarStackLocPair(instrDesc* id);
#endif // _TARGET_ARMARCH_
/************************************************************************/
}
}
+bool emitter::emitInsWritesToLclVarStackLocPair(instrDesc* id)
+{
+ if (!id->idIsLclVar())
+ return false;
+
+ instruction ins = id->idIns();
+
+ // This list is related to the list of instructions used to store local vars in emitIns_S_S_R_R().
+ // We don't accept writing to float local vars.
+
+ switch (ins)
+ {
+ case INS_stnp:
+ case INS_stp:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool emitter::emitInsMayWriteMultipleRegs(instrDesc* id)
{
instruction ins = id->idIns();
/*****************************************************************************
*
+ * Add an instruction referencing consecutive stack-based local variable slots and two registers
+ */
+void emitter::emitIns_S_S_R_R(
+ instruction ins, emitAttr attr1, emitAttr attr2, regNumber reg1, regNumber reg2, int varx, int offs)
+{
+ assert((ins == INS_stp) || (ins == INS_stnp));
+ assert(EA_8BYTE == EA_SIZE(attr1));
+ assert(EA_8BYTE == EA_SIZE(attr2));
+ assert(isGeneralRegisterOrZR(reg1));
+ assert(isGeneralRegisterOrZR(reg2));
+ assert(offs >= 0);
+
+ emitAttr size = EA_SIZE(attr1);
+ insFormat fmt = IF_LS_3B;
+ int disp = 0;
+ const unsigned scale = 3;
+
+ /* Figure out the variable's frame position */
+ int base;
+ bool FPbased;
+
+ base = emitComp->lvaFrameAddress(varx, &FPbased);
+ disp = base + offs;
+
+ // TODO-ARM64-CQ: with compLocallocUsed, should we use REG_SAVED_LOCALLOC_SP instead?
+ regNumber reg3 = FPbased ? REG_FPBASE : REG_SPBASE;
+ reg3 = encodingSPtoZR(reg3);
+
+ bool useRegForAdr = true;
+ ssize_t imm = disp;
+ ssize_t mask = (1 << scale) - 1; // the mask of low bits that must be zero to encode the immediate
+ if (imm == 0)
+ {
+ useRegForAdr = false;
+ }
+ else
+ {
+ if ((imm & mask) == 0)
+ {
+ ssize_t immShift = imm >> scale; // The immediate is scaled by the size of the ld/st
+
+ if ((immShift >= -64) && (immShift <= 63))
+ {
+ fmt = IF_LS_3C;
+ useRegForAdr = false;
+ imm = immShift;
+ }
+ }
+ }
+
+ if (useRegForAdr)
+ {
+ regNumber rsvd = codeGen->rsGetRsvdReg();
+ emitIns_R_R_Imm(INS_add, EA_8BYTE, rsvd, reg3, imm);
+ reg3 = rsvd;
+ imm = 0;
+ }
+
+ assert(fmt != IF_NONE);
+
+ instrDesc* id = emitNewInstrCns(attr1, imm);
+
+ id->idIns(ins);
+ id->idInsFmt(fmt);
+ id->idInsOpt(INS_OPTS_NONE);
+
+ if (EA_IS_GCREF(attr2))
+ {
+ /* A special value indicates a GCref pointer value */
+
+ id->idGCrefReg2(GCT_GCREF);
+ }
+ else if (EA_IS_BYREF(attr2))
+ {
+ /* A special value indicates a Byref pointer value */
+
+ id->idGCrefReg2(GCT_BYREF);
+ }
+
+ id->idReg1(reg1);
+ id->idReg2(reg2);
+ id->idReg3(reg3);
+ id->idAddr()->iiaLclVar.initLclVarAddr(varx, offs);
+ id->idSetIsLclVar();
+
+#ifdef DEBUG
+ id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs;
+#endif
+
+ dispIns(id);
+ appendToCurIG(id);
+}
+
+/*****************************************************************************
+ *
* Add an instruction referencing stack-based local variable and an immediate
*/
void emitter::emitIns_S_I(instruction ins, emitAttr attr, int varx, int offs, int val)
// Now we determine if the instruction has written to a (local variable) stack location, and either written a GC
// ref or overwritten one.
- if (emitInsWritesToLclVarStackLoc(id))
+ if (emitInsWritesToLclVarStackLoc(id) || emitInsWritesToLclVarStackLocPair(id))
{
int varNum = id->idAddr()->iiaLclVar.lvaVarNum();
unsigned ofs = AlignDown(id->idAddr()->iiaLclVar.lvaOffset(), sizeof(size_t));
if (vt == TYP_REF || vt == TYP_BYREF)
emitGCvarDeadUpd(adr + ofs, dst);
}
+ if (emitInsWritesToLclVarStackLocPair(id))
+ {
+ unsigned ofs2 = ofs + sizeof(size_t);
+ if (id->idGCrefReg2() != GCT_NONE)
+ {
+ emitGCvarLiveUpd(adr + ofs2, varNum, id->idGCrefReg2(), dst);
+ }
+ else
+ {
+ // If the type of the local is a gc ref type, update the liveness.
+ var_types vt;
+ if (varNum >= 0)
+ {
+ // "Regular" (non-spill-temp) local.
+ vt = var_types(emitComp->lvaTable[varNum].lvType);
+ }
+ else
+ {
+ TempDsc* tmpDsc = emitComp->tmpFindNum(varNum);
+ vt = tmpDsc->tdTempType();
+ }
+ if (vt == TYP_REF || vt == TYP_BYREF)
+ emitGCvarDeadUpd(adr + ofs2, dst);
+ }
+ }
}
#ifdef DEBUG