Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b88ff31
Replace successive "ldr" and "str" instructions with "ldp" and "stp"
AndyJGraham Sep 6, 2022
f0c918c
No longer use a temporary buffer to build the optimized instruction.
AndyJGraham Oct 31, 2022
f1b236e
Addressed assorted review comments.
AndyJGraham Nov 1, 2022
c0533bd
Now optimizes ascending locations and decending locations with
AndyJGraham Nov 3, 2022
372ee97
Modification to remove last instructions.
AndyJGraham Nov 14, 2022
12fc291
Merge branch 'main'
AndyJGraham Nov 15, 2022
0b377ed
Ongoing improvements to remove previously-emitted instruction
AndyJGraham Nov 29, 2022
46b85f8
Stopped optimization of consecutive instructions that straddled an in…
AndyJGraham Dec 1, 2022
e4741f9
Addressed code change requests in GitHub.
AndyJGraham Dec 1, 2022
2822f64
Merge branch 'main'
AndyJGraham Dec 1, 2022
10a4510
Various fixes to ldp/stp optimization
BruceForstall Dec 2, 2022
d80a69a
Merge pull request #1 from BruceForstall/LdpStp_Modifications_Fixes
AndyJGraham Dec 5, 2022
f6a49bf
Delete unnecessary and incorrect assert
BruceForstall Dec 7, 2022
ed4d070
Merge pull request #2 from BruceForstall/LdpStp_Modifications_FixAsse…
AndyJGraham Dec 7, 2022
4b0e51e
Diagnostic change only, to confirm whether a theory is correct or
AndyJGraham Dec 9, 2022
2997a8e
Revert "Diagnostic change only, to confirm whether a theory is correc…
AndyJGraham Dec 14, 2022
f0907cc
Do not merge. Temporarily removed calls to
AndyJGraham Dec 14, 2022
c5c4234
Modifications to better update the IP mapping table for a replaced in…
AndyJGraham Dec 15, 2022
bb8fdea
Merge branch 'main' of ssh://gerrit.oss.arm.com/enterprise-llt/dotnet…
AndyJGraham Dec 16, 2022
65eed90
Minor formatting change.
AndyJGraham Dec 16, 2022
e03b375
Check for out of range offsets
a74nh Jan 10, 2023
2cef6fc
Don't optimise during prolog/epilog
a74nh Jan 16, 2023
41a9828
Merge branch 'dotnet:main' into LdpStp_Modifications
a74nh Jan 16, 2023
ba89fd3
Fix windows build error
a74nh Jan 16, 2023
1fbf423
Merge branch main
a74nh Jan 19, 2023
ca9a325
IGF_HAS_REMOVED_INSTR is ARM64 only
a74nh Jan 20, 2023
e66ad66
Add OptimizeLdrStr function
a74nh Jan 20, 2023
8b44843
Fix formatting
a74nh Jan 20, 2023
2e7aaf6
Ensure local variables are tracked
a74nh Jan 24, 2023
fe76782
Don't peephole local variables
a74nh Jan 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Various fixes to ldp/stp optimization
Add code to update IP mappings when an instruction is removed.
  • Loading branch information
BruceForstall committed Dec 2, 2022
commit 10a4510246f3529d9bcc62d8b48365e0692452be
3 changes: 2 additions & 1 deletion src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -664,12 +664,13 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/

#ifdef DEBUG
void genIPmappingDisp(unsigned mappingNum, IPmappingDsc* ipMapping);
void genIPmappingDisp(unsigned mappingNum, const IPmappingDsc* ipMapping);
void genIPmappingListDisp();
#endif // DEBUG

void genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool isLabel);
void genIPmappingAddToFront(IPmappingDscKind kind, const DebugInfo& di, bool isLabel);
void genIPmappingUpdateForRemovedInstruction(emitLocation loc, unsigned removedCodeSize);
void genIPmappingGen();
void genAddRichIPMappingHere(const DebugInfo& di);

