summaryrefslogtreecommitdiff
path: root/src/jit/regset.h
blob: 985cee71d762f6563894efb3c9ed41d2cafd77ce (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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

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

#ifndef _REGSET_H
#define _REGSET_H
#include "vartype.h"
#include "target.h"

class LclVarDsc;
class TempDsc;
typedef struct GenTree *  GenTreePtr;
class Compiler;
class CodeGen;
class GCInfo;

/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XX                                                                           XX
XX                           RegSet                                          XX
XX                                                                           XX
XX  Represents the register set, and their states during code generation     XX
XX  Can select an unused register, keeps track of the contents of the        XX
XX  registers, and can spill registers                                       XX
XX                                                                           XX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/


/*****************************************************************************
*
*  Keep track of the current state of each register. This is intended to be
*  used for things like register reload suppression, but for now the only
*  thing it does is note which registers we use in each method.
*/

enum    regValKind
{
    RV_TRASH,           // random unclassified garbage
    RV_INT_CNS,         // integer constant
    RV_LCL_VAR,         // local variable value
    RV_LCL_VAR_LNG_LO,  // lower half of long local variable
    RV_LCL_VAR_LNG_HI,
};


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

class RegSet
{
    friend class CodeGen;
    friend class CodeGenInterface;

private:
    Compiler            *m_rsCompiler;
    GCInfo              &m_rsGCInfo;

public :

    RegSet(Compiler* compiler, GCInfo &gcInfo);

#ifdef _TARGET_ARM_
    regMaskTP           rsMaskPreSpillRegs(bool includeAlignment)
                        {
                            return includeAlignment ? 
                                (rsMaskPreSpillRegArg | rsMaskPreSpillAlign) : rsMaskPreSpillRegArg;
                        }
#endif // _TARGET_ARM_

private:
    // The same descriptor is also used for 'multi-use' register tracking, BTW.
    struct  SpillDsc
    {
        SpillDsc   *        spillNext;    // next spilled value of same reg

        union
        {
            GenTreePtr      spillTree;    // the value that was spilled
#ifdef LEGACY_BACKEND
            LclVarDsc *     spillVarDsc;  // variable if it's an enregistered variable
#endif // LEGACY_BACKEND
        };

        TempDsc    *        spillTemp;    // the temp holding the spilled value

#ifdef LEGACY_BACKEND
        GenTreePtr          spillAddr;    // owning complex address mode or nullptr

        union
        {
            bool            spillMoreMultis;
            bool            bEnregisteredVariable;  // For FP. Indicates that what was spilled was
                                                    // an enregistered variable
        };
#endif // LEGACY_BACKEND

        static SpillDsc   * alloc   (Compiler * pComp, RegSet *regSet, var_types type);
        static void         freeDsc (RegSet *regSet, SpillDsc *spillDsc);
    };

#ifdef LEGACY_BACKEND
public:
    regMaskTP               rsUseIfZero(regMaskTP regs, regMaskTP includeHint);
#endif // LEGACY_BACKEND


    //-------------------------------------------------------------------------
    //
    //  Track the status of the registers
    //
#ifdef LEGACY_BACKEND
public: // TODO-Cleanup: Should be private, but Compiler uses it
    GenTreePtr          rsUsedTree[REG_COUNT];    // trees currently sitting in the registers
private:
    GenTreePtr          rsUsedAddr[REG_COUNT];    // addr for which rsUsedTree[reg] is a part of the addressing mode
    SpillDsc   *        rsMultiDesc[REG_COUNT];   // keeps track of 'multiple-use' registers.
#endif // LEGACY_BACKEND

private:

    bool                rsNeededSpillReg;      // true if this method needed to spill any registers
    regMaskTP           rsModifiedRegsMask;    // mask of the registers modified by the current function.

#ifdef DEBUG
    bool                rsModifiedRegsMaskInitialized;  // Has rsModifiedRegsMask been initialized? Guards against illegal use.
#endif // DEBUG

public:

    regMaskTP           rsGetModifiedRegsMask() const
    {
        assert(rsModifiedRegsMaskInitialized);
        return rsModifiedRegsMask;
    }

    void                rsClearRegsModified();

    void                rsSetRegsModified(regMaskTP mask DEBUG_ARG(bool suppressDump = false));

    void                rsRemoveRegsModified(regMaskTP mask);

    bool                rsRegsModified(regMaskTP mask) const
    {
        assert(rsModifiedRegsMaskInitialized);
        return (rsModifiedRegsMask & mask) != 0;
    }

public: // TODO-Cleanup: Should be private, but GCInfo uses them

#ifdef LEGACY_BACKEND
    regMaskTP           rsMaskUsed;            // currently 'used' registers mask
#endif // LEGACY_BACKEND
    regMaskTP           rsMaskVars;            // mask of registers currently allocated to variables

private:

#ifdef LEGACY_BACKEND
    regMaskTP           rsMaskLock;            // currently 'locked' registers mask
    regMaskTP           rsMaskMult;            // currently 'multiply used' registers mask
#endif // LEGACY_BACKEND

#ifdef _TARGET_ARMARCH_
    regMaskTP           rsMaskCalleeSaved;     // mask of the registers pushed/popped in the prolog/epilog
#endif // _TARGET_ARM_

public: // TODO-Cleanup: Should be private, but Compiler uses it

    regMaskTP           rsMaskResvd;           // mask of the registers that are reserved for special purposes (typically empty)

public: // The PreSpill masks are used in LclVars.cpp

#ifdef _TARGET_ARM_
    regMaskTP           rsMaskPreSpillAlign;   // Mask of alignment padding added to prespill to keep double aligned args
                                               // at aligned stack addresses.
    regMaskTP           rsMaskPreSpillRegArg;  // mask of incoming registers that are spilled at the start of the prolog
                                               // This includes registers used to pass a struct (or part of a struct)
                                               // and all enregistered user arguments in a varargs call
#endif // _TARGET_ARM_

#ifdef LEGACY_BACKEND

private:

    // These getters/setters are ifdef here so that the accesses to these values in sharedfloat.cpp are redirected
    // to the appropriate value.  
    // With FEATURE_STACK_FP_X87 (x86 FP codegen) we have separate register mask that just handle FP registers.  
    // For all other platforms (and eventually on x86) we use unified register masks that handle both kinds.
    //
    regMaskTP           rsGetMaskUsed     ();                    // Getter for rsMaskUsed or rsMaskUsedFloat
    regMaskTP           rsGetMaskVars     ();                    // Getter for rsMaskVars or rsMaskRegVarFloat
    regMaskTP           rsGetMaskLock     ();                    // Getter for rsMaskLock or rsMaskLockedFloat
    regMaskTP           rsGetMaskMult     ();                    // Getter for rsMaskMult or 0

    void                rsSetMaskUsed     (regMaskTP maskUsed);  // Setter for rsMaskUsed or rsMaskUsedFloat
    void                rsSetMaskVars     (regMaskTP maskVars);  // Setter for rsMaskVars or rsMaskRegVarFloat
    void                rsSetMaskLock     (regMaskTP maskLock);  // Setter for rsMaskLock or rsMaskLockedFloat

    void                rsSetUsedTree     (regNumber  regNum, GenTreePtr tree);  // Setter for  rsUsedTree[]/genUsedRegsFloat[]
    void                rsFreeUsedTree    (regNumber  regNum, GenTreePtr tree);  // Free   for  rsUsedTree[]/genUsedRegsFloat[]

public:

    regPairNo           rsFindRegPairNo   (regMaskTP  regMask);

private:
    bool                rsIsTreeInReg     (regNumber  reg, GenTreePtr tree);

    regMaskTP           rsExcludeHint(regMaskTP regs, regMaskTP excludeHint);
    regMaskTP           rsNarrowHint(regMaskTP regs, regMaskTP narrowHint);
    regMaskTP           rsMustExclude(regMaskTP regs, regMaskTP exclude);
    regMaskTP           rsRegMaskFree     ();
    regMaskTP           rsRegMaskCanGrab  ();

    void                rsMarkRegUsed     (GenTreePtr tree, GenTreePtr addr = 0);
    // A special case of "rsMarkRegUsed": the register used is an argument register, used to hold part of
    // the given argument node "promotedStructArg".  (The name suggests that we're likely to use use this
    // for register holding a promoted struct argument, but the implementation doesn't depend on that.)  The
    // "isGCRef" argument indicates whether the register contains a GC reference.
    void                rsMarkArgRegUsedByPromotedFieldArg(GenTreePtr promotedStructArg, regNumber regNum, bool isGCRef);

    void                rsMarkRegPairUsed (GenTreePtr tree);

    void                rsMarkRegFree     (regMaskTP  regMask);
    void                rsMarkRegFree     (regNumber  reg, GenTreePtr tree);
    void                rsMultRegFree     (regMaskTP  regMask);
    unsigned            rsFreeNeededRegCount(regMaskTP  needReg);

    void                rsLockReg         (regMaskTP  regMask);
    void                rsUnlockReg       (regMaskTP  regMask);
    void                rsLockUsedReg     (regMaskTP  regMask);
    void                rsUnlockUsedReg   (regMaskTP  regMask);
    void                rsLockReg         (regMaskTP  regMask, regMaskTP * usedMask);
    void                rsUnlockReg       (regMaskTP  regMask, regMaskTP   usedMask);

    regMaskTP           rsRegExclMask     (regMaskTP  regMask, regMaskTP   rmvMask);

    regNumber           rsPickRegInTmpOrder(regMaskTP  regMask);


public: // used by emitter (!)
    regNumber           rsGrabReg         (regMaskTP    regMask);
private:
    regNumber           rsPickReg         (regMaskTP    regMask = RBM_NONE,
                                            regMaskTP    regBest = RBM_NONE);
public: // used by emitter (!)
    regNumber           rsPickFreeReg     (regMaskTP    regMaskHint = RBM_ALLINT);
private:
    regPairNo           rsGrabRegPair     (regMaskTP    regMask);
    regPairNo           rsPickRegPair     (regMaskTP    regMask);

    class RegisterPreference
    {
    public:
        regMaskTP ok;
        regMaskTP best;
        RegisterPreference(regMaskTP _ok, regMaskTP _best)
        {
            ok = _ok;
            best = _best;
        }
    };
    regNumber       PickRegFloat                            (GenTreePtr tree,
                                                             var_types type = TYP_DOUBLE, 
                                                             RegisterPreference *pref = NULL,
                                                             bool bUsed = true);
    regNumber       PickRegFloat                            (var_types type = TYP_DOUBLE, 
                                                             RegisterPreference *pref = NULL,
                                                             bool bUsed = true);
    regNumber       PickRegFloatOtherThan                   (GenTreePtr tree, var_types type, regNumber reg);
    regNumber       PickRegFloatOtherThan                   (var_types type, regNumber reg);

    regMaskTP       RegFreeFloat                            ();

    void            SetUsedRegFloat                         (GenTreePtr tree, bool bValue);
    void            SetLockedRegFloat                       (GenTreePtr tree, bool bValue);
    bool            IsLockedRegFloat                        (GenTreePtr tree);

    var_types           rsRmvMultiReg     (regNumber    reg);
    void                rsRecMultiReg     (regNumber    reg,  var_types  type);
#endif // LEGACY_BACKEND

public:

#ifdef DEBUG
    /*****************************************************************************
        *  Should we stress register tracking logic ?
        *  This is set via COMPLUS_JitStressRegs.
        *  The following values are ordered, such that any value greater than RS_xx
        *  implies RS_xx.
        *  LSRA defines a different set of values, but uses the same COMPLUS_JitStressRegs
        *  value, with the same notion of relative ordering.
        *  1 = rsPickReg() picks 'bad' registers.
        *  2 = codegen spills at safe points. This is still flaky
        */
    enum                rsStressRegsType { RS_STRESS_NONE  = 0,
                                            RS_PICK_BAD_REG = 01,
                                            RS_SPILL_SAFE   = 02,
                                            };
    rsStressRegsType    rsStressRegs      ();
#endif // DEBUG

private:
    //-------------------------------------------------------------------------
    //
    //  The following tables keep track of spilled register values.
    //

    // When a register gets spilled, the old information is stored here
    SpillDsc*           rsSpillDesc[REG_COUNT];
    SpillDsc*           rsSpillFree;      // list of unused spill descriptors

#ifdef LEGACY_BACKEND
    SpillDsc*           rsSpillFloat;
#endif // LEGACY_BACKEND


    void                rsSpillChk      ();
    void                rsSpillInit     ();
    void                rsSpillDone     ();
    void                rsSpillBeg      ();
    void                rsSpillEnd      ();

    void                rsSpillTree     (regNumber      reg,
                                         GenTreePtr     tree);

#if defined(_TARGET_X86_) && !FEATURE_STACK_FP_X87
    void                rsSpillFPStack(GenTreePtr tree);
#endif // defined(_TARGET_X86_) && !FEATURE_STACK_FP_X87

#ifdef LEGACY_BACKEND
    void                rsSpillReg      (regNumber      reg);
    void                rsSpillRegIfUsed(regNumber      reg);
    void                rsSpillRegs     (regMaskTP      regMask);
#endif // LEGACY_BACKEND

    SpillDsc *          rsGetSpillInfo  (GenTreePtr     tree,
                                         regNumber      reg,
                                         SpillDsc **    pPrevDsc = NULL
#ifdef LEGACY_BACKEND
                                         , SpillDsc **    pMultiDsc = NULL
#endif // LEGACY_BACKEND
                                         );

    TempDsc     *       rsGetSpillTempWord(regNumber    oldReg,
                                           SpillDsc *     dsc,
                                           SpillDsc *     prevDsc);

#ifdef LEGACY_BACKEND
    enum                ExactReg {  ANY_REG, EXACT_REG };
    enum                KeepReg  { FREE_REG, KEEP_REG  };

    regNumber           rsUnspillOneReg (GenTreePtr     tree,
                                        regNumber      oldReg,
                                        KeepReg        willKeepNewReg,
                                        regMaskTP      needReg);
#endif // LEGACY_BACKEND

    TempDsc *           rsUnspillInPlace(GenTreePtr     tree);

#ifdef LEGACY_BACKEND
    void                rsUnspillReg    (GenTreePtr     tree,
                                        regMaskTP      needReg,
                                        KeepReg        keepReg);

    void                rsUnspillRegPair(GenTreePtr     tree,
                                        regMaskTP      needReg,
                                        KeepReg        keepReg);
#endif // LEGACY_BACKEND

    void                rsMarkSpill     (GenTreePtr     tree,
                                        regNumber      reg);

#ifdef LEGACY_BACKEND
    void                rsMarkUnspill   (GenTreePtr     tree,
                                        regNumber      reg);
#endif // LEGACY_BACKEND

#if FEATURE_STACK_FP_X87
    regMaskTP       rsMaskUsedFloat;
    regMaskTP       rsMaskRegVarFloat;
    regMaskTP       rsMaskLockedFloat;
    GenTreePtr      genUsedRegsFloat[REG_FPCOUNT];
    LclVarDsc*      genRegVarsFloat[REG_FPCOUNT];
#endif // FEATURE_STACK_FP_X87

};

//-------------------------------------------------------------------------
//
//  These are used to track the contents of the registers during
//  code generation.
//
//  Only integer registers are tracked.
//

struct      RegValDsc
{
    regValKind          rvdKind;
    union
    {
        ssize_t              rvdIntCnsVal;  // for rvdKind == RV_INT_CNS
        unsigned             rvdLclVarNum;  // for rvdKind == RV_LCL_VAR, RV_LCL_VAR_LNG_LO, RV_LCL_VAR_LNG_HI
    };
};

class  RegTracker
{
    Compiler*           compiler;
    RegSet*             regSet;
    RegValDsc           rsRegValues[REG_COUNT];

public:

    void                rsTrackInit(Compiler* comp, RegSet* rs)
    {
        compiler = comp;
        regSet = rs;
        rsTrackRegClr();
    }

    void                rsTrackRegClr     ();
    void                rsTrackRegClrPtr  ();
    void                rsTrackRegTrash   (regNumber reg);
    void                rsTrackRegMaskTrash(regMaskTP regMask);
    regMaskTP           rsTrashRegsForGCInterruptability();
    void                rsTrackRegIntCns  (regNumber reg, ssize_t val);
    void                rsTrackRegLclVar  (regNumber reg, unsigned var);
    void                rsTrackRegLclVarLng(regNumber reg, unsigned var, bool low);
    bool                rsTrackIsLclVarLng(regValKind rvKind);
    void                rsTrackRegClsVar  (regNumber reg, GenTreePtr clsVar);
    void                rsTrackRegCopy    (regNumber reg1, regNumber reg2);
    void                rsTrackRegSwap    (regNumber reg1, regNumber reg2);
    void                rsTrackRegAssign  (GenTree *op1, GenTree *op2);

    regNumber           rsIconIsInReg     (ssize_t  val,  ssize_t* closeDelta = NULL);
    bool                rsIconIsInReg     (ssize_t  val,  regNumber reg);
    regNumber           rsLclIsInReg      (unsigned var);
    regPairNo           rsLclIsInRegPair  (unsigned var);

    //---------------------- Load suppression ---------------------------------

#if REDUNDANT_LOAD

    void                rsTrashLclLong    (unsigned     var);
    void                rsTrashLcl        (unsigned     var);
    void                rsTrashRegSet     (regMaskTP    regMask);

    regMaskTP           rsUselessRegs     ();

#endif // REDUNDANT_LOAD

};
#endif // _REGSET_H