@@ -883,6 +883,26 @@ bool emitter::emitInsWritesToLclVarStackLoc(instrDesc* id)
883883 }
884884}
885885
886+ bool emitter::emitInsWritesToLclVarStackLocPair (instrDesc* id)
887+ {
888+ if (!id->idIsLclVar ())
889+ return false ;
890+
891+ instruction ins = id->idIns ();
892+
893+ // This list is related to the list of instructions used to store local vars in emitIns_S_S_R_R().
894+ // We don't accept writing to float local vars.
895+
896+ switch (ins)
897+ {
898+ case INS_stnp:
899+ case INS_stp:
900+ return true ;
901+ default :
902+ return false ;
903+ }
904+ }
905+
886906bool emitter::emitInsMayWriteMultipleRegs (instrDesc* id)
887907{
888908 instruction ins = id->idIns ();
@@ -6218,6 +6238,101 @@ void emitter::emitIns_S_R(instruction ins, emitAttr attr, regNumber reg1, int va
62186238 appendToCurIG (id);
62196239}
62206240
6241+ /* ****************************************************************************
6242+ *
6243+ * Add an instruction referencing consecutive stack-based local variable slots and two registers
6244+ */
6245+ void emitter::emitIns_S_S_R_R (
6246+ instruction ins, emitAttr attr1, emitAttr attr2, regNumber reg1, regNumber reg2, int varx, int offs)
6247+ {
6248+ assert ((ins == INS_stp) || (ins == INS_stnp));
6249+ assert (EA_8BYTE == EA_SIZE (attr1));
6250+ assert (EA_8BYTE == EA_SIZE (attr2));
6251+ assert (isGeneralRegisterOrZR (reg1));
6252+ assert (isGeneralRegisterOrZR (reg2));
6253+ assert (offs >= 0 );
6254+
6255+ emitAttr size = EA_SIZE (attr1);
6256+ insFormat fmt = IF_LS_3B;
6257+ int disp = 0 ;
6258+ const unsigned scale = 3 ;
6259+
6260+ /* Figure out the variable's frame position */
6261+ int base;
6262+ bool FPbased;
6263+
6264+ base = emitComp->lvaFrameAddress (varx, &FPbased);
6265+ disp = base + offs;
6266+
6267+ // TODO-ARM64-CQ: with compLocallocUsed, should we use REG_SAVED_LOCALLOC_SP instead?
6268+ regNumber reg3 = FPbased ? REG_FPBASE : REG_SPBASE;
6269+ reg3 = encodingSPtoZR (reg3);
6270+
6271+ bool useRegForAdr = true ;
6272+ ssize_t imm = disp;
6273+ ssize_t mask = (1 << scale) - 1 ; // the mask of low bits that must be zero to encode the immediate
6274+ if (imm == 0 )
6275+ {
6276+ useRegForAdr = false ;
6277+ }
6278+ else
6279+ {
6280+ if ((imm & mask) == 0 )
6281+ {
6282+ ssize_t immShift = imm >> scale; // The immediate is scaled by the size of the ld/st
6283+
6284+ if ((immShift >= -64 ) && (immShift <= 63 ))
6285+ {
6286+ fmt = IF_LS_3C;
6287+ useRegForAdr = false ;
6288+ imm = immShift;
6289+ }
6290+ }
6291+ }
6292+
6293+ if (useRegForAdr)
6294+ {
6295+ regNumber rsvd = codeGen->rsGetRsvdReg ();
6296+ emitIns_R_R_Imm (INS_add, EA_8BYTE, rsvd, reg3, imm);
6297+ reg3 = rsvd;
6298+ imm = 0 ;
6299+ }
6300+
6301+ assert (fmt != IF_NONE);
6302+
6303+ instrDesc* id = emitNewInstrCns (attr1, imm);
6304+
6305+ id->idIns (ins);
6306+ id->idInsFmt (fmt);
6307+ id->idInsOpt (INS_OPTS_NONE);
6308+
6309+ if (EA_IS_GCREF (attr2))
6310+ {
6311+ /* A special value indicates a GCref pointer value */
6312+
6313+ id->idGCrefReg2 (GCT_GCREF);
6314+ }
6315+ else if (EA_IS_BYREF (attr2))
6316+ {
6317+ /* A special value indicates a Byref pointer value */
6318+
6319+ id->idGCrefReg2 (GCT_BYREF);
6320+ }
6321+
6322+ id->idReg1 (reg1);
6323+ id->idReg2 (reg2);
6324+ id->idReg3 (reg3);
6325+ id->idAddr ()->iiaLclVar .initLclVarAddr (varx, offs);
6326+ id->idSetIsLclVar ();
6327+
6328+ #ifdef DEBUG
6329+ id->idDebugOnlyInfo ()->idVarRefOffs = emitVarRefOffs;
6330+ #endif
6331+
6332+ dispIns (id);
6333+ appendToCurIG (id);
6334+ }
6335+
62216336/* ****************************************************************************
62226337 *
62236338 * Add an instruction referencing stack-based local variable and an immediate
@@ -9369,7 +9484,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
93699484
93709485 // Now we determine if the instruction has written to a (local variable) stack location, and either written a GC
93719486 // ref or overwritten one.
9372- if (emitInsWritesToLclVarStackLoc (id))
9487+ if (emitInsWritesToLclVarStackLoc (id) || emitInsWritesToLclVarStackLocPair (id) )
93739488 {
93749489 int varNum = id->idAddr ()->iiaLclVar .lvaVarNum ();
93759490 unsigned ofs = AlignDown (id->idAddr ()->iiaLclVar .lvaOffset (), sizeof (size_t ));
@@ -9396,6 +9511,31 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
93969511 if (vt == TYP_REF || vt == TYP_BYREF)
93979512 emitGCvarDeadUpd (adr + ofs, dst);
93989513 }
9514+ if (emitInsWritesToLclVarStackLocPair (id))
9515+ {
9516+ unsigned ofs2 = ofs + sizeof (size_t );
9517+ if (id->idGCrefReg2 () != GCT_NONE)
9518+ {
9519+ emitGCvarLiveUpd (adr + ofs2, varNum, id->idGCrefReg2 (), dst);
9520+ }
9521+ else
9522+ {
9523+ // If the type of the local is a gc ref type, update the liveness.
9524+ var_types vt;
9525+ if (varNum >= 0 )
9526+ {
9527+ // "Regular" (non-spill-temp) local.
9528+ vt = var_types (emitComp->lvaTable [varNum].lvType );
9529+ }
9530+ else
9531+ {
9532+ TempDsc* tmpDsc = emitComp->tmpFindNum (varNum);
9533+ vt = tmpDsc->tdTempType ();
9534+ }
9535+ if (vt == TYP_REF || vt == TYP_BYREF)
9536+ emitGCvarDeadUpd (adr + ofs2, dst);
9537+ }
9538+ }
93999539 }
94009540
94019541#ifdef DEBUG
0 commit comments