Expand Down
53 changes: 52 additions & 1 deletion src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7166,7 +7166,7 @@ const char* CodeGen::siStackVarName(size_t offs, size_t size, unsigned reg, unsi
* Display a IPmappingDsc. Pass -1 as mappingNum to not display a mapping number.
*/

void CodeGen::genIPmappingDisp(unsigned mappingNum, IPmappingDsc* ipMapping)
void CodeGen::genIPmappingDisp(unsigned mappingNum, const IPmappingDsc* ipMapping)
{
if (mappingNum != unsigned(-1))
{
Expand Down Expand Up @@ -7318,6 +7318,57 @@ void CodeGen::genIPmappingAddToFront(IPmappingDscKind kind, const DebugInfo& di,
#endif // DEBUG
}

//------------------------------------------------------------------------
// genIPmappingUpdateForRemovedInstruction: Update the IP mapping table for a removed instruction.
// If the last IP mapping corresponds to the location immediately after the removed instruction, then
// update the mapping to the location before the removed instruction.
//
// Arguments:
// loc - the emitter location we compare against
// removedCodeSize - the size of the instruction being removed
//
void CodeGen::genIPmappingUpdateForRemovedInstruction(emitLocation loc, unsigned removedCodeSize)
{
if (!compiler->opts.compDbgInfo)
{
return;
}

if (compiler->genIPmappings.size() <= 0)
{
return;
}

IPmappingDsc& prev = compiler->genIPmappings.back();
if (prev.ipmdNativeLoc == loc)
{
assert(prev.ipmdKind == IPmappingDscKind::Normal);

#ifdef DEBUG
if (verbose)
{
printf("Updating last IP mapping: ");
genIPmappingDisp(unsigned(-1), &prev);
}
#endif // DEBUG

int newInsCount = prev.ipmdNativeLoc.GetInsNum() - 1;
int newInsOffset = prev.ipmdNativeLoc.GetInsOffset() - removedCodeSize;
assert(newInsCount >= 0);
assert(newInsOffset >= 0);
unsigned newLoc = GetEmitter()->emitSpecifiedOffset(newInsCount, newInsOffset);
prev.ipmdNativeLoc.SetLocation(prev.ipmdNativeLoc.GetIG(), newLoc);

#ifdef DEBUG
if (verbose)
{
printf(" to: ");
genIPmappingDisp(unsigned(-1), &prev);
}
#endif // DEBUG
}
}

/*****************************************************************************/

void CodeGen::genEnsureCodeEmitted(const DebugInfo& di)
Expand Down
79 changes: 50 additions & 29 deletions src/coreclr/jit/emit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ void emitLocation::CaptureLocation(emitter* emit)
assert(Valid());
}

void emitLocation::SetLocation(insGroup* _ig, unsigned _codePos)
{
ig = _ig;
codePos = _codePos;

assert(Valid());
}

bool emitLocation::IsCurrentLocation(emitter* emit) const
{
assert(Valid());
Expand All @@ -50,6 +58,11 @@ int emitLocation::GetInsNum() const
return emitGetInsNumFromCodePos(codePos);
}

int emitLocation::GetInsOffset() const
{
return emitGetInsOfsFromCodePos(codePos);
}

// Get the instruction offset in the current instruction group, which must be a funclet prolog group.
// This is used to find an instruction offset used in unwind data.
// TODO-AMD64-Bug?: We only support a single main function prolog group, but allow for multiple funclet prolog
Expand Down Expand Up @@ -1002,28 +1015,26 @@ insGroup* emitter::emitSavIG(bool emitAdd)
}
}

// Fix the last instruction field
// Fix the last instruction field, if set. Note that even if there are instructions in the IG,
// emitLastIns might not be set if an optimization has just deleted it, and the new instruction
// being adding causes a new EXTEND IG to be created. Also, emitLastIns might not be in this IG
// at all if this IG is empty.

if (sz != 0)
assert((emitLastIns == nullptr) == (emitLastInsIG == nullptr));
if ((emitLastIns != nullptr) && (sz != 0))
{
assert(emitLastIns != nullptr);
// If we get here, emitLastIns must be in the current IG we are saving.
assert(emitLastInsIG == emitCurIG);
assert(emitCurIGfreeBase <= (BYTE*)emitLastIns);
assert((BYTE*)emitLastIns < emitCurIGfreeBase + sz);

#if defined(TARGET_XARCH)
assert(emitLastIns != nullptr);
if (emitLastIns->idIns() == INS_jmp)
{
ig->igFlags |= IGF_HAS_REMOVABLE_JMP;
}
#endif

// Make a copy of "emitLastIns", to be used in the event that a future
// optimisation call for the removal of a previously-emitted instruction.

emitPrevLastIns = emitLastIns;

emitLastIns = (instrDesc*)((BYTE*)id + ((BYTE*)emitLastIns - (BYTE*)emitCurIGfreeBase));
emitLastInsIG = ig;
}
Expand Down Expand Up @@ -1174,9 +1185,8 @@ void emitter::emitBegFN(bool hasFramePtr

emitPrologIG = emitIGlist = emitIGlast = emitCurIG = ig = emitAllocIG();

emitLastIns = nullptr;
emitLastInsIG = nullptr;
emitPrevLastIns = nullptr;
emitLastIns = nullptr;
emitLastInsIG = nullptr;

#ifdef TARGET_ARMARCH
emitLastMemBarrier = nullptr;
Expand Down Expand Up @@ -1497,11 +1507,6 @@ void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz)

