@@ -5844,6 +5844,12 @@ void emitter::emitIns_R_R_I(instruction ins,
58445844 return;
58455845 }
58465846
5847+ if ((reg1 == reg2) && (EA_SIZE(attr) == EA_PTRSIZE) && emitComp->opts.OptimizationEnabled() &&
5848+ OptimizePostIndexed(ins, reg1, imm, attr))
5849+ {
5850+ return;
5851+ }
5852+
58475853 reg1 = encodingSPtoZR(reg1);
58485854 reg2 = encodingSPtoZR(reg2);
58495855 }
@@ -11070,6 +11076,37 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
1107011076 code |= ((code_t)imm << 12); // iiiiiiiii
1107111077 code |= insEncodeReg_Rn(id->idReg2()); // nnnnn
1107211078 dst += emitOutput_Instr(dst, code);
11079+
11080+ // With pre or post-indexing we may have a second GC register to
11081+ // update.
11082+ if (insOptsIndexed(id->idInsOpt()) && !id->idIsSmallDsc())
11083+ {
11084+ if (emitInsIsLoad(ins))
11085+ {
11086+ // Load will write the destination (reg1).
11087+ if (id->idGCref() != GCT_NONE)
11088+ {
11089+ emitGCregLiveUpd(id->idGCref(), id->idReg1(), dst);
11090+ }
11091+ else
11092+ {
11093+ emitGCregDeadUpd(id->idReg1(), dst);
11094+ }
11095+ }
11096+
11097+ // We will always write reg2.
11098+ if (id->idGCrefReg2() != GCT_NONE)
11099+ {
11100+ emitGCregLiveUpd(id->idGCrefReg2(), id->idReg2(), dst);
11101+ }
11102+ else
11103+ {
11104+ emitGCregDeadUpd(id->idReg2(), dst);
11105+ }
11106+
11107+ goto SKIP_GC_UPDATE;
11108+ }
11109+
1107311110 break;
1107411111
1107511112 case IF_LS_2D: // LS_2D .Q.............. ....ssnnnnnttttt Vt Rn
@@ -17150,6 +17187,127 @@ bool emitter::IsOptimizableLdrToMov(
1715017187 return true;
1715117188}
1715217189
17190+ //-----------------------------------------------------------------------------------
17191+ // OptimizePostIndexed: Optimize an addition/subtraction from a register by
17192+ // replacing the previous instruction with a post-indexed addressing form if
17193+ // possible.
17194+ //
17195+ // Arguments:
17196+ // ins - Whether this is an add or subtraction
17197+ // reg - The register that is being updated
17198+ // imm - Immediate that is being added/subtracted
17199+ //
17200+ // Returns:
17201+ // True if the previous instruction was optimized to perform the add/sub.
17202+ //
17203+ bool emitter::OptimizePostIndexed(instruction ins, regNumber reg, ssize_t imm, emitAttr regAttr)
17204+ {
17205+ assert((ins == INS_add) || (ins == INS_sub));
17206+
17207+ if (!emitCanPeepholeLastIns() || !emitInsIsLoadOrStore(emitLastIns->idIns()))
17208+ {
17209+ return false;
17210+ }
17211+
17212+ if ((emitLastIns->idInsFmt() != IF_LS_2A) || emitLastIns->idIsTlsGD())
17213+ {
17214+ return false;
17215+ }
17216+
17217+ // Cannot allow post indexing if the load itself is already modifying the
17218+ // register.
17219+ regNumber loadStoreDataReg = emitLastIns->idReg1();
17220+ if (loadStoreDataReg == reg)
17221+ {
17222+ return false;
17223+ }
17224+
17225+ // We must be updating the same register that the addressing is happening
17226+ // on. The SP register is stored as ZR, so make sure to normalize that too.
17227+ regNumber loadStoreAddrReg = encodingZRtoSP(emitLastIns->idReg2());
17228+ if (loadStoreAddrReg != reg)
17229+ {
17230+ return false;
17231+ }
17232+
17233+ // Only some stores/loads are eligible
17234+ switch (emitLastIns->idIns())
17235+ {
17236+ case INS_ldrb:
17237+ case INS_strb:
17238+ case INS_ldurb:
17239+ case INS_sturb:
17240+ case INS_ldrh:
17241+ case INS_strh:
17242+ case INS_ldurh:
17243+ case INS_sturh:
17244+ case INS_ldrsb:
17245+ case INS_ldursb:
17246+ case INS_ldrsh:
17247+ case INS_ldursh:
17248+ case INS_ldrsw:
17249+ case INS_ldursw:
17250+ case INS_ldr:
17251+ case INS_str:
17252+ case INS_ldur:
17253+ case INS_stur:
17254+ break;
17255+
17256+ default:
17257+ return false;
17258+ }
17259+
17260+ if (ins == INS_sub)
17261+ {
17262+ imm = -imm;
17263+ }
17264+
17265+ // Only some post-indexing offsets can be represented.
17266+ if ((imm < -256) || (imm >= 256))
17267+ {
17268+ return false;
17269+ }
17270+
17271+ instruction newIns = emitLastIns->idIns();
17272+ emitAttr newAttr;
17273+
17274+ switch (emitLastIns->idGCref())
17275+ {
17276+ case GCT_BYREF:
17277+ newAttr = EA_BYREF;
17278+ break;
17279+ case GCT_GCREF:
17280+ newAttr = EA_GCREF;
17281+ break;
17282+ default:
17283+ newAttr = emitLastIns->idOpSize();
17284+ break;
17285+ }
17286+
17287+ emitRemoveLastInstruction();
17288+
17289+ instrDesc* id = emitNewInstrCns(newAttr, imm);
17290+ id->idIns(newIns);
17291+ id->idInsFmt(IF_LS_2C);
17292+ id->idInsOpt(INS_OPTS_POST_INDEX);
17293+
17294+ id->idReg1(loadStoreDataReg);
17295+ id->idReg2(encodingSPtoZR(loadStoreAddrReg));
17296+
17297+ if (EA_IS_BYREF(regAttr))
17298+ {
17299+ id->idGCrefReg2(GCT_BYREF);
17300+ }
17301+ else if (EA_IS_GCREF(regAttr))
17302+ {
17303+ id->idGCrefReg2(GCT_GCREF);
17304+ }
17305+
17306+ dispIns(id);
17307+ appendToCurIG(id);
17308+ return true;
17309+ }
17310+
1715317311#if defined(FEATURE_SIMD)
1715417312//-----------------------------------------------------------------------------------
1715517313// emitStoreSimd12ToLclOffset: store SIMD12 value from dataReg to varNum+offset.
0 commit comments