-
Notifications
You must be signed in to change notification settings - Fork 4.9k
/
Copy pathCachedInterfaceDispatchPal.h
195 lines (166 loc) · 7.17 KB
/
CachedInterfaceDispatchPal.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#ifndef __CACHEDINTERFACEDISPATCHPAL_H__
#define __CACHEDINTERFACEDISPATCHPAL_H__
#ifdef FEATURE_CACHED_INTERFACE_DISPATCH
extern "C" void RhpInitialInterfaceDispatch();
bool InterfaceDispatch_InitializePal();
// Allocate memory aligned at sizeof(void*)*2 boundaries
void *InterfaceDispatch_AllocDoublePointerAligned(size_t size);
// Allocate memory aligned at sizeof(void*) boundaries
void *InterfaceDispatch_AllocPointerAligned(size_t size);
enum Flags
{
// The low 2 bits of the m_pCache pointer are treated specially so that we can avoid the need for
// extra fields on this type.
// OR if the m_pCache value is less than 0x1000 then this is a vtable offset and should be used as such
IDC_CachePointerPointsIsVTableOffset = 0x2,
IDC_CachePointerPointsAtCache = 0x0,
IDC_CachePointerMask = 0x3,
IDC_CachePointerMaskShift = 0x2,
};
enum class DispatchCellType
{
InterfaceAndSlot = 0x0,
VTableOffset = 0x2,
};
struct DispatchCellInfo
{
private:
static DispatchCellType CellTypeFromToken(DispatchToken token)
{
if (token.IsThisToken())
{
return DispatchCellType::VTableOffset;
}
return DispatchCellType::InterfaceAndSlot;
}
public:
DispatchCellInfo(DispatchToken token, bool hasCache) :
CellType(CellTypeFromToken(token)),
Token(token),
HasCache(hasCache ? 1 : 0)
{
}
const DispatchCellType CellType;
const DispatchToken Token;
uintptr_t GetVTableOffset() const
{
if (CellType == DispatchCellType::VTableOffset)
{
// The vtable offset is stored in a pointer sized field, but actually represents 2 values.
// 1. The offset of the first indirection to use. which is stored in the upper half of the
// pointer sized field (bits 16-31 of a 32 bit pointer, or bits 32-63 of a 64 bit pointer).
//
// 2. The offset of the second indirection, which is a stored is the upper half of the lower
// half of the pointer size field (bits 8-15 of a 32 bit pointer, or bits 16-31 of a 64
// bit pointer) This second offset is always less than 255, so we only really need a single
// byte, and the assembly code on some architectures may take a dependency on that
// so the VTableOffsetToSlot function has a mask to ensure that it is only ever a single byte.
uint32_t slot = Token.GetSlotNumber();
unsigned offsetOfIndirection = MethodTable::GetVtableOffset() + MethodTable::GetIndexOfVtableIndirection(slot) * TARGET_POINTER_SIZE;
unsigned offsetAfterIndirection = MethodTable::GetIndexAfterVtableIndirection(slot) * TARGET_POINTER_SIZE;
uintptr_t offsetOfIndirectionPortion = (((uintptr_t)offsetOfIndirection) << ((TARGET_POINTER_SIZE * 8) / 2));
uintptr_t offsetAfterIndirectionPortion = (((uintptr_t)offsetAfterIndirection) << ((TARGET_POINTER_SIZE * 8) / 4));
uintptr_t flagPortion = (uintptr_t)IDC_CachePointerPointsIsVTableOffset;
uintptr_t result = offsetOfIndirectionPortion | offsetAfterIndirectionPortion | flagPortion;
_ASSERTE(slot == VTableOffsetToSlot(result));
return result;
}
return 0;
}
static unsigned VTableOffsetToSlot(uintptr_t vtableOffset)
{
// See comment in GetVTableOffset() for what we're doing here.
unsigned offsetOfIndirection = (unsigned)(vtableOffset >> ((TARGET_POINTER_SIZE * 8) / 2));
unsigned offsetAfterIndirection = (unsigned)(vtableOffset >> ((TARGET_POINTER_SIZE * 8) / 4)) & 0xFF;
unsigned slotGroupPerChunk = (offsetOfIndirection - MethodTable::GetVtableOffset()) / TARGET_POINTER_SIZE;
unsigned slot = (slotGroupPerChunk * VTABLE_SLOTS_PER_CHUNK) + (offsetAfterIndirection / TARGET_POINTER_SIZE);
return slot;
}
const uint8_t HasCache = 0;
};
struct InterfaceDispatchCacheHeader
{
private:
enum Flags
{
CH_TypeAndSlotIndex = 0x0,
CH_MetadataToken = 0x1,
CH_Mask = 0x3,
CH_Shift = 0x2,
};
public:
void Initialize(DispatchToken token)
{
m_token = token;
}
void Initialize(const DispatchCellInfo *pNewCellInfo)
{
m_token = pNewCellInfo->Token;
}
DispatchCellInfo GetDispatchCellInfo()
{
DispatchCellInfo cellInfo(m_token, true);
return cellInfo;
}
private:
DispatchToken m_token;
TADDR padding; // Ensure that the size of this structure is a multiple of 2 pointers
};
// One of these is allocated per interface call site. It holds the stub to call, data to pass to that stub
// (cache information) and the interface contract, i.e. the interface type and slot being called.
struct InterfaceDispatchCell
{
// The first two fields must remain together and at the beginning of the structure. This is due to the
// synchronization requirements of the code that updates these at runtime and the instructions generated
// by the binder for interface call sites.
TADDR m_pStub; // Call this code to execute the interface dispatch
Volatile<TADDR> m_pCache; // Context used by the stub above (one or both of the low two bits are set
// for initial dispatch, and if not set, using this as a cache pointer or
// as a vtable offset.)
DispatchCellInfo GetDispatchCellInfo()
{
// Capture m_pCache into a local for safe access (this is a volatile read of a value that may be
// modified on another thread while this function is executing.)
TADDR cachePointerValue = m_pCache;
if (IsCache(cachePointerValue))
{
return ((InterfaceDispatchCacheHeader*)cachePointerValue)->GetDispatchCellInfo();
}
else if (DispatchToken::IsCachedInterfaceDispatchToken(cachePointerValue))
{
return DispatchCellInfo(DispatchToken::FromCachedInterfaceDispatchToken(cachePointerValue), false);
}
else
{
_ASSERTE(IsVTableOffset(cachePointerValue));
unsigned slot = DispatchCellInfo::VTableOffsetToSlot(cachePointerValue);
return DispatchCellInfo(DispatchToken::CreateDispatchToken(slot), false);
}
}
static bool IsCache(TADDR value)
{
return (value & IDC_CachePointerMask) == 0;
}
static bool IsVTableOffset(TADDR value)
{
return (value & IDC_CachePointerPointsIsVTableOffset) == IDC_CachePointerPointsIsVTableOffset;
}
InterfaceDispatchCacheHeader* GetCache() const
{
// Capture m_pCache into a local for safe access (this is a volatile read of a value that may be
// modified on another thread while this function is executing.)
TADDR cachePointerValue = m_pCache;
if (IsCache(cachePointerValue))
{
return (InterfaceDispatchCacheHeader*)cachePointerValue;
}
else
{
return nullptr;
}
}
};
#endif // FEATURE_CACHED_INTERFACE_DISPATCH
#endif // __CACHEDINTERFACEDISPATCHPAL_H__