@@ -171,6 +171,7 @@ void StubHelpers::Init()
171171
172172#ifdef FEATURE_COMINTEROP
173173
174+ // Clears FP state on x86 for compatibility with VB6.
174175FORCEINLINE static void GetCOMIPFromRCW_ClearFP ()
175176{
176177 LIMITED_METHOD_CONTRACT;
@@ -190,91 +191,112 @@ FORCEINLINE static void GetCOMIPFromRCW_ClearFP()
190191#endif // TARGET_X86
191192}
192193
193- FORCEINLINE static SOleTlsData * GetOrCreateOleTlsData ()
194+ FORCEINLINE static SOleTlsData* TryGetOleTlsData ()
194195{
195196 LIMITED_METHOD_CONTRACT;
196197
197- SOleTlsData *pOleTlsData;
198198#ifdef TARGET_X86
199199 // This saves 1 memory instruction over NtCurretTeb()->ReservedForOle because
200200 // NtCurrentTeb() reads _TEB.NtTib.Self which is the same as what FS:0 already
201201 // points to.
202- pOleTlsData = (SOleTlsData *)(ULONG_PTR)__readfsdword (offsetof (TEB, ReservedForOle));
202+ return (SOleTlsData*)(ULONG_PTR)__readfsdword (offsetof (TEB, ReservedForOle));
203203#else // TARGET_X86
204- pOleTlsData = (SOleTlsData *) NtCurrentTeb ()-> ReservedForOle ;
204+ return (SOleTlsData*) ClrTeb::GetOleReservedPtr () ;
205205#endif // TARGET_X86
206- if (pOleTlsData == NULL )
206+ }
207+
208+ static SOleTlsData* GetOrCreateOleTlsData ()
209+ {
210+ CONTRACTL
207211 {
208- pOleTlsData = (SOleTlsData *)SetupOleContext ();
212+ NOTHROW;
213+ GC_TRIGGERS;
214+ MODE_COOPERATIVE;
209215 }
216+ CONTRACT_END;
217+
218+ SOleTlsData* pOleTlsData = TryGetOleTlsData ();
219+ if (pOleTlsData == NULL )
220+ pOleTlsData = (SOleTlsData*)SetupOleContext ();
221+
210222 return pOleTlsData;
211223}
212224
213- FORCEINLINE static IUnknown * GetCOMIPFromRCW_GetIUnknownFromRCWCache (RCW *pRCW, MethodTable * pItfMT )
225+ FORCEINLINE static void * GetCOMIPFromRCW_GetTarget (IUnknown *pUnk, CLRToCOMCallInfo *pComInfo )
214226{
215227 LIMITED_METHOD_CONTRACT;
216228
217- // The code in this helper is the "fast path" that used to be generated directly
218- // to compiled ML stubs. The idea is to aim for an efficient RCW cache hit.
219- SOleTlsData * pOleTlsData = GetOrCreateOleTlsData ();
229+ LPVOID* lpVtbl = *(LPVOID **)pUnk;
230+ LPVOID tgt = lpVtbl[pComInfo->m_cachedComSlot ];
231+ if (tgt != NULL )
232+ GetCOMIPFromRCW_ClearFP ();
233+
234+ return tgt;
235+ }
236+
237+ FORCEINLINE static IUnknown* GetCOMIPFromRCW_GetTargetFromRCWCache (SOleTlsData* pOleTlsData, RCW* pRCW, CLRToCOMCallInfo* pComInfo, void ** ppTarget)
238+ {
239+ LIMITED_METHOD_CONTRACT;
240+ _ASSERTE (pOleTlsData != NULL );
241+ _ASSERTE (pRCW != NULL );
242+ _ASSERTE (pComInfo != NULL );
243+ _ASSERTE (ppTarget != NULL );
220244
221245 // test for free-threaded after testing for context match to optimize for apartment-bound objects
222246 if (pOleTlsData->pCurrentCtx == pRCW->GetWrapperCtxCookie () || pRCW->IsFreeThreaded ())
223247 {
224248 for (int i = 0 ; i < INTERFACE_ENTRY_CACHE_SIZE; i++)
225249 {
226- if (pRCW->m_aInterfaceEntries [i].m_pMT == pItfMT )
250+ if (pRCW->m_aInterfaceEntries [i].m_pMT == pComInfo-> m_pInterfaceMT )
227251 {
228- return pRCW->m_aInterfaceEntries [i].m_pUnknown ;
252+ IUnknown* pUnk = pRCW->m_aInterfaceEntries [i].m_pUnknown ;
253+ if (pUnk != NULL )
254+ {
255+ void * targetMaybe = GetCOMIPFromRCW_GetTarget (pUnk, pComInfo);
256+ if (targetMaybe != NULL )
257+ {
258+ *ppTarget = targetMaybe;
259+ return pUnk;
260+ }
261+ }
229262 }
230263 }
231264 }
232265
233266 return NULL ;
234267}
235268
236- FORCEINLINE static void *GetCOMIPFromRCW_GetTarget (IUnknown *pUnk, CLRToCOMCallInfo *pComInfo)
237- {
238- LIMITED_METHOD_CONTRACT;
239-
240- LPVOID *lpVtbl = *(LPVOID **)pUnk;
241- return lpVtbl[pComInfo->m_cachedComSlot ];
242- }
243-
244269// ==================================================================================================================
245270// The GetCOMIPFromRCW helper exists in four specialized versions to optimize CLR->COM perf. Please be careful when
246271// changing this code as one of these methods is executed as part of every CLR->COM call so every instruction counts.
247272// ==================================================================================================================
248273
249-
250274#include < optsmallperfcritical.h>
251275
252- // This helper can handle any CLR->COM call, it supports hosting,
253- // and clears FP state on x86 for compatibility with VB6.
254- FCIMPL3 (IUnknown*, StubHelpers::GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* pMD, void **ppTarget)
276+ // This helper can handle any CLR->COM call.
277+ FCIMPL3 (IUnknown*, StubHelpers::GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* pMD, void ** ppTarget)
255278{
256279 CONTRACTL
257280 {
258281 FCALL_CHECK;
259- PRECONDITION (pMD->IsCLRToCOMCall () || pMD->IsEEImpl ());
282+ PRECONDITION (pSrcUNSAFE != NULL );
283+ PRECONDITION (pMD != NULL && (pMD->IsCLRToCOMCall () || pMD->IsEEImpl ()));
284+ PRECONDITION (ppTarget != NULL );
260285 }
261286 CONTRACTL_END;
262287
288+ // See the slow path as well, StubHelpers_GetCOMIPFromRCWSlow. The first part of that
289+ // function is identical to this one, but it handles the case where the OLE TLS
290+ // data hasn't been created yet.
263291 OBJECTREF pSrc = ObjectToOBJECTREF (pSrcUNSAFE);
264- CLRToCOMCallInfo * pComInfo = CLRToCOMCallInfo::FromMethodDesc (pMD);
265- RCW * pRCW = pSrc->PassiveGetSyncBlock ()->GetInteropInfoNoCreate ()->GetRawRCW ();
292+ CLRToCOMCallInfo* pComInfo = CLRToCOMCallInfo::FromMethodDesc (pMD);
293+ RCW* pRCW = pSrc->PassiveGetSyncBlock ()->GetInteropInfoNoCreate ()->GetRawRCW ();
266294 if (pRCW != NULL )
267295 {
268- IUnknown * pUnk = GetCOMIPFromRCW_GetIUnknownFromRCWCache (pRCW, pComInfo->m_pInterfaceMT );
269- if (pUnk != NULL )
270- {
271- *ppTarget = GetCOMIPFromRCW_GetTarget (pUnk, pComInfo);
272- if (*ppTarget != NULL )
273- {
274- GetCOMIPFromRCW_ClearFP ();
275- return pUnk;
276- }
277- }
296+ // This is the "fast path" for compiled ML stubs. The idea is to aim for an efficient RCW cache hit.
297+ SOleTlsData* pOleTlsData = TryGetOleTlsData ();
298+ if (pOleTlsData != NULL )
299+ return GetCOMIPFromRCW_GetTargetFromRCWCache (pOleTlsData, pRCW, pComInfo, ppTarget);
278300 }
279301 return NULL ;
280302}
@@ -296,12 +318,25 @@ extern "C" IUnknown* QCALLTYPE StubHelpers_GetCOMIPFromRCWSlow(QCall::ObjectHand
296318 OBJECTREF objRef = pSrc.Get ();
297319 GCPROTECT_BEGIN (objRef);
298320
321+ // This snippet exists to enable OLE TLS data creation that isn't possible on the fast path.
322+ // It is practically identical to the StubHelpers::GetCOMIPFromRCW FCALL, but in the event the OLE TLS
323+ // data on this thread hasn't occurred yet, we will create it. Since this is the slow path, trying the
324+ // cache again isn't a problem.
325+ SOleTlsData* pOleTlsData = GetOrCreateOleTlsData (); // Ensure OLE TLS data is created.
299326 CLRToCOMCallInfo* pComInfo = CLRToCOMCallInfo::FromMethodDesc (pMD);
327+ RCW* pRCW = objRef->PassiveGetSyncBlock ()->GetInteropInfoNoCreate ()->GetRawRCW ();
328+ if (pRCW != NULL )
329+ {
330+ IUnknown* pUnk = GetCOMIPFromRCW_GetTargetFromRCWCache (pOleTlsData, pRCW, pComInfo, ppTarget);
331+ if (pUnk != NULL )
332+ return pUnk;
333+ }
334+
335+ // Still not in the cache and we've ensured the OLE TLS data was created.
300336 SafeComHolder<IUnknown> pRetUnk = ComObject::GetComIPFromRCWThrowing (&objRef, pComInfo->m_pInterfaceMT );
301337 *ppTarget = GetCOMIPFromRCW_GetTarget (pRetUnk, pComInfo);
302338 _ASSERTE (*ppTarget != NULL );
303339
304- GetCOMIPFromRCW_ClearFP ();
305340 pIntf = pRetUnk.Extract ();
306341
307342 GCPROTECT_END ();
0 commit comments