xref: /trunk/main/bridges/source/cpp_uno/s5abi_macosx_aarch64/cpp2uno.cxx (revision a23f5aaf7731494d1484f0272a1646643fd459f5)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_bridges.hxx"
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <hash_map>
30 #include <libkern/OSCacheControl.h>
31 
32 #include <rtl/alloc.h>
33 #include <osl/mutex.hxx>
34 
35 #include <com/sun/star/uno/genfunc.hxx>
36 #include "com/sun/star/uno/RuntimeException.hpp"
37 #include <uno/data.h>
38 #include <typelib/typedescription.hxx>
39 
40 #include "bridges/cpp_uno/shared/bridge.hxx"
41 #include "bridges/cpp_uno/shared/cppinterfaceproxy.hxx"
42 #include "bridges/cpp_uno/shared/types.hxx"
43 #include "bridges/cpp_uno/shared/vtablefactory.hxx"
44 
45 #include "abi.hxx"
46 #include "share.hxx"
47 
48 using namespace ::osl;
49 using namespace ::rtl;
50 using namespace ::com::sun::star::uno;
51 
52 //==================================================================================================
53 
54 // Perform the UNO call
55 //
56 // We must convert the parameters stored in gpreg, fpreg and ovrflw to UNO
57 // arguments and call pThis->getUnoI()->pDispatcher.
58 //
59 // gpreg:  this, [gpr params x0..x7]   (the indirect-result ptr is x8, separate)
60 // fpreg:  [fpr params d0..d7]
61 // ovrflw: [gpr or fpr params (properly aligned)]
62 //
63 // On AArch64 a structure bigger than 16 bytes is returned via the buffer
64 // addressed by x8 (pIndirectReturn); 'this' is always x0 = gpreg[0].
65 // Simple types are returned in x0,x1 (int) or d0,d1 (fp); HFAs in d0..d3;
66 // non-HFA structures <= 16 bytes in x0,x1.
67 static typelib_TypeClass cpp2uno_call(
68     bridges::cpp_uno::shared::CppInterfaceProxy * pThis,
69     const typelib_TypeDescription * pMemberTypeDescr,
70     typelib_TypeDescriptionReference * pReturnTypeRef, // 0 indicates void return
71     sal_Int32 nParams, typelib_MethodParameter * pParams,
72     void ** gpreg, void ** fpreg, void ** ovrflw,
73     void * pIndirectReturn, // AArch64 x8 indirect-result pointer (0 if none)
74     sal_uInt64 * pRegisterReturn /* space for register return */ )
75 {
76     unsigned int nr_gpr = 0; //number of gpr registers used
77     unsigned int nr_fpr = 0; //number of fpr registers used
78 
79     // return
80     typelib_TypeDescription * pReturnTypeDescr = 0;
81     if (pReturnTypeRef)
82         TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef );
83 
84     void * pUnoReturn = 0;
85     void * pCppReturn = 0; // complex return ptr: if != 0 && != pUnoReturn, reconversion need
86 
87     if ( pReturnTypeDescr )
88     {
89         if ( aarch64::return_in_hidden_param( pReturnTypeRef ) )
90         {
91             // AArch64: the indirect-result pointer arrives in x8, NOT in the
92             // first general-purpose argument register (unlike x86-64 SysV).
93             // So we take it from pIndirectReturn and do NOT consume a gpreg
94             // slot here; 'this' still occupies gpreg[0] below.
95             pCppReturn = pIndirectReturn;
96 
97             pUnoReturn = ( bridges::cpp_uno::shared::relatesToInterfaceType( pReturnTypeDescr )
98                            ? alloca( pReturnTypeDescr->nSize )
99                            : pCppReturn ); // direct way
100         }
101         else
102             pUnoReturn = pRegisterReturn; // direct way for simple types
103     }
104 
105     // pop this (x0)
106     gpreg++;
107     nr_gpr++;
108 
109     // stack space
110     // parameters
111     void ** pUnoArgs = reinterpret_cast<void **>(alloca( 4 * sizeof(void *) * nParams ));
112     void ** pCppArgs = pUnoArgs + nParams;
113     // indizes of values this have to be converted (interface conversion cpp<=>uno)
114     sal_Int32 * pTempIndizes = reinterpret_cast<sal_Int32 *>(pUnoArgs + (2 * nParams));
115     // type descriptions for reconversions
116     typelib_TypeDescription ** ppTempParamTypeDescr = reinterpret_cast<typelib_TypeDescription **>(pUnoArgs + (3 * nParams));
117 
118     sal_Int32 nTempIndizes = 0;
119 
120     for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos )
121     {
122         const typelib_MethodParameter & rParam = pParams[nPos];
123 
124         int nUsedGPR = 0;
125         int nUsedFPR = 0;
126         aarch64::examine_argument( rParam.pTypeRef, false, nUsedGPR, nUsedFPR );
127         if ( !rParam.bOut && bridges::cpp_uno::shared::isSimpleType( rParam.pTypeRef ) ) // value
128         {
129             // A simple UNO type occupies exactly one register, GPR or FPR.
130             OSL_ASSERT( ( nUsedFPR == 1 && nUsedGPR == 0 ) || ( nUsedFPR == 0 && nUsedGPR == 1 ) );
131 
132             if ( nUsedFPR == 1 )
133             {
134                 if ( nr_fpr < aarch64::MAX_FPR_REGS )
135                 {
136                     pCppArgs[nPos] = pUnoArgs[nPos] = fpreg++;
137                     nr_fpr++;
138                 }
139                 else
140                     pCppArgs[nPos] = pUnoArgs[nPos] = ovrflw++;
141             }
142             else if ( nUsedGPR == 1 )
143             {
144                 if ( nr_gpr < aarch64::MAX_GPR_REGS )
145                 {
146                     pCppArgs[nPos] = pUnoArgs[nPos] = gpreg++;
147                     nr_gpr++;
148                 }
149                 else
150                     pCppArgs[nPos] = pUnoArgs[nPos] = ovrflw++;
151             }
152         }
153         else // struct <= 16 bytes || ptr to complex value || ref
154         {
155             typelib_TypeDescription * pParamTypeDescr = 0;
156             TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef );
157 
158             void *pCppStack = 0;
159             if ( nr_gpr < aarch64::MAX_GPR_REGS )
160             {
161                 pCppArgs[nPos] = pCppStack = *gpreg++;
162                 nr_gpr++;
163             }
164             else
165                 pCppArgs[nPos] = pCppStack = *ovrflw++;
166 
167             if (! rParam.bIn) // is pure out
168             {
169                 // uno out is unconstructed mem!
170                 pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize );
171                 pTempIndizes[nTempIndizes] = nPos;
172                 // will be released at reconversion
173                 ppTempParamTypeDescr[nTempIndizes++] = pParamTypeDescr;
174             }
175             else if ( bridges::cpp_uno::shared::relatesToInterfaceType( pParamTypeDescr ) ) // is in/inout
176             {
177                 uno_copyAndConvertData( pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize ),
178                                         pCppStack, pParamTypeDescr,
179                                         pThis->getBridge()->getCpp2Uno() );
180                 pTempIndizes[nTempIndizes] = nPos; // has to be reconverted
181                 // will be released at reconversion
182                 ppTempParamTypeDescr[nTempIndizes++] = pParamTypeDescr;
183             }
184             else // direct way
185             {
186                 pUnoArgs[nPos] = pCppStack;
187                 // no longer needed
188                 TYPELIB_DANGER_RELEASE( pParamTypeDescr );
189             }
190         }
191     }
192 
193     // ExceptionHolder
194     uno_Any aUnoExc; // Any will be constructed by callee
195     uno_Any * pUnoExc = &aUnoExc;
196 
197     // invoke uno dispatch call
198     (*pThis->getUnoI()->pDispatcher)( pThis->getUnoI(), pMemberTypeDescr, pUnoReturn, pUnoArgs, &pUnoExc );
199 
200     // in case an exception occurred...
201     if ( pUnoExc )
202     {
203         // destruct temporary in/inout params
204         for ( ; nTempIndizes--; )
205         {
206             sal_Int32 nIndex = pTempIndizes[nTempIndizes];
207 
208             if (pParams[nIndex].bIn) // is in/inout => was constructed
209                 uno_destructData( pUnoArgs[nIndex], ppTempParamTypeDescr[nTempIndizes], 0 );
210             TYPELIB_DANGER_RELEASE( ppTempParamTypeDescr[nTempIndizes] );
211         }
212         if (pReturnTypeDescr)
213             TYPELIB_DANGER_RELEASE( pReturnTypeDescr );
214 
215         CPPU_CURRENT_NAMESPACE::raiseException( &aUnoExc, pThis->getBridge()->getUno2Cpp() ); // has to destruct the any
216         // is here for dummy
217         return typelib_TypeClass_VOID;
218     }
219     else // else no exception occurred...
220     {
221         // temporary params
222         for ( ; nTempIndizes--; )
223         {
224             sal_Int32 nIndex = pTempIndizes[nTempIndizes];
225             typelib_TypeDescription * pParamTypeDescr = ppTempParamTypeDescr[nTempIndizes];
226 
227             if ( pParams[nIndex].bOut ) // inout/out
228             {
229                 // convert and assign
230                 uno_destructData( pCppArgs[nIndex], pParamTypeDescr, cpp_release );
231                 uno_copyAndConvertData( pCppArgs[nIndex], pUnoArgs[nIndex], pParamTypeDescr,
232                                         pThis->getBridge()->getUno2Cpp() );
233             }
234             // destroy temp uno param
235             uno_destructData( pUnoArgs[nIndex], pParamTypeDescr, 0 );
236 
237             TYPELIB_DANGER_RELEASE( pParamTypeDescr );
238         }
239         // return
240         if ( pCppReturn ) // has complex return
241         {
242             if ( pUnoReturn != pCppReturn ) // needs reconversion
243             {
244                 uno_copyAndConvertData( pCppReturn, pUnoReturn, pReturnTypeDescr,
245                                         pThis->getBridge()->getUno2Cpp() );
246                 // destroy temp uno return
247                 uno_destructData( pUnoReturn, pReturnTypeDescr, 0 );
248             }
249             // complex return ptr is set to return reg
250             *reinterpret_cast<void **>(pRegisterReturn) = pCppReturn;
251         }
252         if ( pReturnTypeDescr )
253         {
254             typelib_TypeClass eRet = (typelib_TypeClass)pReturnTypeDescr->eTypeClass;
255             TYPELIB_DANGER_RELEASE( pReturnTypeDescr );
256             return eRet;
257         }
258         else
259             return typelib_TypeClass_VOID;
260     }
261 }
262 
263 
264 //==================================================================================================
265 extern "C" sal_uInt32 cpp_vtable_call(
266     sal_Int32 nFunctionIndex, sal_Int32 nVtableOffset,
267     void ** gpreg, void ** fpreg, void ** ovrflw,
268     void * pIndirectReturn, // AArch64 x8 indirect-result pointer (0 if none)
269     sal_uInt64 * pRegisterReturn /* space for register return */ )
270 {
271     // gpreg:  this, [other gpr params x0..x7]
272     // fpreg:  [fpr params d0..d7]
273     // ovrflw: [gpr or fpr params (properly aligned)]
274     // pIndirectReturn: x8 (the hidden return buffer), when bit 0x80000000 set.
275     //
276     // On AArch64 'this' is ALWAYS x0 = gpreg[0]; the hidden return pointer is
277     // the separate x8 register, not a displaced first GPR (unlike x86-64 SysV
278     // where it occupied gpreg[0] and 'this' moved to gpreg[1]).
279     if ( nFunctionIndex & 0x80000000 )
280         nFunctionIndex &= 0x7fffffff;
281 
282     void * pThis = gpreg[0];
283     pThis = static_cast<char *>( pThis ) - nVtableOffset;
284 
285     bridges::cpp_uno::shared::CppInterfaceProxy * pCppI =
286         bridges::cpp_uno::shared::CppInterfaceProxy::castInterfaceToProxy( pThis );
287 
288     typelib_InterfaceTypeDescription * pTypeDescr = pCppI->getTypeDescr();
289 
290     OSL_ENSURE( nFunctionIndex < pTypeDescr->nMapFunctionIndexToMemberIndex, "### illegal vtable index!\n" );
291     if ( nFunctionIndex >= pTypeDescr->nMapFunctionIndexToMemberIndex )
292     {
293         throw RuntimeException( OUString::createFromAscii("illegal vtable index!"),
294                                 reinterpret_cast<XInterface *>( pCppI ) );
295     }
296 
297     // determine called method
298     sal_Int32 nMemberPos = pTypeDescr->pMapFunctionIndexToMemberIndex[nFunctionIndex];
299     OSL_ENSURE( nMemberPos < pTypeDescr->nAllMembers, "### illegal member index!\n" );
300 
301     TypeDescription aMemberDescr( pTypeDescr->ppAllMembers[nMemberPos] );
302 
303     sal_uInt32 eRet;
304     switch ( aMemberDescr.get()->eTypeClass )
305     {
306         case typelib_TypeClass_INTERFACE_ATTRIBUTE:
307         {
308             typelib_TypeDescriptionReference *pAttrTypeRef =
309                 reinterpret_cast<typelib_InterfaceAttributeTypeDescription *>( aMemberDescr.get() )->pAttributeTypeRef;
310 
311             if ( pTypeDescr->pMapMemberIndexToFunctionIndex[nMemberPos] == nFunctionIndex )
312             {
313                 // is GET method
314                     eRet = cpp2uno_call( pCppI, aMemberDescr.get(), pAttrTypeRef,
315                         0, 0, // no params
316                         gpreg, fpreg, ovrflw, pIndirectReturn, pRegisterReturn );
317                     eRet = aarch64::get_return_kind( pAttrTypeRef );
318             }
319             else
320             {
321                 // is SET method
322                 typelib_MethodParameter aParam;
323                 aParam.pTypeRef = pAttrTypeRef;
324                 aParam.bIn      = sal_True;
325                 aParam.bOut     = sal_False;
326 
327                 eRet = cpp2uno_call( pCppI, aMemberDescr.get(),
328                         0, // indicates void return
329                         1, &aParam,
330                         gpreg, fpreg, ovrflw, pIndirectReturn, pRegisterReturn );
331             }
332             break;
333         }
334         case typelib_TypeClass_INTERFACE_METHOD:
335         {
336             // is METHOD
337             switch ( nFunctionIndex )
338             {
339                 case 1: // acquire()
340                     pCppI->acquireProxy(); // non virtual call!
341                     eRet = typelib_TypeClass_VOID;
342                     break;
343                 case 2: // release()
344                     pCppI->releaseProxy(); // non virtual call!
345                     eRet = typelib_TypeClass_VOID;
346                     break;
347                 case 0: // queryInterface() opt
348                 {
349                     // queryInterface([in] type) returns an Any (> 16 bytes),
350                     // so on AArch64 the result buffer is x8 (pIndirectReturn),
351                     // 'this' is gpreg[0], and the type argument is the first
352                     // real parameter, gpreg[1].
353                     typelib_TypeDescription * pTD = 0;
354                     TYPELIB_DANGER_GET( &pTD, reinterpret_cast<Type *>( gpreg[1] )->getTypeLibType() );
355                     if ( pTD )
356                     {
357                         XInterface * pInterface = 0;
358                         (*pCppI->getBridge()->getCppEnv()->getRegisteredInterface)
359                             ( pCppI->getBridge()->getCppEnv(),
360                               reinterpret_cast<void **>(&pInterface),
361                               pCppI->getOid().pData,
362                               reinterpret_cast<typelib_InterfaceTypeDescription *>( pTD ) );
363 
364                         if ( pInterface )
365                         {
366                             ::uno_any_construct( reinterpret_cast<uno_Any *>( pIndirectReturn ),
367                                                  &pInterface, pTD, cpp_acquire );
368 
369                             pInterface->release();
370                             TYPELIB_DANGER_RELEASE( pTD );
371 
372                             reinterpret_cast<void **>( pRegisterReturn )[0] = pIndirectReturn;
373                             eRet = typelib_TypeClass_ANY;
374                             break;
375                         }
376                         TYPELIB_DANGER_RELEASE( pTD );
377                     }
378                 } // else perform queryInterface()
379                 default:
380                 {
381                     typelib_InterfaceMethodTypeDescription *pMethodTD =
382                         reinterpret_cast<typelib_InterfaceMethodTypeDescription *>( aMemberDescr.get() );
383 
384                     eRet = cpp2uno_call( pCppI, aMemberDescr.get(),
385                                          pMethodTD->pReturnTypeRef,
386                                          pMethodTD->nParams,
387                                          pMethodTD->pParams,
388                                          gpreg, fpreg, ovrflw, pIndirectReturn, pRegisterReturn );
389                     eRet = aarch64::get_return_kind( pMethodTD->pReturnTypeRef );
390                 }
391             }
392             break;
393         }
394         default:
395         {
396             throw RuntimeException( OUString::createFromAscii("no member description found!"),
397                                     reinterpret_cast<XInterface *>( pCppI ) );
398             // is here for dummy
399             eRet = typelib_TypeClass_VOID;
400         }
401     }
402 
403     return eRet;
404 }
405 
406 //==================================================================================================
407 // The incoming register-spill executor, implemented in call.s.  It is reached
408 // via BR from a per-slot snippet (codeSnippet below) with x16 carrying the
409 // packed (nVtableOffset << 32) | nFunctionIndex; it spills the argument
410 // registers and calls cpp_vtable_call.
411 extern "C" void privateSnippetExecutor( void );
412 
413 // Each snippet is 5 AArch64 instructions (20 bytes) + 4 bytes padding to an
414 // 8-byte boundary + two 8-byte literals (the packed index and the executor
415 // address) = 40 bytes.
416 const int codeSnippetSize = 40;
417 
418 // Generate a per-vtable-slot trampoline that loads the packed function index
419 // into x16 and branches to privateSnippetExecutor(), preserving every
420 // argument register.  Uses PC-relative literal loads because AArch64 cannot
421 // embed a 64-bit immediate in a single instruction.
422 //
423 // Layout (offsets in bytes from code):
424 //    0:  ldr  x16, #24      ; x16 = nOffsetAndIndex (literal at +24)
425 //    4:  ldr  x17, #28      ; x17 = privateSnippetExecutor (literal at +32)
426 //    8:  br   x17
427 //   12:  (unused / padding)
428 //   16:  (padding to 8-byte align the literals at 24)
429 //   24:  .quad nOffsetAndIndex
430 //   32:  .quad privateSnippetExecutor
431 //
432 // Note: the snippet creates no stack frame, so the C++ unwinder walks straight
433 // through it to the original caller (required for UNO exception propagation).
434 unsigned char * codeSnippet( unsigned char * code,
435         sal_Int32 nFunctionIndex, sal_Int32 nVtableOffset,
436         bool bHasHiddenParam ) SAL_THROW( () )
437 {
438     sal_uInt64 nOffsetAndIndex = ( static_cast<sal_uInt64>( nVtableOffset ) << 32 ) | static_cast<sal_uInt64>( nFunctionIndex );
439 
440     if ( bHasHiddenParam )
441         nOffsetAndIndex |= 0x80000000;
442 
443     sal_uInt32 * p = reinterpret_cast<sal_uInt32 *>( code );
444 
445     // ldr x16, #24  -> literal at code+24. imm19 = 24/4 = 6.
446     //   encoding: 0x58000000 | (imm19 << 5) | Rt(16)
447     p[0] = 0x58000000 | ( 6 << 5 ) | 16;
448     // ldr x17, #28  -> literal at code+32 (relative to this insn at +4): 28.
449     //   imm19 = 28/4 = 7.
450     p[1] = 0x58000000 | ( 7 << 5 ) | 17;
451     // br x17  -> 0xD61F0000 | (Rn(17) << 5)
452     p[2] = 0xD61F0000 | ( 17 << 5 );
453     // p[3] (offset 12) and p[4] (offset 16..20) are padding.
454     p[3] = 0xD503201F; // NOP
455     p[4] = 0xD503201F; // NOP
456 
457     // literals, 8-byte aligned at offset 24 and 32
458     *reinterpret_cast<sal_uInt64 *>( code + 24 ) = nOffsetAndIndex;
459     *reinterpret_cast<sal_uInt64 *>( code + 32 ) = reinterpret_cast<sal_uInt64>( privateSnippetExecutor );
460 
461     return code + codeSnippetSize;
462 }
463 
464 //==================================================================================================
465 struct bridges::cpp_uno::shared::VtableFactory::Slot { void * fn; };
466 
467 bridges::cpp_uno::shared::VtableFactory::Slot *
468 bridges::cpp_uno::shared::VtableFactory::mapBlockToVtable(void * block)
469 {
470     return static_cast< Slot * >(block) + 2;
471 }
472 
473 //==================================================================================================
474 sal_Size bridges::cpp_uno::shared::VtableFactory::getBlockSize(
475     sal_Int32 slotCount)
476 {
477     return (slotCount + 2) * sizeof (Slot) + slotCount * codeSnippetSize;
478 }
479 
480 //==================================================================================================
481 bridges::cpp_uno::shared::VtableFactory::Slot *
482 bridges::cpp_uno::shared::VtableFactory::initializeBlock(
483     void * block, sal_Int32 slotCount)
484 {
485     Slot * slots = mapBlockToVtable(block);
486     slots[-2].fn = 0;
487     slots[-1].fn = 0;
488     return slots + slotCount;
489 }
490 
491 //==================================================================================================
492 
493 unsigned char * bridges::cpp_uno::shared::VtableFactory::addLocalFunctions(
494     Slot ** slots, unsigned char * code, /*sal_PtrDiff writetoexecdiff,*/
495     typelib_InterfaceTypeDescription const * type, sal_Int32 nFunctionOffset,
496     sal_Int32 functionCount, sal_Int32 nVtableOffset )
497 {
498     const sal_PtrDiff writetoexecdiff = 0;
499     (*slots) -= functionCount;
500     Slot * s = *slots;
501     for ( sal_Int32 nPos = 0; nPos < type->nMembers; ++nPos )
502     {
503         typelib_TypeDescription * pTD = 0;
504 
505         TYPELIB_DANGER_GET( &pTD, type->ppMembers[ nPos ] );
506         OSL_ASSERT( pTD );
507 
508         if ( typelib_TypeClass_INTERFACE_ATTRIBUTE == pTD->eTypeClass )
509         {
510             typelib_InterfaceAttributeTypeDescription *pAttrTD =
511                 reinterpret_cast<typelib_InterfaceAttributeTypeDescription *>( pTD );
512 
513             // get method
514             (s++)->fn = code + writetoexecdiff;
515             code = codeSnippet( code, nFunctionOffset++, nVtableOffset,
516                                 aarch64::return_in_hidden_param( pAttrTD->pAttributeTypeRef ) );
517 
518             if ( ! pAttrTD->bReadOnly )
519             {
520                 // set method
521                 (s++)->fn = code + writetoexecdiff;
522                 code = codeSnippet( code, nFunctionOffset++, nVtableOffset, false );
523             }
524         }
525         else if ( typelib_TypeClass_INTERFACE_METHOD == pTD->eTypeClass )
526         {
527             typelib_InterfaceMethodTypeDescription *pMethodTD =
528                 reinterpret_cast<typelib_InterfaceMethodTypeDescription *>( pTD );
529 
530             (s++)->fn = code + writetoexecdiff;
531             code = codeSnippet( code, nFunctionOffset++, nVtableOffset,
532                                 aarch64::return_in_hidden_param( pMethodTD->pReturnTypeRef ) );
533         }
534         else
535             OSL_ASSERT( false );
536 
537         TYPELIB_DANGER_RELEASE( pTD );
538     }
539     return code;
540 }
541 
542 //==================================================================================================
543 void bridges::cpp_uno::shared::VtableFactory::flushCode(
544     unsigned char const * begin, unsigned char const * end )
545 {
546     sys_icache_invalidate(
547         const_cast<unsigned char *>(begin), end - begin );
548 }
549