/* Grab the space for the instruction */

// Make a copy of "emitLastIns", to be used in the event that a future
// optimisation calls for the removal of a previously-emitted instruction.

emitPrevLastIns = emitLastIns;

emitLastIns = id = (instrDesc*)(emitCurIGfreeNext + m_debugInfoSize);
emitLastInsIG = emitCurIG;
emitCurIGfreeNext += fullSize;
Expand Down Expand Up @@ -9101,6 +9106,19 @@ void emitter::emitNxtIG(bool extend)
// The `emitLastIns` is set to nullptr after this function. It is expected that a new instruction
// will be immediately generated after this, which will set it again.
//
// Removing an instruction can invalidate any captured emitter location (using emitLocation::CaptureLocation())
// after the instruction was generated. This is because the emitLocation stores the current IG instruction
// number and code size. If the instruction is removed and not replaced (e.g., it is at the end of the IG,
// and any replacement creates a new EXTEND IG), then the saved instruction number is incorrect. One way to
// work around this problem might be to make emitter::emitCodeOffset() tolerant of too-large instruction numbers.
// However, this doesn't fix the real problem. If an instruction is generated, then an emitLocation is captured,
// then the instruction is removed and replaced, the captured emitLocation will be pointing *after* the new
// instruction. Semantically, it probably should be pointing *before* the new instruction. The main case
// where emitLocations are captured, and this problem occurs, is in genIPmappingAdd(). So, if the removed
// instruction is just before the last added IP mapping, then update the IP mapping. This assumes that there
// will be only one such mapping (and not a set of equivalent, same-location mappings), which would require
// looping over the mappings.
//
// NOTE: It is expected that the GC effect of the removed instruction will be handled by the newly
// generated replacement(s).
//
Expand All @@ -9109,25 +9127,28 @@ void emitter::emitRemoveLastInstruction()
assert(emitLastIns != nullptr);
assert(emitLastInsIG != nullptr);

JITDUMP("Removing saved instruction in %s:\n", emitLabelString(emitLastInsIG));
JITDUMP("Removing saved instruction in %s:\n> ", emitLabelString(emitLastInsIG));
JITDUMPEXEC(dispIns(emitLastIns))

// We should assert it's not a jmp, as that would require updating the jump lists, e.g. emitCurIGjmpList.

BYTE* lastInsActualStartAddr = (BYTE*)emitLastIns - m_debugInfoSize;
unsigned short lastCodeSize = (unsigned short)emitLastIns->idCodeSize();

if ((emitCurIGfreeBase <= (BYTE*)lastInsActualStartAddr) && ((BYTE*)lastInsActualStartAddr < emitCurIGfreeEndp))
if ((emitCurIGfreeBase <= lastInsActualStartAddr) && (lastInsActualStartAddr < emitCurIGfreeEndp))
{
// The last instruction is in the current buffer. That means the current IG is non-empty.
assert(emitCurIGnonEmpty());
assert((BYTE*)lastInsActualStartAddr < emitCurIGfreeNext);
assert(lastInsActualStartAddr < emitCurIGfreeNext);
assert(emitCurIGinsCnt >= 1);
assert(emitCurIGsize >= emitLastIns->idCodeSize());

size_t insSize = emitCurIGfreeNext - (BYTE*)lastInsActualStartAddr;
// Use an emit location representing the current location (before the instruction is removed).
codeGen->genIPmappingUpdateForRemovedInstruction(emitLocation(this), emitLastIns->idCodeSize());

size_t insSize = emitCurIGfreeNext - lastInsActualStartAddr;

emitCurIGfreeNext = (BYTE*)lastInsActualStartAddr;
emitCurIGfreeNext = lastInsActualStartAddr;
emitCurIGinsCnt -= 1;
emitCurIGsize -= lastCodeSize;

Expand All @@ -9137,23 +9158,23 @@ void emitter::emitRemoveLastInstruction()
else
{
// The last instruction has already been saved. It must be the last instruction in the group.
assert((BYTE*)emitLastInsIG->igData + emitLastInsIG->igDataSize ==
(BYTE*)emitLastIns + emitSizeOfInsDsc(emitLastIns));
assert(emitLastInsIG->igData + emitLastInsIG->igDataSize == (BYTE*)emitLastIns + emitSizeOfInsDsc(emitLastIns));
assert(emitLastInsIG->igInsCnt >= 1);
assert(emitLastInsIG->igSize >= lastCodeSize);

// Create an emit location representing the end of the saved IG.
emitLocation loc(emitLastInsIG, emitSpecifiedOffset(emitLastInsIG->igInsCnt, emitLastInsIG->igSize));
codeGen->genIPmappingUpdateForRemovedInstruction(loc, emitLastIns->idCodeSize());

emitLastInsIG->igInsCnt -= 1;
emitLastInsIG->igSize -= lastCodeSize;

// We don't overwrite this memory; it's simply ignored. We could zero it to be sure nobody uses it,
// but it's not necessary, and leaving it might be useful for debugging.
}

// As we are removing the instruction, so we need to wind back
// "emitLastIns" to its previous value.

emitLastIns = emitPrevLastIns;
emitPrevLastIns = nullptr;
emitLastIns = nullptr;
emitLastInsIG = nullptr;
}

