From bbd32c484f2d00a566217e52bddb6960c2ec0b22 Mon Sep 17 00:00:00 2001 From: Sean Gillespie Date: Mon, 23 Jan 2017 19:44:10 -0800 Subject: [Local GC] Provide an implementation of GCToOSInterface for Unix-like platforms (#8976) * Add way to build with FEATURE_STANDALONE_GC from build.sh * Make CMake changes to build the GC 'PAL' as its own build target (to avoid -nostdinc). In addition, introduce a "GC PAL" that provides an implementation of GCToOSInterface on Unix-like platforms, for use with FEATURE_STANDALONE_GC. --- src/gc/gcenv.ee.standalone.inl | 84 ++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 27 deletions(-) (limited to 'src/gc/gcenv.ee.standalone.inl') diff --git a/src/gc/gcenv.ee.standalone.inl b/src/gc/gcenv.ee.standalone.inl index e285394eb3..31f3d1d8da 100644 --- a/src/gc/gcenv.ee.standalone.inl +++ b/src/gc/gcenv.ee.standalone.inl @@ -11,172 +11,202 @@ // will be fowarded to this interface instance. extern IGCToCLR* g_theGCToCLR; +// A note about this: +// In general, we don't want to pretend to be smarter than the compiler +// and force it to inline things. However, inlining is here is required +// for correctness as it stands today (though it will not always be required). +// +// The reason for this is because: +// 1) This file (and the GCToEEInterface class) define symbols that are inline +// and static, so the symbol GCToEEInterface::XYZ defines a symbol with weak +// linkage when the function is not inlined, +// 2) src/vm/gcenv.ee.cpp all define symbols that are not inline and instance methods +// of GCToEEInterface, with external linkage. +// 3) When it comes time to link the GC and the VM, the linker observes the duplicate +// symbols and discards the one with weak linkage. +// 4) All of the calls within the GC to the functions in this file are replaced by +// the linker to calls to the implementation of a pure virtual IGCToCLR. The +// functions implementing IGCToCLR have an extra argument (this). +// 5) Now, all calls to these functions from within the GC are doomed because of the +// functions that actually get called expect this to be in rdi, where the compiler +// has placed the first argument instead. +// +// For now, by forcing the compiler to inline these functions, the compiler won't actually +// emit symbols for them and we'll avoid the linker havoc. +#ifdef _MSC_VER + #define ALWAYS_INLINE __forceinline +#else + #define ALWAYS_INLINE __attribute__((always_inline)) inline +#endif + // When we are building the GC in a standalone environment, we // will be dispatching virtually against g_theGCToCLR to call // into the EE. This class provides an identical API to the existing // GCToEEInterface, but only forwards the call onto the global // g_theGCToCLR instance. -inline void GCToEEInterface::SuspendEE(SUSPEND_REASON reason) +ALWAYS_INLINE void GCToEEInterface::SuspendEE(SUSPEND_REASON reason) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->SuspendEE(reason); } -inline void GCToEEInterface::RestartEE(bool bFinishedGC) +ALWAYS_INLINE void GCToEEInterface::RestartEE(bool bFinishedGC) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->RestartEE(bFinishedGC); } -inline void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, ScanContext* sc) +ALWAYS_INLINE void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, ScanContext* sc) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->GcScanRoots(fn, condemned, max_gen, sc); } -inline void GCToEEInterface::GcStartWork(int condemned, int max_gen) +ALWAYS_INLINE void GCToEEInterface::GcStartWork(int condemned, int max_gen) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->GcStartWork(condemned, max_gen); } -inline void GCToEEInterface::AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc) +ALWAYS_INLINE void GCToEEInterface::AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->AfterGcScanRoots(condemned, max_gen, sc); } -inline void GCToEEInterface::GcBeforeBGCSweepWork() +ALWAYS_INLINE void GCToEEInterface::GcBeforeBGCSweepWork() { assert(g_theGCToCLR != nullptr); g_theGCToCLR->GcBeforeBGCSweepWork(); } -inline void GCToEEInterface::GcDone(int condemned) +ALWAYS_INLINE void GCToEEInterface::GcDone(int condemned) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->GcDone(condemned); } -inline bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject) +ALWAYS_INLINE bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject) { assert(g_theGCToCLR != nullptr); return g_theGCToCLR->RefCountedHandleCallbacks(pObject); } -inline void GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2) +ALWAYS_INLINE void GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->SyncBlockCacheWeakPtrScan(scanProc, lp1, lp2); } -inline void GCToEEInterface::SyncBlockCacheDemote(int max_gen) +ALWAYS_INLINE void GCToEEInterface::SyncBlockCacheDemote(int max_gen) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->SyncBlockCacheDemote(max_gen); } -inline void GCToEEInterface::SyncBlockCachePromotionsGranted(int max_gen) +ALWAYS_INLINE void GCToEEInterface::SyncBlockCachePromotionsGranted(int max_gen) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->SyncBlockCachePromotionsGranted(max_gen); } -inline bool GCToEEInterface::IsPreemptiveGCDisabled(Thread * pThread) +ALWAYS_INLINE bool GCToEEInterface::IsPreemptiveGCDisabled(Thread * pThread) { assert(g_theGCToCLR != nullptr); return g_theGCToCLR->IsPreemptiveGCDisabled(pThread); } -inline void GCToEEInterface::EnablePreemptiveGC(Thread * pThread) +ALWAYS_INLINE void GCToEEInterface::EnablePreemptiveGC(Thread * pThread) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->EnablePreemptiveGC(pThread); } -inline void GCToEEInterface::DisablePreemptiveGC(Thread * pThread) +ALWAYS_INLINE void GCToEEInterface::DisablePreemptiveGC(Thread * pThread) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->DisablePreemptiveGC(pThread); } -inline gc_alloc_context * GCToEEInterface::GetAllocContext(Thread * pThread) +ALWAYS_INLINE gc_alloc_context * GCToEEInterface::GetAllocContext(Thread * pThread) { assert(g_theGCToCLR != nullptr); return g_theGCToCLR->GetAllocContext(pThread); } -inline bool GCToEEInterface::CatchAtSafePoint(Thread * pThread) +ALWAYS_INLINE bool GCToEEInterface::CatchAtSafePoint(Thread * pThread) { assert(g_theGCToCLR != nullptr); return g_theGCToCLR->CatchAtSafePoint(pThread); } -inline void GCToEEInterface::GcEnumAllocContexts(enum_alloc_context_func* fn, void* param) +ALWAYS_INLINE void GCToEEInterface::GcEnumAllocContexts(enum_alloc_context_func* fn, void* param) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->GcEnumAllocContexts(fn, param); } -inline Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg) +ALWAYS_INLINE Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg) { assert(g_theGCToCLR != nullptr); return g_theGCToCLR->CreateBackgroundThread(threadStart, arg); } -inline void GCToEEInterface::DiagGCStart(int gen, bool isInduced) +ALWAYS_INLINE void GCToEEInterface::DiagGCStart(int gen, bool isInduced) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->DiagGCStart(gen, isInduced); } -inline void GCToEEInterface::DiagUpdateGenerationBounds() +ALWAYS_INLINE void GCToEEInterface::DiagUpdateGenerationBounds() { assert(g_theGCToCLR != nullptr); g_theGCToCLR->DiagUpdateGenerationBounds(); } -inline void GCToEEInterface::DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent) +ALWAYS_INLINE void GCToEEInterface::DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->DiagGCEnd(index, gen, reason, fConcurrent); } -inline void GCToEEInterface::DiagWalkFReachableObjects(void* gcContext) +ALWAYS_INLINE void GCToEEInterface::DiagWalkFReachableObjects(void* gcContext) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->DiagWalkFReachableObjects(gcContext); } -inline void GCToEEInterface::DiagWalkSurvivors(void* gcContext) +ALWAYS_INLINE void GCToEEInterface::DiagWalkSurvivors(void* gcContext) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->DiagWalkSurvivors(gcContext); } -inline void GCToEEInterface::DiagWalkLOHSurvivors(void* gcContext) +ALWAYS_INLINE void GCToEEInterface::DiagWalkLOHSurvivors(void* gcContext) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->DiagWalkLOHSurvivors(gcContext); } -inline void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext) +ALWAYS_INLINE void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext) { assert(g_theGCToCLR != nullptr); return g_theGCToCLR->DiagWalkBGCSurvivors(gcContext); } -inline void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) +ALWAYS_INLINE void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->StompWriteBarrier(args); } -inline void GCToEEInterface::EnableFinalization(bool foundFinalizers) +ALWAYS_INLINE void GCToEEInterface::EnableFinalization(bool foundFinalizers) { assert(g_theGCToCLR != nullptr); g_theGCToCLR->EnableFinalization(foundFinalizers); } +#undef ALWAYS_INLINE + #endif // __GCTOENV_EE_STANDALONE_INL__ -- cgit v1.2.3