Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

[x86/Linux] Do NOT use pXXX fields #9121

Merged
merged 16 commits into from
Feb 2, 2017

Conversation

parjong
Copy link

@parjong parjong commented Jan 26, 2017

This commit removes pXXX fields in REGDISPLAY, and revises all the references on these fields (as discussed in #8998) to refers corresponding fields in pCurrentContext and pCurrentContetPointers.

@parjong
Copy link
Author

parjong commented Jan 26, 2017

@dotnet-bot test OSX x64 Checked Build and Test please

1 similar comment
@parjong
Copy link
Author

parjong commented Jan 26, 2017

@dotnet-bot test OSX x64 Checked Build and Test please

@parjong
Copy link
Author

parjong commented Jan 26, 2017

@jkotas @janvorli Please take a look.

@parjong
Copy link
Author

parjong commented Jan 26, 2017

\CC @seanshpark @wateret

@janvorli
Copy link
Member

janvorli commented Jan 26, 2017

@parjong I think this refactoring went too far. It seems that instead of defining all the four methods per each register, it would be enough to define just GetRegisterLocation(rd, reg) macro or inline functions GetXXXLocation() where XXX is Eax, Ebx, Ecx, Edx, ... Then you would replace all usages of pXXX by GetXXXLocation(). It seems easier to read that way with using C++ dereferencing to read and write the values instead of introducing new functions.
Also the RestoreXXX has different semantics for WIN64EXCEPTIONS than for !WIN64EXCEPTIONS, which is really confusing.
The TrashXX functions are completely unnecessary, since they are just setters.
Could you please simplify it this way?

Edit: I can see now that the Trash is actually different from the setter for non-volatile registers.
Also, the Restore name itself is confusing, since it just assigns the context pointer for !WIN64EXCEPTIONS, which doesn't feel like restoring anything.

@janvorli
Copy link
Member

Also, I've missed before that you haven't removed the pXXX members for WIN64EXCEPTIONS, which I have expected to happen.

@parjong
Copy link
Author

parjong commented Jan 30, 2017

@janvorli Sorry for late response. I was out of office in last 4 days.

Do you mean that the value of *pCurrentContextPointers->(Reg) may differ from that of pCurrentContext->(Reg)? From your first comment, GetXXXLocation may work when we read the value, but I'm not sure how we could update the value only with this method. Could you let me know some details?

For your second comment, this PR removes pXXX members for WIN64EXCEPTIONS. These members are inside #ifndef WIN64EXCEPTIONS ~ #else region, and thus do not exist for WIN64EXCEPTIONS. (I'll revise this PR to make it easy to read).

@janvorli
Copy link
Member

@parjong than you for your response. As for my last comment, I am sorry, I can see now that you are right and the pXXX members are no longer there for WIN64EXCEPTIONS. I somehow confused myself.

As for the GetXXXLocation, you are right, we would also need a SetXXXLocation function. But these two should be sufficient and it would be very easy to understand their intent.

@parjong
Copy link
Author

parjong commented Jan 30, 2017

@janvorli Thank you for comment. Could you let me know some details about pCurrentContextPointers? (I'm trying to revise TrashXXX methods)

UnwindEbpDoubleAlignFrame updates all the registers with some garbage data. To revise that code, I introduced TrashXXX methods because I though that these updates should not be reflected in pCurrentContextPointers.

Is it enough to use SetXXX for this code?

@janvorli
Copy link
Member

@parjong I think that basically everything that was done through pXXX before should now be done through pCurrentContextPointers->XXX. I see this change as a pure replacement of pXXX with the other ones.

@parjong
Copy link
Author

parjong commented Jan 31, 2017

@dotnet-bot test Windows_NT x64 Debug Build and Test please

@parjong
Copy link
Author

parjong commented Jan 31, 2017

@janvorli PR is revised per feedback. Please take a look.


#define VOLATILE_REG_METHODS(reg) \
inline PDWORD Get##reg##Location(void) { return &pCurrentContext->reg; } \
inline void Set##reg##Location(PDWORD p##reg) { pCurrentContext->reg = *p##reg; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to distinguish between the volatile and non-volatile cases. We can add Eax, Ecx and Edx to the KNONVOLATILE_CONTEXT_POINTERS instead. If you look at the structure for AMD64, you can see that the same thing is done there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SetXXXLocation should really just set the context pointer without touching the register value in the context and that we should update the context as an additional step after the unwinding (in OOPStackUnwinderX86::VirtualUnwind and EECodeManager::QuickUnwindStackFrame). Letting the function have a side effect that's not obvious based on its name is confusing when reading the code that uses this function.

It also seems that it would be better to keep the XXXFrame::UpdateRegDisplay code for x86 Linux as close as possible to the x64 version. And these functions also update context and context pointers separately on x64. And thinking about it more, it seems it would make sense to use the new GetXXXLocation / SetXXXLocation only in code that's common for WIN64EXCEPTIONS and !WIN64EXCEPTIONS, but let places where the code is split into separate WIN64EXCEPTIONS and !WIN64EXCEPTIONS versions access the specific pXXX or pCurrentContextPointers->XXX explicitly for the reason of consistency with the x64 version.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@janvorli Could you let me know the role of pCurrentContext? It seems that pCurrentContextPointers is enough for the purpose of unwind.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pCurrentContext->XXX contain values that the pCurrentContextPointers->XXX point to. Please note that there are actually two context and two context pointer structures. To one of them points the pCurrentContext / pCurrentContextPointers. To the other points the pParentContext / pParentContextPointers. That allows unwinding at some places to eliminate some context / context pointers copying by just switching the the pCurrentXXX and pParentXXX pointers.

While it might theoretically be possible to modify the existing code to not to use pCurrentContext for x86, it would be error prone and make the x86 Linux code even more different from the other targets using WIN64EXCEPTIONS.


#define NONVOLATILE_REG_METHODS(reg) \
inline PDWORD Get##reg##Location(void) { return (pCurrentContextPointers) ? pCurrentContextPointers->reg : &pCurrentContext->reg; } \
inline void Set##reg##Location(PDWORD p##reg) { if (pCurrentContextPointers) { pCurrentContextPointers->reg = p##reg; } pCurrentContext->reg = *p##reg; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, let's not check the pCurrentContextPointers here and ensure the pCurrentContextPointers is always valid when calling these functions instead. I think that the only place where this could be called with the context pointers set to NULL is from OOPStackUnwinderX86::VirtualUnwind with the way you've modified the function. If you didn't reassign the pCurrentContextPointers and pCurrentContext and just copied the values as you were doing before, you'd not have this issue. Or is there another place that I am missing?

@@ -3207,12 +3211,12 @@ const RegMask CALLEE_SAVED_REGISTERS_MASK[] =
RM_EBP // last register to be pushed
};

const SIZE_T REGDISPLAY_OFFSET_OF_CALLEE_SAVED_REGISTERS[] =
const REGDISPLAY::TAG CALLEE_SAVED_REGISTERS_TAG[] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to keep using offsets instead of a tag and switch in the SetLocation. It should work if we let the offsets array for WIN64EXCEPTIONS to keep offsets in the context pointers structure and the code accessing it would use pContext->pCurrentContextPointers as the base.
Since there are five places where we use the offset array the same way, it would then make sense to factor this out into a separate function, e.g. SetRegisterLocation(i, addr) so that the WIN64EXCEPTIONS changes would be localized.

Copy link
Member

@janvorli janvorli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, we are almost there! Just a few little things remaining.

@@ -430,6 +423,10 @@ inline void FillRegDisplay(const PREGDISPLAY pRD, PT_CONTEXT pctx, PT_CONTEXT pC
pRD->ctxPtrsOne.Esi = &pctx->Esi;
pRD->ctxPtrsOne.Edi = &pctx->Edi;
pRD->ctxPtrsOne.Ebp = &pctx->Ebp;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nit - for consistency with other target, you could now revert back to using a loop for copying the registers if you reordered them in the KNONVOLATILE_CONTEXT_POINTERS to match their order in the CONTEXT.

inline void Set##reg##Location(PDWORD p##reg) { pCurrentContext->reg = *p##reg; }

#define NONVOLATILE_REG_METHODS(reg) \
#define REG_METHODS(reg) \
inline PDWORD Get##reg##Location(void) { return (pCurrentContextPointers) ? pCurrentContextPointers->reg : &pCurrentContext->reg; } \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GetXXXLocation should also assume the pCurrentContextPointers is not NULL.

@@ -97,6 +97,10 @@ OOPStackUnwinderX86::VirtualUnwind(

ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;

#define CALLER_SAVED_REGISTER(reg) if (rd.pCurrentContextPointers->reg) ContextRecord->reg = *rd.pCurrentContextPointers->reg;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed. The UnwindStackFrame never sets context pointers of the caller saved registers.

Copy link
Author

@parjong parjong Feb 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnwindEbpDoubleAlignFrame sets Eax, Ecx, Edx for debugging purpose:

3832 #ifdef _DEBUG
3833             /* The filter has to be called by the VM. So we dont need to
3834                update callee-saved registers.
3835              */
3836
3837             if (flags & UpdateAllRegs)
3838             {
3839                 static DWORD s_badData = 0xDEADBEEF;
3840
3841                 pContext->SetEaxLocation(&s_badData);
3842                 pContext->SetEcxLocation(&s_badData);
3843                 pContext->SetEdxLocation(&s_badData);
3844
3845                 pContext->SetEbxLocation(&s_badData);
3846                 pContext->SetEsiLocation(&s_badData);
3847                 pContext->SetEdiLocation(&s_badData);
3848             }
3849 #endif

I'm not sure which one is better: enclosing this region with _DEBUG, or simply removing it. Please let me know your opinion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sorry, I have missed this. So please leave your change as it is.


#else // WIN64EXCEPTIONS
#define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = &m_ctx.regname;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the amd64 version, we should also set the context pointers for caller saved registers here.

ENUM_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER

#define CALLER_SAVED_REGISTER(reg) pRD->pCurrentContextPointers->reg = &m_Args->reg;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the amd64 version, context pointers to argument registers (ecx, edx) should be NULLed and only the pointer to the return register (eax) should be set.

pRD->pCurrentContext->Eip = *PTR_PCODE(pRD->PCTAddr);
pRD->pCurrentContext->Esp = (DWORD)(pRD->PCTAddr + sizeof(TADDR));

#define CALLEE_SAVED_REGISTER(reg) pRD->pCurrentContext->reg = m_Args->reg;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems we can use the UpdateRegDisplayFromCalleeSavedRegisters that you've added now.

Copy link
Author

@parjong parjong Feb 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my first trial, but UpdateRegDisplayFromCalleeSavedRegisters cannot be used here as m_Args is of type HijackArgs (not CalleeSavedRegisters).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see, the HijackArgs differ from the amd64 version.

@@ -154,6 +154,11 @@ typedef INT32 StackElemType;
// This represents some of the FramedMethodFrame fields that are
// stored at negative offsets.
//--------------------------------------------------------------------
#define ENUM_CALLER_SAVED_REGISTERS() \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nit. These are argument and scratch registers, so it seems it might be better to call it ENUM_ARGUMENT_AND_SCRATCH_REGISTERS. We have ENUM_ARGUMENT_REGISTERS for amd64, so that would make it similar.

Copy link
Member

@janvorli janvorli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you a lot!

@janvorli
Copy link
Member

janvorli commented Feb 1, 2017

@parjong I'll be OOF on Thursday and Friday, so if you create PRs during that time, please ask @jkotas to look at them instead.

@parjong
Copy link
Author

parjong commented Feb 2, 2017

@janvorli Oh, I see. Thank you for your consideration.

@parjong
Copy link
Author

parjong commented Feb 2, 2017

@dotnet-bot test Windows_NT x64 Release Priority 1 Build and Test please

@janvorli janvorli merged commit 009c70f into dotnet:master Feb 2, 2017
@parjong parjong deleted the fix/x86_revised_unwinder branch February 2, 2017 09:42
@karelz karelz modified the milestone: 2.0.0 Aug 28, 2017
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
* [x86/Linux] Do NOT use pXXX fields

* Fix x86/Windows build error

* Fix another x86/Windows build error

* Fix typo

* Do NOT trash EBP

* Reflect the original semantics of EHContext::UpdateFrame

* Unify ReadXXX/LocateXXX into GetXXXLocation, and RestoreXXX/TrashXXX into SetXXXLocation

* Revert the order of pXXX fields

* Revise cgenx86.cpp

* Revert unnecessary changes

* Remove direct accesses to CALLEE_SAVED_REGISTERS_TAG

* Do NOT update pCurrentContext inside SetXXXLocation

* Update RegPtr via offset

* Unify REG_METHODS (and revise UpdateRegDisplay methods accordingly)

* Revise per feedback

* Fix x86/Windows build error


Commit migrated from dotnet/coreclr@009c70f
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants