Welcome to mirror list, hosted at ThFree Co, Russian Federation.

callcounting.h « vm « coreclr « src - github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: fa0345238c870c6c1a6dc04df93a3800dbdb8486 (plain)
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma once

#include "codeversion.h"

#ifdef FEATURE_TIERED_COMPILATION

/*******************************************************************************************************************************
** Summary

Outline of phases
-----------------

When starting call counting for a method (see CallCountingManager::SetCodeEntryPoint):
- A CallCountingInfo is created (associated with the NativeCodeVersion to be counted), which initializes a remaining call count
  with a threshold
- A CallCountingStub is created. It contains a small amount of code that decrements the remaining call count and checks for
  zero. When nonzero, it jumps to the code version's native code entry point. When zero, it forwards to a helper function that
  handles tier promotion.
- For tiered methods that don't have a precode (virtual and interface methods when slot backpatching is enabled), a forwarder
  stub (a precode) is created and it forwards to the call counting stub. This is so that the call counting stub can be safely
  and easily deleted. The forwarder stubs are only used when counting calls, there is one per method (not per code version), and
  they are not deleted.
- The method's code entry point is set to the forwarder stub or the call counting stub to count calls to the code version

When the call count threshold is reached (see CallCountingManager::OnCallCountThresholdReached):
- The helper call enqueues completion of call counting for background processing
- When completing call counting in the background, the code version is enqueued for promotion, and the call counting stub is
  removed from the call chain

After all work queued for promotion is completed and methods transitioned to optimized tier, some cleanup follows
(see CallCountingManager::StopAndDeleteAllCallCountingStubs):
- Some heuristics are checked and if cleanup will be done, the runtime is suspended
- All call counting stubs are deleted. For code versions that have not completed counting, the method's code entry point is
  reset such that call counting would be reestablished on the next call.
- Completed call counting infos are deleted
- For methods that no longer have any code versions that need to be counted, the forwarder stubs are no longer tracked. If a
  new IL code version is added thereafter (perhaps by a profiler), a new forwarder stub may be created.

Miscellaneous
-------------

- The CallCountingManager is the main class with most of the logic. Its private subclasses are just simple data structures.
- The code versioning lock is used for data structures used for call counting. Installing a call counting stub requires that we
  know what the currently active code version is, it made sense to use the same lock.
- Call counting stubs have hardcoded code. x64 has short and long stubs, short stubs are used when possible (often) and use
  IP-relative branches to the method's code and helper stub. Other archs have only one type of stub (a short stub).
  - Call counting stubs pass a stub-identifying token to the threshold-reached helper function. The stub's address can be
    determined from it. On x64, it also indicates whether the stub is a short or long stub.
  - From a call counting stub, the call counting info can be determined using the remaining call count cell, and from the call
    counting info the code version and method can be determined
- Call counting is not stopped when the tiering delay is reactivated (often happens in larger and more realistic scenarios). The
  overhead necessary to stop and restart call counting (among other things, many methods will have to go through the prestub
  again) is greater than the overhead of completing call counting + calling the threshold-reached helper function, even for very
  high call count thresholds. While it may at times be desirable to not count method calls during startup phases, there would be
  a fair bit of additional overhead to stop counting. On the other hand, it may at times be beneficial to rejit some methods
  during startup. So for now, only newly called methods during the current tiering delay would not be counted, any that already
  started counting will continue (their delay already expired).

*******************************************************************************************************************************/

#define DISABLE_COPY(T) \
    T(const T &) = delete; \
    T &operator =(const T &) = delete

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CallCountingManager

class CallCountingManager;
typedef DPTR(CallCountingManager) PTR_CallCountingManager;

class CallCountingManager
{
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // CallCountingManager::CallCountingInfo

private:
    class CallCountingInfo;
    typedef DPTR(CallCountingInfo) PTR_CallCountingInfo;

    class CallCountingInfo
    {
    public:
        enum class Stage : UINT8
        {
            // Stub is definitely not going to be called, stub may be deleted
            StubIsNotActive,

