diff options
Diffstat (limited to 'src/inc/gcrefmap.h')
-rw-r--r-- | src/inc/gcrefmap.h | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/src/inc/gcrefmap.h b/src/inc/gcrefmap.h new file mode 100644 index 0000000000..0e07074cda --- /dev/null +++ b/src/inc/gcrefmap.h @@ -0,0 +1,247 @@ +// 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. + + +#ifndef _GCREFMAP_H_ +#define _GCREFMAP_H_ + +#include "sigbuilder.h" + +// +// The GCRef map is used to encode GC type of arguments for callsites. Logically, it is sequence <pos, token> where pos is +// position of the reference in the stack frame and token is type of GC reference (one of GCREFMAP_XXX values). +// +// - The encoding always starts at the byte boundary. The high order bit of each byte is used to signal end of the encoding +// stream. The last byte has the high order bit zero. It means that there are 7 useful bits in each byte. +// - "pos" is always encoded as delta from previous pos. +// - The basic encoding unit is two bits. Values 0, 1 and 2 are the common constructs (skip single slot, GC reference, interior +// pointer). Value 3 means that extended encoding follows. +// - The extended information is integer encoded in one or more four bit blocks. The high order bit of the four bit block is +// used to signal the end. +// - For x86, the encoding starts by size of the callee poped stack. The size is encoded using the same mechanism as above (two bit +// basic encoding, with extended encoding for large values). + +///////////////////////////////////////////////////////////////////////////////////// +// A utility class to encode sequence of GC summaries for a callsite + +class GCRefMapBuilder +{ + int m_PendingByte; // Pending value, not yet written out + + int m_Bits; // Number of bits in pending byte. Note that the trailing zero bits are not written out, + // so this can be more than 7. + + int m_Pos; // Current position + + SigBuilder m_SigBuilder; + + // Append single bit to the stream + void AppendBit(int bit) + { + if (bit != 0) + { + while (m_Bits >= 7) + { + m_SigBuilder.AppendByte((BYTE)(m_PendingByte | 0x80)); + m_PendingByte = 0; + m_Bits -= 7; + } + + m_PendingByte |= (1 << m_Bits); + } + + m_Bits++; + } + + void AppendTwoBit(int bits) + { + AppendBit(bits & 1); + AppendBit(bits >> 1); + } + + void AppendInt(int val) + { + do { + AppendBit(val & 1); + AppendBit((val >> 1) & 1); + AppendBit((val >> 2) & 1); + + val >>= 3; + + AppendBit((val != 0) ? 1 : 0); + } + while (val != 0); + } + +public: + GCRefMapBuilder() + : m_PendingByte(0), m_Bits(0), m_Pos(0) + { + } + +#ifdef _TARGET_X86_ + void WriteStackPop(int stackPop) + { + if (stackPop < 3) + { + AppendTwoBit(stackPop); + } + else + { + AppendTwoBit(3); + AppendInt(stackPop - 3); + } + } +#endif + + void WriteToken(int pos, int gcRefMapToken) + { + int posDelta = pos - m_Pos; + m_Pos = pos + 1; + + if (posDelta != 0) + { + if (posDelta < 4) + { + // Skipping by one slot at a time for small deltas produces smaller encoding. + while (posDelta > 0) + { + AppendTwoBit(0); + posDelta--; + } + } + else + { + AppendTwoBit(3); + AppendInt((posDelta - 4) << 1); + } + } + + if (gcRefMapToken < 3) + { + AppendTwoBit(gcRefMapToken); + } + else + { + AppendTwoBit(3); + AppendInt(((gcRefMapToken - 3) << 1) | 1); + } + } + + void Flush() + { + if ((m_PendingByte & 0x7F) != 0 || m_Pos == 0) + m_SigBuilder.AppendByte((BYTE)(m_PendingByte & 0x7F)); + + m_PendingByte = 0; + m_Bits = 0; + + m_Pos = 0; + } + + PVOID GetBlob(DWORD * pdwLength) + { + return m_SigBuilder.GetSignature(pdwLength); + } + + DWORD GetBlobLength() + { + return m_SigBuilder.GetSignatureLength(); + } +}; + +///////////////////////////////////////////////////////////////////////////////////// +// A utility class to decode a GC summary for a callsite + +class GCRefMapDecoder +{ + PTR_BYTE m_pCurrentByte; + int m_PendingByte; + int m_Pos; + + FORCEINLINE int GetBit() + { + int x = m_PendingByte; + if (x & 0x80) + { + x = *m_pCurrentByte++; + x |= ((x & 0x80) << 7); + } + m_PendingByte = x >> 1; + return x & 1; + } + + FORCEINLINE int GetTwoBit() + { + int result = GetBit(); + result |= GetBit() << 1; + return result; + } + + int GetInt() + { + int result = 0; + + int bit = 0; + do { + result |= GetBit() << (bit++); + result |= GetBit() << (bit++); + result |= GetBit() << (bit++); + } + while (GetBit() != 0); + + return result; + } + +public: + GCRefMapDecoder(PTR_BYTE pBlob) + : m_pCurrentByte(pBlob), m_PendingByte(0x80), m_Pos(0) + { + } + + BOOL AtEnd() + { + return m_PendingByte == 0; + } + +#ifdef _TARGET_X86_ + UINT ReadStackPop() + { + int x = GetTwoBit(); + + if (x == 3) + x = GetInt() + 3; + + return x; + } +#endif + + int CurrentPos() + { + return m_Pos; + } + + int ReadToken() + { + int val = GetTwoBit(); + if (val == 3) + { + int ext = GetInt(); + if ((ext & 1) == 0) + { + m_Pos += (ext >> 1) + 4; + return GCREFMAP_SKIP; + } + else + { + m_Pos++; + return (ext >> 1) + 3; + } + } + m_Pos++; + return val; + } +}; + +#endif // _GCREFMAP_H_ |