summaryrefslogtreecommitdiff
path: root/src/inc/gcinfoencoder.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/inc/gcinfoencoder.h')
-rw-r--r--src/inc/gcinfoencoder.h552
1 files changed, 552 insertions, 0 deletions
diff --git a/src/inc/gcinfoencoder.h b/src/inc/gcinfoencoder.h
new file mode 100644
index 0000000000..838f1babf7
--- /dev/null
+++ b/src/inc/gcinfoencoder.h
@@ -0,0 +1,552 @@
+// 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.
+/*****************************************************************
+ *
+ * GC Information Encoding API
+ *
+ *****************************************************************/
+
+/*****************************************************************
+
+ ENCODING LAYOUT
+
+ 1. Header
+
+ Slim Header for simple and common cases:
+ - EncodingType[Slim]
+ - ReturnKind (Fat: 2 bits)
+ - CodeLength
+ - NumCallSites (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED)
+
+ Fat Header for other cases:
+ - EncodingType[Fat]
+ - Flag: isVarArg,
+ hasSecurityObject,
+ hasGSCookie,
+ hasPSPSymStackSlot,
+ hasGenericsInstContextStackSlot,
+ hasStackBaseregister,
+ wantsReportOnlyLeaf,
+ hasSizeOfEditAndContinuePreservedArea
+ hasReversePInvokeFrame,
+ - ReturnKind (Fat: 4 bits)
+ - CodeLength
+ - Prolog (if hasSecurityObject || hasGenericsInstContextStackSlot || hasGSCookie)
+ - Epilog (if hasGSCookie)
+ - SecurityObjectStackSlot (if any)
+ - GSCookieStackSlot (if any)
+ - PSPSymStackSlot (if any)
+ - GenericsInstContextStackSlot (if any)
+ - StackBaseRegister (if any)
+ - SizeOfEditAndContinuePreservedArea (if any)
+ - ReversePInvokeFrameSlot (if any)
+ - SizeOfStackOutgoingAndScratchArea (#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA)
+ - NumCallSites (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED)
+ - NumInterruptibleRanges
+
+ 2. Call sites offsets (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED)
+ 3. Fully-interruptible ranges
+ 4. Slot table
+ 5. GC state at call sites (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED)
+ 6. GC state at try clauses (#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED)
+ 7. Chunk pointers
+ 8. Chunk encodings
+
+
+ STANDALONE_BUILD
+
+ The STANDALONE_BUILD switch can be used to build the GcInfoEncoder library
+ independently by clients outside the CoreClr tree.
+
+ The GcInfo library uses some custom data-structures (ex: ArrayList, SimplerHashTable)
+ and includes some utility libraries (ex: UtilCode) which pull in several other
+ headers with considerable unrelated content. Rather than porting all the
+ utility code to suite other clients, the STANDALONE_BUILD switch can be used
+ to include only the minimal set of headers specific to GcInfo encodings.
+
+ Clients of STANDALONE_BUILD will likely use standard library
+ implementations of data-structures like ArrayList, HashMap etc., in place
+ of the custom implementation currently used by GcInfoEncoder.
+
+ Rather than spew the GcInfoEnoder code with
+ #ifdef STANDALONE_BUILD ... #else .. #endif blocks, we include a special
+ header GcInfoUtil.h in STANDALONE_BUILD mode. GcInfoUtil.h is expected to
+ supply the interface/implementation for the data-structures and utilities
+ used by GcInfoEncoder. This header should be provided by the clients doing
+ the standalone build in their source tree.
+
+*****************************************************************/
+
+
+#ifndef __GCINFOENCODER_H__
+#define __GCINFOENCODER_H__
+
+#ifdef STANDALONE_BUILD
+#include <wchar.h>
+#include <stdio.h>
+#include "GcInfoUtil.h"
+#include "corjit.h"
+#else
+#include <windows.h>
+#include <wchar.h>
+#include <stdio.h>
+#include "corjit.h"
+#include "iallocator.h"
+#include "gcinfoarraylist.h"
+#include "stdmacros.h"
+#include "eexcp.h"
+#endif
+
+#include "gcinfotypes.h"
+
+#ifdef MEASURE_GCINFO
+struct GcInfoSize
+{
+ size_t TotalSize;
+
+ size_t NumMethods;
+ size_t NumCallSites;
+ size_t NumRanges;
+ size_t NumRegs;
+ size_t NumStack;
+ size_t NumEh;
+ size_t NumTransitions;
+ size_t SizeOfCode;
+
+ size_t FlagsSize;
+ size_t RetKindSize;
+ size_t CodeLengthSize;
+ size_t ProEpilogSize;
+ size_t SecObjSize;
+ size_t GsCookieSize;
+ size_t PspSymSize;
+ size_t GenericsCtxSize;
+ size_t StackBaseSize;
+ size_t ReversePInvokeFrameSize;
+ size_t FixedAreaSize;
+ size_t NumCallSitesSize;
+ size_t NumRangesSize;
+ size_t CallSitePosSize;
+ size_t RangeSize;
+ size_t NumRegsSize;
+ size_t NumStackSize;
+ size_t RegSlotSize;
+ size_t StackSlotSize;
+ size_t CallSiteStateSize;
+ size_t NumEhSize;
+ size_t EhPosSize;
+ size_t EhStateSize;
+ size_t ChunkPtrSize;
+ size_t ChunkMaskSize;
+ size_t ChunkFinalStateSize;
+ size_t ChunkTransitionSize;
+
+ GcInfoSize();
+ GcInfoSize& operator+=(const GcInfoSize& other);
+ void Log(DWORD level, const char * header);
+};
+#endif
+
+struct GcSlotDesc
+{
+ union
+ {
+ UINT32 RegisterNumber;
+ GcStackSlot Stack;
+ } Slot;
+ GcSlotFlags Flags;
+
+ BOOL IsRegister() const
+ {
+ return (Flags & GC_SLOT_IS_REGISTER);
+ }
+ BOOL IsInterior() const
+ {
+ return (Flags & GC_SLOT_INTERIOR);
+ }
+ BOOL IsPinned() const
+ {
+ return (Flags & GC_SLOT_PINNED);
+ }
+ BOOL IsUntracked() const
+ {
+ return (Flags & GC_SLOT_UNTRACKED);
+ }
+ BOOL IsDeleted() const
+ {
+ return (Flags & GC_SLOT_IS_DELETED);
+ }
+ void MarkDeleted()
+ {
+ Flags = (GcSlotFlags) (Flags | GC_SLOT_IS_DELETED);
+ }
+};
+
+class BitArray;
+class BitStreamWriter
+{
+public:
+ BitStreamWriter( IAllocator* pAllocator );
+
+ // bit 0 is the least significative bit
+ void Write( size_t data, UINT32 count );
+ void Write( BitArray& a, UINT32 count );
+
+ inline size_t GetBitCount()
+ {
+ return m_BitCount;
+ }
+
+ inline size_t GetByteCount()
+ {
+ return ( m_BitCount + 7 ) / 8;
+ }
+
+
+ void CopyTo( BYTE* buffer );
+ void Dispose();
+
+ //--------------------------------------------------------
+ // Compute the number of bits used to encode variable length numbers
+ // Uses base+1 bits at minimum
+ // Bits 0..(base-1) represent the encoded quantity
+ // If it doesn't fit, set bit #base to 1 and use base+1 more bits
+ //--------------------------------------------------------
+ static int SizeofVarLengthUnsigned( size_t n, UINT32 base );
+
+ //--------------------------------------------------------
+ // Encode variable length numbers
+ // Uses base+1 bits at minimum
+ // Bits 0..(base-1) represent the encoded quantity
+ // If it doesn't fit, set bit #base to 1 and use base+1 more bits
+ //--------------------------------------------------------
+ int EncodeVarLengthUnsigned( size_t n, UINT32 base );
+
+ //--------------------------------------------------------
+ // Signed quantities are encoded the same as unsigned
+ // The most relevant difference is that a number is considered
+ // to fit in base bits if the topmost bit of a base-long chunk
+ // matches the sign of the whole number
+ //--------------------------------------------------------
+ int EncodeVarLengthSigned( SSIZE_T n, UINT32 base );
+
+private:
+ class MemoryBlockList;
+ class MemoryBlock
+ {
+ friend class MemoryBlockList;
+ MemoryBlock* m_next;
+
+ public:
+ size_t Contents[];
+
+ inline MemoryBlock* Next()
+ {
+ return m_next;
+ }
+ };
+
+ class MemoryBlockList
+ {
+ MemoryBlock* m_head;
+ MemoryBlock* m_tail;
+
+ public:
+ MemoryBlockList();
+
+ inline MemoryBlock* Head()
+ {
+ return m_head;
+ }
+
+ MemoryBlock* AppendNew(IAllocator* allocator, size_t bytes);
+ void Dispose(IAllocator* allocator);
+ };
+
+ IAllocator* m_pAllocator;
+ size_t m_BitCount;
+ UINT32 m_FreeBitsInCurrentSlot;
+ MemoryBlockList m_MemoryBlocks;
+ const static int m_MemoryBlockSize = 128; // must be a multiple of the pointer size
+ size_t* m_pCurrentSlot; // bits are written through this pointer
+ size_t* m_OutOfBlockSlot; // sentinel value to determine when the block is full
+#ifdef _DEBUG
+ int m_MemoryBlocksCount;
+#endif
+
+private:
+ // Writes bits knowing that they will all fit in the current memory slot
+ inline void WriteInCurrentSlot( size_t data, UINT32 count )
+ {
+ data &= SAFE_SHIFT_LEFT(1, count) - 1;
+ data <<= (BITS_PER_SIZE_T - m_FreeBitsInCurrentSlot);
+ *m_pCurrentSlot |= data;
+ }
+
+ inline void AllocMemoryBlock()
+ {
+ _ASSERTE( IS_ALIGNED( m_MemoryBlockSize, sizeof( size_t ) ) );
+ MemoryBlock* pMemBlock = m_MemoryBlocks.AppendNew(m_pAllocator, m_MemoryBlockSize);
+
+ m_pCurrentSlot = pMemBlock->Contents;
+ m_OutOfBlockSlot = m_pCurrentSlot + m_MemoryBlockSize / sizeof( size_t );
+
+#ifdef _DEBUG
+ m_MemoryBlocksCount++;
+#endif
+
+ }
+
+ inline void InitCurrentSlot()
+ {
+ m_FreeBitsInCurrentSlot = BITS_PER_SIZE_T;
+ *m_pCurrentSlot = 0;
+ }
+};
+
+
+typedef UINT32 GcSlotId;
+
+
+inline UINT32 GetNormCodeOffsetChunk(UINT32 normCodeOffset)
+{
+ return normCodeOffset / NUM_NORM_CODE_OFFSETS_PER_CHUNK;
+}
+
+inline UINT32 GetCodeOffsetChunk(UINT32 codeOffset)
+{
+ return (NORMALIZE_CODE_OFFSET(codeOffset)) / NUM_NORM_CODE_OFFSETS_PER_CHUNK;
+}
+
+enum GENERIC_CONTEXTPARAM_TYPE
+{
+ GENERIC_CONTEXTPARAM_NONE = 0,
+ GENERIC_CONTEXTPARAM_MT = 1,
+ GENERIC_CONTEXTPARAM_MD = 2,
+ GENERIC_CONTEXTPARAM_THIS = 3,
+};
+
+extern void DECLSPEC_NORETURN ThrowOutOfMemory();
+
+class GcInfoEncoder
+{
+public:
+ typedef void (*NoMemoryFunction)(void);
+
+ GcInfoEncoder(
+ ICorJitInfo* pCorJitInfo,
+ CORINFO_METHOD_INFO* pMethodInfo,
+ IAllocator* pJitAllocator,
+ NoMemoryFunction pNoMem = ::ThrowOutOfMemory
+ );
+
+ struct LifetimeTransition
+ {
+ UINT32 CodeOffset;
+ GcSlotId SlotId;
+ BYTE BecomesLive;
+ BYTE IsDeleted;
+ };
+
+
+#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
+ void DefineCallSites(UINT32* pCallSites, BYTE* pCallSiteSizes, UINT32 numCallSites);
+#endif
+
+ //------------------------------------------------------------------------
+ // Interruptibility
+ //------------------------------------------------------------------------
+
+ // An instruction at offset x will be interruptible
+ // if-and-only-if startInstructionOffset <= x < startInstructionOffset+length
+ void DefineInterruptibleRange( UINT32 startInstructionOffset, UINT32 length );
+
+
+ //------------------------------------------------------------------------
+ // Slot information
+ //------------------------------------------------------------------------
+
+ //
+ // If spOffset is relative to the current SP, spOffset must be non-negative.
+ // If spOffset is relative to the SP of the caller (same as SP at the method entry and exit)
+ // Negative offsets describe GC refs in the local and outgoing areas.
+ // Positive offsets describe GC refs in the scratch area
+ // Note that if the dynamic allocation area is resized, the outgoing area will not be valid anymore
+ // Old slots must be declared dead and new ones can be defined.
+ // It's up to the JIT to do the right thing. We don't enforce this.
+
+ GcSlotId GetRegisterSlotId( UINT32 regNum, GcSlotFlags flags );
+ GcSlotId GetStackSlotId( INT32 spOffset, GcSlotFlags flags, GcStackSlotBase spBase = GC_CALLER_SP_REL );
+
+ //
+ // After a FinalizeSlotIds is called, no more slot definitions can be made.
+ // FinalizeSlotIds must be called once and only once before calling Build()
+ //
+ void FinalizeSlotIds();
+
+
+ //------------------------------------------------------------------------
+ // Fully-interruptible information
+ //------------------------------------------------------------------------
+
+ //
+ // For inputs, pass zero as offset
+ //
+
+ // Indicates that the GC state of slot "slotId" becomes (and remains, until another transition)
+ // "slotState" after the instruction preceding "instructionOffset" (so it is first in this state when
+ // the IP of a suspended thread is at this instruction offset).
+
+ void SetSlotState( UINT32 instructionOffset,
+ GcSlotId slotId,
+ GcSlotState slotState
+ );
+
+
+ //------------------------------------------------------------------------
+ // ReturnKind
+ //------------------------------------------------------------------------
+
+ void SetReturnKind(ReturnKind returnKind);
+
+ //------------------------------------------------------------------------
+ // Miscellaneous method information
+ //------------------------------------------------------------------------
+
+ void SetSecurityObjectStackSlot( INT32 spOffset );
+ void SetPrologSize( UINT32 prologSize );
+ void SetGSCookieStackSlot( INT32 spOffsetGSCookie, UINT32 validRangeStart, UINT32 validRangeEnd );
+ void SetPSPSymStackSlot( INT32 spOffsetPSPSym );
+ void SetGenericsInstContextStackSlot( INT32 spOffsetGenericsContext, GENERIC_CONTEXTPARAM_TYPE type);
+ void SetReversePInvokeFrameSlot(INT32 spOffset);
+ void SetIsVarArg();
+ void SetCodeLength( UINT32 length );
+
+ // Optional in the general case. Required if the method uses GC_FRAMEREG_REL stack slots
+ void SetStackBaseRegister( UINT32 registerNumber );
+
+ // Number of slots preserved during EnC remap
+ void SetSizeOfEditAndContinuePreservedArea( UINT32 size );
+
+ // Used to only report a frame once for the leaf function/funclet
+ // instead of once for each live function/funclet on the stack.
+ // Called only by RyuJIT (not JIT64)
+ void SetWantsReportOnlyLeaf();
+
+#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
+ void SetSizeOfStackOutgoingAndScratchArea( UINT32 size );
+#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
+
+
+ //------------------------------------------------------------------------
+ // Encoding
+ //------------------------------------------------------------------------
+
+ //
+ // Build() encodes GC information into temporary buffers.
+ // The method description cannot change after Build is called
+ //
+ void Build();
+
+ //
+ // Write encoded information to its final destination and frees temporary buffers.
+ // The encoder shouldn't be used anymore after calling this method.
+ // It returns a pointer to the destination buffer, which address is byte-aligned
+ //
+ BYTE* Emit();
+
+private:
+
+ friend int __cdecl CompareLifetimeTransitionsByOffsetThenSlot(const void*, const void*);
+ friend int CompareLifetimeTransitionsByChunk(const void*, const void*);
+
+
+ struct InterruptibleRange
+ {
+ UINT32 NormStartOffset;
+ UINT32 NormStopOffset;
+ };
+
+ ICorJitInfo* m_pCorJitInfo;
+ CORINFO_METHOD_INFO* m_pMethodInfo;
+ IAllocator* m_pAllocator;
+ NoMemoryFunction m_pNoMem;
+
+#ifdef _DEBUG
+ const char *m_MethodName, *m_ModuleName;
+#endif
+
+ BitStreamWriter m_Info1; // Used for everything except for chunk encodings
+ BitStreamWriter m_Info2; // Used for chunk encodings
+
+ GcInfoArrayList<InterruptibleRange, 8> m_InterruptibleRanges;
+ GcInfoArrayList<LifetimeTransition, 64> m_LifetimeTransitions;
+
+ bool m_IsVarArg;
+ bool m_WantsReportOnlyLeaf;
+ INT32 m_SecurityObjectStackSlot;
+ INT32 m_GSCookieStackSlot;
+ UINT32 m_GSCookieValidRangeStart;
+ UINT32 m_GSCookieValidRangeEnd;
+ INT32 m_PSPSymStackSlot;
+ INT32 m_GenericsInstContextStackSlot;
+ GENERIC_CONTEXTPARAM_TYPE m_contextParamType;
+ ReturnKind m_ReturnKind;
+ UINT32 m_CodeLength;
+ UINT32 m_StackBaseRegister;
+ UINT32 m_SizeOfEditAndContinuePreservedArea;
+ INT32 m_ReversePInvokeFrameSlot;
+ InterruptibleRange* m_pLastInterruptibleRange;
+
+#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
+ UINT32 m_SizeOfStackOutgoingAndScratchArea;
+#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
+
+ void * eeAllocGCInfo (size_t blockSize);
+
+private:
+
+ friend class EncoderCheckState;
+
+ static const UINT32 m_SlotTableInitialSize = 32;
+ UINT32 m_SlotTableSize;
+ UINT32 m_NumSlots;
+ GcSlotDesc *m_SlotTable;
+
+#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
+ UINT32* m_pCallSites;
+ BYTE* m_pCallSiteSizes;
+ UINT32 m_NumCallSites;
+#endif // PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
+
+ void GrowSlotTable();
+
+ void WriteSlotStateVector(BitStreamWriter &writer, const BitArray& vector);
+
+ UINT32 SizeofSlotStateVarLengthVector(const BitArray& vector, UINT32 baseSkip, UINT32 baseRun);
+ void SizeofSlotStateVarLengthVector(const BitArray& vector, UINT32 baseSkip, UINT32 baseRun, UINT32 * pSizeofSimple, UINT32 * pSizeofRLE, UINT32 * pSizeofRLENeg);
+ UINT32 WriteSlotStateVarLengthVector(BitStreamWriter &writer, const BitArray& vector, UINT32 baseSkip, UINT32 baseRun);
+
+ bool IsAlwaysScratch(GcSlotDesc &slot);
+
+ // Assumes that "*ppTransitions" is has size "numTransitions", is sorted by CodeOffset then by SlotId,
+ // and that "*ppEndTransitions" points one beyond the end of the array. If "*ppTransitions" contains
+ // any dead/live transitions pairs for the same CodeOffset and SlotID, removes those, by allocating a
+ // new array, and copying the non-removed elements into it. If it does this, sets "*ppTransitions" to
+ // point to the new array, "*pNumTransitions" to its shorted length, and "*ppEndTransitions" to
+ // point one beyond the used portion of this array.
+ void EliminateRedundantLiveDeadPairs(LifetimeTransition** ppTransitions,
+ size_t* pNumTransitions,
+ LifetimeTransition** ppEndTransitions);
+
+#ifdef _DEBUG
+ bool m_IsSlotTableFrozen;
+#endif
+
+#ifdef MEASURE_GCINFO
+ GcInfoSize m_CurrentMethodSize;
+#endif
+};
+
+#endif // !__GCINFOENCODER_H__