            // Stub may be called, don't know if it's actually active (changes to code versions, etc.)
            StubMayBeActive,

            // Stub may be active, call counting complete, not yet promoted
            PendingCompletion,

            // Stub is not active and will not become active, call counting complete, promoted, stub may be deleted
            Complete,

            // Call counting is disabled, only used for the default code version to indicate that it is to be optimized
            Disabled
        };

    private:
        const NativeCodeVersion m_codeVersion;
        const CallCountingStub *m_callCountingStub;
        CallCount m_remainingCallCount;
        Stage m_stage;

    #ifndef DACCESS_COMPILE
    private:
        CallCountingInfo(NativeCodeVersion codeVersion);
    public:
        static CallCountingInfo *CreateWithCallCountingDisabled(NativeCodeVersion codeVersion);
        CallCountingInfo(NativeCodeVersion codeVersion, CallCount callCountThreshold);
        ~CallCountingInfo();
    #endif

    public:
        static PTR_CallCountingInfo From(PTR_CallCount remainingCallCountCell);
        NativeCodeVersion GetCodeVersion() const;

    #ifndef DACCESS_COMPILE
    public:
        const CallCountingStub *GetCallCountingStub() const;
        void SetCallCountingStub(const CallCountingStub *callCountingStub);
        void ClearCallCountingStub();
        CallCount *GetRemainingCallCountCell();
    #endif

    public:
        Stage GetStage() const;
    #ifndef DACCESS_COMPILE
    public:
        void SetStage(Stage stage);
    #endif

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // CallCountingManager::CallCountingInfo::CodeVersionHashTraits

    public:
        class CodeVersionHashTraits : public DefaultSHashTraits<PTR_CallCountingInfo>
        {
        private:
            typedef DefaultSHashTraits<PTR_CallCountingInfo> Base;
        public:
            typedef Base::element_t element_t;
            typedef Base::count_t count_t;
            typedef const NativeCodeVersion key_t;

        public:
            static key_t GetKey(const element_t &e);
            static BOOL Equals(const key_t &k1, const key_t &k2);
            static count_t Hash(const key_t &k);
        };
    };

    typedef SHash<CallCountingInfo::CodeVersionHashTraits> CallCountingInfoByCodeVersionHash;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // CallCountingManager::CallCountingStubAllocator

private:
    class CallCountingStubAllocator
    {
    private:
        // LoaderHeap cannot be constructed when DACCESS_COMPILE is defined (at the time, its destructor was private). Working
        // around that by controlling creation/destruction using a pointer.
        LoaderHeap *m_heap;
        RangeList m_heapRangeList;

    public:
        CallCountingStubAllocator();
        ~CallCountingStubAllocator();

    #ifndef DACCESS_COMPILE
    public:
        void Reset();
        const CallCountingStub *AllocateStub(CallCount *remainingCallCountCell, PCODE targetForMethod);
    private:
        LoaderHeap *AllocateHeap();
    #endif // !DACCESS_COMPILE

    public:
        bool IsStub(TADDR entryPoint);

    #ifdef DACCESS_COMPILE
        void EnumerateHeapRanges(CLRDataEnumMemoryFlags flags);
    #endif

        DISABLE_COPY(CallCountingStubAllocator);
    };

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // CallCountingManager::MethodDescForwarderStub

private:
    class MethodDescForwarderStubHashTraits : public DefaultSHashTraits<Precode *>
    {
    private:
        typedef DefaultSHashTraits<Precode *> Base;
    public:
        typedef Base::element_t element_t;
        typedef Base::count_t count_t;
        typedef MethodDesc *key_t;

    public:
        static key_t GetKey(const element_t &e);
        static BOOL Equals(const key_t &k1, const key_t &k2);
        static count_t Hash(const key_t &k);
    };

