Skip to content

Commit fcb9b18

Browse files
authored
JIT: Add an emitter peephole for post-indexed addressing (#105181)
This transforms sequences like ```asm ldr x0, [x1] add x1, x1, #8 ``` into the equivalent ```asm ldr x0, [x1], #8 ```
1 parent 894f22d commit fcb9b18

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

src/coreclr/jit/emitarm64.cpp

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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.

src/coreclr/jit/emitarm64.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ FORCEINLINE bool OptimizeLdrStr(instruction ins,
193193
int varx = -1,
194194
int offs = -1 DEBUG_ARG(bool useRsvdReg = false));
195195

196+
bool OptimizePostIndexed(instruction ins, regNumber reg, ssize_t imm, emitAttr regAttr);
197+
196198
emitLclVarAddr* emitGetLclVarPairLclVar2(instrDesc* id)
197199
{
198200
assert(id->idIsLclVarPair());

0 commit comments

Comments
 (0)