diff options
Diffstat (limited to 'src/vm/arm/gmscpu.h')
-rw-r--r-- | src/vm/arm/gmscpu.h | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/src/vm/arm/gmscpu.h b/src/vm/arm/gmscpu.h new file mode 100644 index 0000000000..dee60633ad --- /dev/null +++ b/src/vm/arm/gmscpu.h @@ -0,0 +1,175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/**************************************************************/ +/* gmscpu.h */ +/**************************************************************/ +/* HelperFrame is defines 'GET_STATE(machState)' macro, which + figures out what the state of the machine will be when the + current method returns. It then stores the state in the + JIT_machState structure. */ + +/**************************************************************/ + +#ifndef __gmscpu_h__ +#define __gmscpu_h__ + +#define __gmscpu_h__ + +#ifdef _DEBUG +class HelperMethodFrame; +struct MachState; +EXTERN_C MachState* __stdcall HelperMethodFrameConfirmState(HelperMethodFrame* frame, void* esiVal, void* ediVal, void* ebxVal, void* ebpVal); +#endif + + // A MachState indicates the register state of the processor at some point in time (usually + // just before or after a call is made). It can be made one of two ways. Either explicitly + // (when you for some reason know the values of all the registers), or implicitly using the + // GET_STATE macros. + +typedef DPTR(struct MachState) PTR_MachState; +struct MachState { + + BOOL isValid() { LIMITED_METHOD_DAC_CONTRACT; return _isValid; } + TADDR GetRetAddr() { LIMITED_METHOD_DAC_CONTRACT; return _pc; } + + friend class HelperMethodFrame; + friend class CheckAsmOffsets; + friend struct LazyMachState; + + +protected: + // The simplest way to understand the relationship between capturedR4_R11 (registers + // representing the captured state) and _R4_R11 (pointers to registers representing + // preserved state) is as follows: + // + // 1) LazyMachState::unwindLazyState is invoked by HelperMethodFrame to initialize the captured + // state. It then performs an unwind and copies the register pointers to _R4_R11. + // + // 2) HelperMethodFrame::UpdateRegdisplay is invoked by our StackWalker that initializes + // the regdisplay with the updated register state. + // + // 3) HelperMethodFrameRestoreState is invoked when the HMF state machine exits and it + // restores the values of unmodified registers. + + TADDR captureR4_R11[8]; // Registers R4..R11 at the time of capture + + PTR_DWORD _R4_R11[8]; // Preserved registers + + TADDR _pc; // program counter after the function returns + TADDR _sp; // stack pointer after the function returns + + BOOL _isValid; +}; + +/********************************************************************/ +/* This allows you to defer the computation of the Machine state + until later. Note that we don't reuse slots, because we want + this to be threadsafe without locks */ + +struct LazyMachState : public MachState { + // compute the machine state of the processor as it will exist just + // after the return after at most'funCallDepth' number of functions. + // if 'testFtn' is non-NULL, the return address is tested at each + // return instruction encountered. If this test returns non-NULL, + // then stack walking stops (thus you can walk up to the point that the + // return address matches some criteria + + // Normally this is called with funCallDepth=1 and testFtn = 0 so that + // it returns the state of the processor after the function that called 'captureState()' + void setLazyStateFromUnwind(MachState* copy); + static void unwindLazyState(LazyMachState* baseState, + MachState* lazyState, + DWORD threadId, + int funCallDepth = 1, + HostCallPreference hostCallPreference = AllowHostCalls); + + friend class HelperMethodFrame; + friend class CheckAsmOffsets; +private: + TADDR captureSp; // Stack pointer at the time of capture + TADDR captureIp; // Instruction pointer at the time of capture +}; + +// R4 - R11 +#define NUM_NONVOLATILE_CONTEXT_POINTERS 8 + +inline void LazyMachState::setLazyStateFromUnwind(MachState* copy) +{ + LIMITED_METHOD_CONTRACT; + +#if defined(DACCESS_COMPILE) + // This function cannot be called in DAC because DAC cannot update target memory. + DacError(E_FAIL); + return; + +#else // !DACCESS_COMPILE + this->_pc = copy->_pc; + this->_sp = copy->_sp; + + // Capture* has already been set, so there is no need to touch it. + // This was setup in LazyMachState::unwindLazyState just before we + // called into the OS for unwind. + + // Prepare to loop over the nonvolatile context pointers for and + // make sure to properly copy interior pointers into the new struct. + + PDWORD* pSrc = ©->_R4_R11[0]; + PDWORD* pDst = &this->_R4_R11[0]; + + const PDWORD LowerBoundDst = (PDWORD) this; + const PDWORD LowerBoundSrc = (PDWORD) copy; + + // Calculate the upperbound till which we need to loop (i.e. the highest address till + // which we have saved non-volatile pointers). + const PDWORD UpperBoundSrc = (PDWORD) (((BYTE*)LowerBoundSrc) + offsetof(LazyMachState, _pc)); + +#ifdef _DEBUG + int count = 0; +#endif // _DEBUG + + while (((PDWORD)pSrc) < UpperBoundSrc) + { +#ifdef _DEBUG + count++; +#endif // _DEBUG + + PDWORD valueSrc = *pSrc++; + + // If any non-volatile register pointer is pointing to the corresponding register field + // in the MachState, then make the corresponding pointer in "this" MachState point + // to the corresponding field. + if ((LowerBoundSrc <= valueSrc) && (valueSrc < UpperBoundSrc)) + { + valueSrc = (PDWORD)((BYTE*)valueSrc - (BYTE*)LowerBoundSrc + (BYTE*)LowerBoundDst); + } + + *pDst++ = valueSrc; + } + + CONSISTENCY_CHECK_MSGF(count == NUM_NONVOLATILE_CONTEXT_POINTERS, ("count != NUM_NONVOLATILE_CONTEXT_POINTERS, actually = %d", count)); + + // this has to be last because we depend on write ordering to + // synchronize the race implicit in updating this struct + VolatileStore(&_isValid, TRUE); + +#endif // !DACCESS_COMPILE + +} +typedef DPTR(LazyMachState) PTR_LazyMachState; + +// Do the initial capture of the machine state. This is meant to be +// as light weight as possible, as we may never need the state that +// we capture. Thus to complete the process you need to call +// 'getMachState()', which finishes the process +EXTERN_C void LazyMachStateCaptureState(struct LazyMachState *pState); + +// CAPTURE_STATE captures just enough register state so that the state of the +// processor can be deterined just after the the routine that has CAPTURE_STATE in +// it returns. + +#define CAPTURE_STATE(machState, ret) \ + LazyMachStateCaptureState(machState) + +#endif |