    typedef SHash<MethodDescForwarderStubHashTraits> MethodDescForwarderStubHash;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // CallCountingManager::CallCountingManagerHashTraits

private:
    class CallCountingManagerHashTraits : public DefaultSHashTraits<PTR_CallCountingManager>
    {
    private:
        typedef DefaultSHashTraits<PTR_CallCountingManager> Base;
    public:
        typedef Base::element_t element_t;
        typedef Base::count_t count_t;
        typedef PTR_CallCountingManager key_t;

    public:
        static key_t GetKey(const element_t &e);
        static BOOL Equals(const key_t &k1, const key_t &k2);
        static count_t Hash(const key_t &k);
    };

    typedef SHash<CallCountingManagerHashTraits> CallCountingManagerHash;
    typedef DPTR(CallCountingManagerHash) PTR_CallCountingManagerHash;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // CallCountingManager members

private:
    static PTR_CallCountingManagerHash s_callCountingManagers;
    static COUNT_T s_callCountingStubCount;
    static COUNT_T s_activeCallCountingStubCount;
    static COUNT_T s_completedCallCountingStubCount;

private:
    CallCountingInfoByCodeVersionHash m_callCountingInfoByCodeVersionHash;
    CallCountingStubAllocator m_callCountingStubAllocator;
    MethodDescForwarderStubHash m_methodDescForwarderStubHash;
    SArray<CallCountingInfo *> m_callCountingInfosPendingCompletion;

public:
    CallCountingManager();
    ~CallCountingManager();

#ifndef DACCESS_COMPILE
public:
    static void StaticInitialize();
#endif // !DACCESS_COMPILE

public:
    bool IsCallCountingEnabled(NativeCodeVersion codeVersion);

#ifndef DACCESS_COMPILE
public:
    void DisableCallCounting(NativeCodeVersion codeVersion);

public:
    static bool SetCodeEntryPoint(
        NativeCodeVersion activeCodeVersion,
        PCODE codeEntryPoint,
        bool wasMethodCalled,
        bool *createTieringBackgroundWorker);
    static PCODE OnCallCountThresholdReached(TransitionBlock *transitionBlock, TADDR stubIdentifyingToken);
    static COUNT_T GetCountOfCodeVersionsPendingCompletion();
    static void CompleteCallCounting();

public:
    static void StopAndDeleteAllCallCountingStubs();
private:
    static void StopAllCallCounting(TieredCompilationManager *tieredCompilationManager);
    static void DeleteAllCallCountingStubs();
    void TrimCollections();
#endif // !DACCESS_COMPILE

public:
    static bool IsCallCountingStub(PCODE entryPoint);
    static PCODE GetTargetForMethod(PCODE callCountingStubEntryPoint);
#ifdef DACCESS_COMPILE
    static void DacEnumerateCallCountingStubHeapRanges(CLRDataEnumMemoryFlags flags);
#endif

    DISABLE_COPY(CallCountingManager);
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CallCountingManager::CallCountingStubManager

class CallCountingStubManager;
typedef VPTR(CallCountingStubManager) PTR_CallCountingStubManager;

class CallCountingStubManager : public StubManager
{
    VPTR_VTABLE_CLASS(CallCountingStubManager, StubManager);

private:
    SPTR_DECL(CallCountingStubManager, g_pManager);

#ifndef DACCESS_COMPILE
public:
    CallCountingStubManager();

public:
    static void Init();
#endif

#ifdef _DEBUG
public:
    virtual const char *DbgGetName(); // override
#endif

#ifdef DACCESS_COMPILE
public:
    virtual LPCWSTR GetStubManagerName(PCODE addr);
#endif

protected:
    virtual BOOL CheckIsStub_Internal(PCODE entryPoint); // override
    virtual BOOL DoTraceStub(PCODE callCountingStubEntryPoint, TraceDestination *trace); // override

#ifdef DACCESS_COMPILE
protected:
    virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags); // override
#endif

    DISABLE_COPY(CallCountingStubManager);
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#undef DISABLE_COPY

#endif // FEATURE_TIERED_COMPILATION