/*****************************************************************************
Expand Down
40 changes: 29 additions & 11 deletions src/coreclr/jit/emit.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,16 @@ class emitLocation
{
}

emitLocation(insGroup* _ig, unsigned _codePos)
{
SetLocation(_ig, _codePos);
}

emitLocation(emitter* emit)
{
CaptureLocation(emit);
}

emitLocation(void* emitCookie) : ig((insGroup*)emitCookie), codePos(0)
{
}
Expand All @@ -142,6 +152,7 @@ class emitLocation
}

void CaptureLocation(emitter* emit);
void SetLocation(insGroup* _ig, unsigned _codePos);

bool IsCurrentLocation(emitter* emit) const;

Expand All @@ -160,6 +171,7 @@ class emitLocation
}

int GetInsNum() const;
int GetInsOffset() const;

bool operator!=(const emitLocation& other) const
{
Expand Down Expand Up @@ -2178,20 +2190,23 @@ class emitter

instrDesc* emitLastIns;
insGroup* emitLastInsIG;
instrDesc* emitPrevLastIns;

// Check if a peephole optimization involving emitLastIns is safe.
//
// We must have a lastInstr to consult.
// We must have a non-null emitLastIns to consult.
// The emitForceNewIG check here prevents peepholes from crossing nogc boundaries.
// The final check prevents looking across an IG boundary unless we're in an extension IG.
bool emitCanPeepholeLastIns()
{
return (emitLastIns != nullptr) && // there is an emitLastInstr
!emitForceNewIG && // and we're not about to start a new IG
((emitCurIGinsCnt > 0) || // and we're not at the start of a new IG
((emitCurIG->igFlags & IGF_EXTEND) != 0)); // or we are at the start of a new IG,
// and it's an extension IG
assert((emitLastIns == nullptr) == (emitLastInsIG == nullptr));

return (emitLastIns != nullptr) && // there is an emitLastInstr
!emitForceNewIG && // and we're not about to start a new IG
((emitCurIGinsCnt > 0) || // and we're not at the start of a new IG
((emitCurIG->igFlags & IGF_EXTEND) != 0)) && // or we are at the start of a new IG,
// and it's an extension IG
((emitLastInsIG->igFlags & IGF_NOGCINTERRUPT) == (emitCurIG->igFlags & IGF_NOGCINTERRUPT));
// and the last instr IG has the same GC interrupt status as the current IG
}

#ifdef TARGET_ARMARCH
Expand Down Expand Up @@ -2821,12 +2836,15 @@ inline unsigned emitGetInsOfsFromCodePos(unsigned codePos)

inline unsigned emitter::emitCurOffset()
{
unsigned codePos = emitCurIGinsCnt + (emitCurIGsize << 16);
return emitSpecifiedOffset(emitCurIGinsCnt, emitCurIGsize);
}

assert(emitGetInsOfsFromCodePos(codePos) == emitCurIGsize);
assert(emitGetInsNumFromCodePos(codePos) == emitCurIGinsCnt);
inline unsigned emitter::emitSpecifiedOffset(unsigned insCount, unsigned igSize)
{
unsigned codePos = insCount + (igSize << 16);

// printf("[IG=%02u;ID=%03u;OF=%04X] => %08X\n", emitCurIG->igNum, emitCurIGinsCnt, emitCurIGsize, codePos);
assert(emitGetInsOfsFromCodePos(codePos) == igSize);
assert(emitGetInsNumFromCodePos(codePos) == insCount);

return codePos;
}
Expand Down
Loading