| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | |
| 5 | |
| 6 | #ifndef _GCREFMAP_H_ |
| 7 | #define _GCREFMAP_H_ |
| 8 | |
| 9 | #include "sigbuilder.h" |
| 10 | |
| 11 | // |
| 12 | // The GCRef map is used to encode GC type of arguments for callsites. Logically, it is sequence <pos, token> where pos is |
| 13 | // position of the reference in the stack frame and token is type of GC reference (one of GCREFMAP_XXX values). |
| 14 | // |
| 15 | // - The encoding always starts at the byte boundary. The high order bit of each byte is used to signal end of the encoding |
| 16 | // stream. The last byte has the high order bit zero. It means that there are 7 useful bits in each byte. |
| 17 | // - "pos" is always encoded as delta from previous pos. |
| 18 | // - The basic encoding unit is two bits. Values 0, 1 and 2 are the common constructs (skip single slot, GC reference, interior |
| 19 | // pointer). Value 3 means that extended encoding follows. |
| 20 | // - The extended information is integer encoded in one or more four bit blocks. The high order bit of the four bit block is |
| 21 | // used to signal the end. |
| 22 | // - For x86, the encoding starts by size of the callee poped stack. The size is encoded using the same mechanism as above (two bit |
| 23 | // basic encoding, with extended encoding for large values). |
| 24 | |
| 25 | ///////////////////////////////////////////////////////////////////////////////////// |
| 26 | // A utility class to encode sequence of GC summaries for a callsite |
| 27 | |
| 28 | class GCRefMapBuilder |
| 29 | { |
| 30 | int m_PendingByte; // Pending value, not yet written out |
| 31 | |
| 32 | int m_Bits; // Number of bits in pending byte. Note that the trailing zero bits are not written out, |
| 33 | // so this can be more than 7. |
| 34 | |
| 35 | int m_Pos; // Current position |
| 36 | |
| 37 | SigBuilder m_SigBuilder; |
| 38 | |
| 39 | // Append single bit to the stream |
| 40 | void AppendBit(int bit) |
| 41 | { |
| 42 | if (bit != 0) |
| 43 | { |
| 44 | while (m_Bits >= 7) |
| 45 | { |
| 46 | m_SigBuilder.AppendByte((BYTE)(m_PendingByte | 0x80)); |
| 47 | m_PendingByte = 0; |
| 48 | m_Bits -= 7; |
| 49 | } |
| 50 | |
| 51 | m_PendingByte |= (1 << m_Bits); |
| 52 | } |
| 53 | |
| 54 | m_Bits++; |
| 55 | } |
| 56 | |
| 57 | void AppendTwoBit(int bits) |
| 58 | { |
| 59 | AppendBit(bits & 1); |
| 60 | AppendBit(bits >> 1); |
| 61 | } |
| 62 | |
| 63 | void AppendInt(int val) |
| 64 | { |
| 65 | do { |
| 66 | AppendBit(val & 1); |
| 67 | AppendBit((val >> 1) & 1); |
| 68 | AppendBit((val >> 2) & 1); |
| 69 | |
| 70 | val >>= 3; |
| 71 | |
| 72 | AppendBit((val != 0) ? 1 : 0); |
| 73 | } |
| 74 | while (val != 0); |
| 75 | } |
| 76 | |
| 77 | public: |
| 78 | GCRefMapBuilder() |
| 79 | : m_PendingByte(0), m_Bits(0), m_Pos(0) |
| 80 | { |
| 81 | } |
| 82 | |
| 83 | #ifdef _TARGET_X86_ |
| 84 | void WriteStackPop(int stackPop) |
| 85 | { |
| 86 | if (stackPop < 3) |
| 87 | { |
| 88 | AppendTwoBit(stackPop); |
| 89 | } |
| 90 | else |
| 91 | { |
| 92 | AppendTwoBit(3); |
| 93 | AppendInt(stackPop - 3); |
| 94 | } |
| 95 | } |
| 96 | #endif |
| 97 | |
| 98 | void WriteToken(int pos, int gcRefMapToken) |
| 99 | { |
| 100 | int posDelta = pos - m_Pos; |
| 101 | m_Pos = pos + 1; |
| 102 | |
| 103 | if (posDelta != 0) |
| 104 | { |
| 105 | if (posDelta < 4) |
| 106 | { |
| 107 | // Skipping by one slot at a time for small deltas produces smaller encoding. |
| 108 | while (posDelta > 0) |
| 109 | { |
| 110 | AppendTwoBit(0); |
| 111 | posDelta--; |
| 112 | } |
| 113 | } |
| 114 | else |
| 115 | { |
| 116 | AppendTwoBit(3); |
| 117 | AppendInt((posDelta - 4) << 1); |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | if (gcRefMapToken < 3) |
| 122 | { |
| 123 | AppendTwoBit(gcRefMapToken); |
| 124 | } |
| 125 | else |
| 126 | { |
| 127 | AppendTwoBit(3); |
| 128 | AppendInt(((gcRefMapToken - 3) << 1) | 1); |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | void Flush() |
| 133 | { |
| 134 | if ((m_PendingByte & 0x7F) != 0 || m_Pos == 0) |
| 135 | m_SigBuilder.AppendByte((BYTE)(m_PendingByte & 0x7F)); |
| 136 | |
| 137 | m_PendingByte = 0; |
| 138 | m_Bits = 0; |
| 139 | |
| 140 | m_Pos = 0; |
| 141 | } |
| 142 | |
| 143 | PVOID GetBlob(DWORD * pdwLength) |
| 144 | { |
| 145 | return m_SigBuilder.GetSignature(pdwLength); |
| 146 | } |
| 147 | |
| 148 | DWORD GetBlobLength() |
| 149 | { |
| 150 | return m_SigBuilder.GetSignatureLength(); |
| 151 | } |
| 152 | }; |
| 153 | |
| 154 | ///////////////////////////////////////////////////////////////////////////////////// |
| 155 | // A utility class to decode a GC summary for a callsite |
| 156 | |
| 157 | class GCRefMapDecoder |
| 158 | { |
| 159 | PTR_BYTE m_pCurrentByte; |
| 160 | int m_PendingByte; |
| 161 | int m_Pos; |
| 162 | |
| 163 | FORCEINLINE int GetBit() |
| 164 | { |
| 165 | int x = m_PendingByte; |
| 166 | if (x & 0x80) |
| 167 | { |
| 168 | x = *m_pCurrentByte++; |
| 169 | x |= ((x & 0x80) << 7); |
| 170 | } |
| 171 | m_PendingByte = x >> 1; |
| 172 | return x & 1; |
| 173 | } |
| 174 | |
| 175 | FORCEINLINE int GetTwoBit() |
| 176 | { |
| 177 | int result = GetBit(); |
| 178 | result |= GetBit() << 1; |
| 179 | return result; |
| 180 | } |
| 181 | |
| 182 | int GetInt() |
| 183 | { |
| 184 | int result = 0; |
| 185 | |
| 186 | int bit = 0; |
| 187 | do { |
| 188 | result |= GetBit() << (bit++); |
| 189 | result |= GetBit() << (bit++); |
| 190 | result |= GetBit() << (bit++); |
| 191 | } |
| 192 | while (GetBit() != 0); |
| 193 | |
| 194 | return result; |
| 195 | } |
| 196 | |
| 197 | public: |
| 198 | GCRefMapDecoder(PTR_BYTE pBlob) |
| 199 | : m_pCurrentByte(pBlob), m_PendingByte(0x80), m_Pos(0) |
| 200 | { |
| 201 | } |
| 202 | |
| 203 | BOOL AtEnd() |
| 204 | { |
| 205 | return m_PendingByte == 0; |
| 206 | } |
| 207 | |
| 208 | #ifdef _TARGET_X86_ |
| 209 | UINT ReadStackPop() |
| 210 | { |
| 211 | int x = GetTwoBit(); |
| 212 | |
| 213 | if (x == 3) |
| 214 | x = GetInt() + 3; |
| 215 | |
| 216 | return x; |
| 217 | } |
| 218 | #endif |
| 219 | |
| 220 | int CurrentPos() |
| 221 | { |
| 222 | return m_Pos; |
| 223 | } |
| 224 | |
| 225 | int ReadToken() |
| 226 | { |
| 227 | int val = GetTwoBit(); |
| 228 | if (val == 3) |
| 229 | { |
| 230 | int ext = GetInt(); |
| 231 | if ((ext & 1) == 0) |
| 232 | { |
| 233 | m_Pos += (ext >> 1) + 4; |
| 234 | return GCREFMAP_SKIP; |
| 235 | } |
| 236 | else |
| 237 | { |
| 238 | m_Pos++; |
| 239 | return (ext >> 1) + 3; |
| 240 | } |
| 241 | } |
| 242 | m_Pos++; |
| 243 | return val; |
| 244 | } |
| 245 | }; |
| 246 | |
| 247 | #endif // _GCREFMAP_H_ |
| 248 | |