Merge pull request #14619 from briansull/emitter-cleanup
[platform/upstream/coreclr.git] / src / vm / gcheaputilities.h
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 #ifndef _GCHEAPUTILITIES_H_
6 #define _GCHEAPUTILITIES_H_
7
8 #include "gcinterface.h"
9
10 // The singular heap instance.
11 GPTR_DECL(IGCHeap, g_pGCHeap);
12
13 #ifndef DACCESS_COMPILE
14 extern "C" {
15 #endif // !DACCESS_COMPILE
16 GPTR_DECL(uint8_t,g_lowest_address);
17 GPTR_DECL(uint8_t,g_highest_address);
18 GPTR_DECL(uint32_t,g_card_table);
19 GVAL_DECL(GCHeapType, g_heap_type);
20 #ifndef DACCESS_COMPILE
21 }
22 #endif // !DACCESS_COMPILE
23
24 // For single-proc machines, the EE will use a single, shared alloc context
25 // for all allocations. In order to avoid extra indirections in assembly
26 // allocation helpers, the EE owns the global allocation context and the
27 // GC will update it when it needs to.
28 extern "C" gc_alloc_context g_global_alloc_context;
29
30 extern "C" uint32_t* g_card_bundle_table;
31 extern "C" uint8_t* g_ephemeral_low;
32 extern "C" uint8_t* g_ephemeral_high;
33
34 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
35
36 // Table containing the dirty state. This table is translated to exclude the lowest address it represents, see
37 // TranslateTableToExcludeHeapStartAddress.
38 extern "C" uint8_t *g_sw_ww_table;
39
40 // Write watch may be disabled when it is not needed (between GCs for instance). This indicates whether it is enabled.
41 extern "C" bool g_sw_ww_enabled_for_gc_heap;
42
43 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
44
45 // g_gc_dac_vars is a structure of pointers to GC globals that the
46 // DAC uses. It is not exposed directly to the DAC.
47 extern GcDacVars g_gc_dac_vars;
48
49 // Instead of exposing g_gc_dac_vars to the DAC, a pointer to it
50 // is exposed here (g_gcDacGlobals). The reason for this is to avoid
51 // a problem in which a debugger attaches to a program while the program
52 // is in the middle of initializing the GC DAC vars - if the "publishing"
53 // of DAC vars isn't atomic, the debugger could see a partially initialized
54 // GcDacVars structure.
55 //
56 // Instead, the debuggee "publishes" GcDacVars by assigning a pointer to g_gc_dac_vars
57 // to this global, and the DAC will read this global.
58 typedef DPTR(GcDacVars) PTR_GcDacVars;
59 GPTR_DECL(GcDacVars, g_gcDacGlobals);
60
61 // GCHeapUtilities provides a number of static methods
62 // that operate on the global heap instance. It can't be
63 // instantiated.
64 class GCHeapUtilities {
65 public:
66     // Retrieves the GC heap.
67     inline static IGCHeap* GetGCHeap() 
68     {
69         LIMITED_METHOD_CONTRACT;
70
71         assert(g_pGCHeap != nullptr);
72         return g_pGCHeap;
73     }
74
75     // Returns true if the heap has been initialized, false otherwise.
76     inline static bool IsGCHeapInitialized()
77     {
78         LIMITED_METHOD_CONTRACT;
79
80         return g_pGCHeap != nullptr;
81     }
82
83     // Returns true if a the heap is initialized and a garbage collection
84     // is in progress, false otherwise.
85     inline static bool IsGCInProgress(bool bConsiderGCStart = false)
86     {
87         WRAPPER_NO_CONTRACT;
88
89         return (IsGCHeapInitialized() ? GetGCHeap()->IsGCInProgressHelper(bConsiderGCStart) : false);
90     }
91
92     // Returns true if we should be competing marking for statics. This
93     // influences the behavior of `GCToEEInterface::GcScanRoots`.
94     inline static bool MarkShouldCompeteForStatics()
95     {
96         WRAPPER_NO_CONTRACT;
97
98         return IsServerHeap() && g_SystemInfo.dwNumberOfProcessors >= 2;
99     }
100
101     // Waits until a GC is complete, if the heap has been initialized.
102     inline static void WaitForGCCompletion(bool bConsiderGCStart = false)
103     {
104         WRAPPER_NO_CONTRACT;
105
106         if (IsGCHeapInitialized())
107             GetGCHeap()->WaitUntilGCComplete(bConsiderGCStart);
108     }
109
110     // Returns true if the held GC heap is a Server GC heap, false otherwise.
111     inline static bool IsServerHeap()
112     {
113         LIMITED_METHOD_CONTRACT;
114
115 #ifdef FEATURE_SVR_GC
116         _ASSERTE(g_heap_type != GC_HEAP_INVALID);
117         return g_heap_type == GC_HEAP_SVR;
118 #else
119         return false;
120 #endif // FEATURE_SVR_GC
121     }
122
123     static bool UseThreadAllocationContexts()
124     {
125         // When running on a single-proc system, it's more efficient to use a single global
126         // allocation context for SOH allocations than to use one for every thread.
127 #if defined(_TARGET_ARM_) || defined(FEATURE_PAL) || defined(FEATURE_REDHAWK)
128         return true;
129 #else
130         return IsServerHeap() || ::GetCurrentProcessCpuCount() != 1;
131 #endif
132
133     }
134
135 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
136
137     // Returns True if software write watch is currently enabled for the GC Heap,
138     // or False if it is not.
139     inline static bool SoftwareWriteWatchIsEnabled()
140     {
141         WRAPPER_NO_CONTRACT;
142
143         return g_sw_ww_enabled_for_gc_heap;
144     }
145
146     // In accordance with the SoftwareWriteWatch scheme, marks a given address as
147     // "dirty" (e.g. has been written to).
148     inline static void SoftwareWriteWatchSetDirty(void* address, size_t write_size)
149     {
150         LIMITED_METHOD_CONTRACT;
151
152         // We presumably have just written something to this address, so it can't be null.
153         assert(address != nullptr);
154
155         // The implementation is limited to writes of a pointer size or less. Writes larger
156         // than pointer size may cross page boundaries and would require us to potentially
157         // set more than one entry in the SWW table, which can't be done atomically under
158         // the current scheme.
159         assert(write_size <= sizeof(void*));
160
161         size_t table_byte_index = reinterpret_cast<size_t>(address) >> SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift;
162
163         // The table byte index that we calculate for the address should be the same as the one
164         // calculated for a pointer to the end of the written region. If this were not the case,
165         // this write crossed a boundary and would dirty two pages.
166         uint8_t* end_of_write_ptr = reinterpret_cast<uint8_t*>(address) + (write_size - 1);
167         assert(table_byte_index == reinterpret_cast<size_t>(end_of_write_ptr) >> SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift);
168         uint8_t* table_address = &g_sw_ww_table[table_byte_index];
169         if (*table_address == 0)
170         {
171             *table_address = 0xFF;
172         }
173     }
174
175     // In accordance with the SoftwareWriteWatch scheme, marks a range of addresses
176     // as dirty, starting at the given address and with the given length.
177     inline static void SoftwareWriteWatchSetDirtyRegion(void* address, size_t length)
178     {
179         LIMITED_METHOD_CONTRACT;
180
181         // We presumably have just memcopied something to this address, so it can't be null.
182         assert(address != nullptr);
183
184         // The "base index" is the first index in the SWW table that covers the target
185         // region of memory.
186         size_t base_index = reinterpret_cast<size_t>(address) >> SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift;
187
188         // The "end_index" is the last index in the SWW table that covers the target
189         // region of memory.
190         uint8_t* end_pointer = reinterpret_cast<uint8_t*>(address) + length - 1;
191         size_t end_index = reinterpret_cast<size_t>(end_pointer) >> SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift;
192
193         // We'll mark the entire region of memory as dirty by memseting all entries in
194         // the SWW table between the start and end indexes.
195         memset(&g_sw_ww_table[base_index], ~0, end_index - base_index + 1);
196     }
197 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
198
199 private:
200     // This class should never be instantiated.
201     GCHeapUtilities() = delete;
202 };
203
204 #endif // _GCHEAPUTILITIES_H_
205