summaryrefslogtreecommitdiff
path: root/src/jit/codegeninterface.h
blob: 2d7ec097f54fecdb331047718d276c13a345f953 (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
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

//
// This file declares the types that constitute the interface between the
// code generator (CodeGen class) and the rest of the JIT.
//
// RegState
//
// CodeGenInterface includes only the public methods that are called by
// the Compiler.
//
// CodeGenContext contains the shared context between the code generator
// and other phases of the JIT, especially the register allocator and
// GC encoder.  It is distinct from CodeGenInterface so that it can be
// included in the Compiler object, and avoid an extra indirection when
// accessed from members of Compiler.
//

#ifndef _CODEGEN_INTERFACE_H_
#define _CODEGEN_INTERFACE_H_

#include "regset.h"
#include "jitgcinfo.h"
#include "treelifeupdater.h"
#include "emit.h"

#if 1
// Enable USING_SCOPE_INFO flag to use psiScope/siScope info to report variables' locations.
#define USING_SCOPE_INFO
#endif
#if 0
// Enable USING_VARIABLE_LIVE_RANGE flag to use VariableLiveRange info to report variables' locations.
// Note: if both USING_SCOPE_INFO and USING_VARIABLE_LIVE_RANGE are defined, then USING_SCOPE_INFO
// information is reported to the debugger.
#define USING_VARIABLE_LIVE_RANGE
#endif

// Forward reference types

class CodeGenInterface;
class emitter;

// Small helper types

//-------------------- Register selection ---------------------------------

struct RegState
{
    regMaskTP rsCalleeRegArgMaskLiveIn; // mask of register arguments (live on entry to method)
    unsigned  rsCalleeRegArgCount;      // total number of incoming register arguments of this kind (int or float)
    bool      rsIsFloat;                // true for float argument registers, false for integer argument registers
};

//-------------------- CodeGenInterface ---------------------------------
// interface to hide the full CodeGen implementation from rest of Compiler

CodeGenInterface* getCodeGenerator(Compiler* comp);

class CodeGenInterface
{
    friend class emitter;

public:
    CodeGenInterface(Compiler* theCompiler);
    virtual void genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode) = 0;

    // genSpillVar is called by compUpdateLifeVar.
    // TODO-Cleanup: We should handle the spill directly in CodeGen, rather than
    // calling it from compUpdateLifeVar.  Then this can be non-virtual.

    virtual void genSpillVar(GenTree* tree) = 0;

    //-------------------------------------------------------------------------
    //  The following property indicates whether to align loops.
    //  (Used to avoid effects of loop alignment when diagnosing perf issues.)
    __declspec(property(get = doAlignLoops, put = setAlignLoops)) bool genAlignLoops;
    bool doAlignLoops()
    {
        return m_genAlignLoops;
    }
    void setAlignLoops(bool value)
    {
        m_genAlignLoops = value;
    }

    // TODO-Cleanup: Abstract out the part of this that finds the addressing mode, and
    // move it to Lower
    virtual bool genCreateAddrMode(GenTree*  addr,
                                   bool      fold,
                                   bool*     revPtr,
                                   GenTree** rv1Ptr,
                                   GenTree** rv2Ptr,
#if SCALED_ADDR_MODES
                                   unsigned* mulPtr,
#endif // SCALED_ADDR_MODES
                                   ssize_t* cnsPtr) = 0;

    GCInfo gcInfo;

    RegSet   regSet;
    RegState intRegState;
    RegState floatRegState;

protected:
    Compiler* compiler;
    bool      m_genAlignLoops;

private:
#if defined(_TARGET_XARCH_)
    static const insFlags instInfo[INS_count];
#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
    static const BYTE instInfo[INS_count];
#else
#error Unsupported target architecture
#endif

#define INST_FP 0x01 // is it a FP instruction?
public:
    static bool instIsFP(instruction ins);

    //-------------------------------------------------------------------------
    // Liveness-related fields & methods
public:
    void genUpdateRegLife(const LclVarDsc* varDsc, bool isBorn, bool isDying DEBUGARG(GenTree* tree));
    void genUpdateVarReg(LclVarDsc* varDsc, GenTree* tree);

protected:
#ifdef DEBUG
    VARSET_TP genTempOldLife;
    bool      genTempLiveChg;
#endif

    VARSET_TP genLastLiveSet;  // A one element map (genLastLiveSet-> genLastLiveMask)
    regMaskTP genLastLiveMask; // these two are used in genLiveMask

    regMaskTP genGetRegMask(const LclVarDsc* varDsc);
    regMaskTP genGetRegMask(GenTree* tree);

    void genUpdateLife(GenTree* tree);
    void genUpdateLife(VARSET_VALARG_TP newLife);

    TreeLifeUpdater<true>* treeLifeUpdater;

public:
    bool genUseOptimizedWriteBarriers(GCInfo::WriteBarrierForm wbf);
    bool genUseOptimizedWriteBarriers(GenTree* tgt, GenTree* assignVal);
    CorInfoHelpFunc genWriteBarrierHelperForWriteBarrierForm(GenTree* tgt, GCInfo::WriteBarrierForm wbf);

    // The following property indicates whether the current method sets up
    // an explicit stack frame or not.
private:
    PhasedVar<bool> m_cgFramePointerUsed;

public:
    bool isFramePointerUsed() const
    {
        return m_cgFramePointerUsed;
    }
    void setFramePointerUsed(bool value)
    {
        m_cgFramePointerUsed = value;
    }
    void resetFramePointerUsedWritePhase()
    {
        m_cgFramePointerUsed.ResetWritePhase();
    }

    // The following property indicates whether the current method requires
    // an explicit frame. Does not prohibit double alignment of the stack.
private:
    PhasedVar<bool> m_cgFrameRequired;

public:
    bool isFrameRequired() const
    {
        return m_cgFrameRequired;
    }
    void setFrameRequired(bool value)
    {
        m_cgFrameRequired = value;
    }

public:
    int genCallerSPtoFPdelta() const;
    int genCallerSPtoInitialSPdelta() const;
    int genSPtoFPdelta() const;
    int genTotalFrameSize() const;

#ifdef _TARGET_ARM64_
    virtual void SetSaveFpLrWithAllCalleeSavedRegisters(bool value) = 0;
    virtual bool IsSaveFpLrWithAllCalleeSavedRegisters() const      = 0;
#endif // _TARGET_ARM64_

    regNumber genGetThisArgReg(GenTreeCall* call) const;

#ifdef _TARGET_XARCH_
#ifdef _TARGET_AMD64_
    // There are no reloc hints on x86
    unsigned short genAddrRelocTypeHint(size_t addr);
#endif
    bool genDataIndirAddrCanBeEncodedAsPCRelOffset(size_t addr);
    bool genCodeIndirAddrCanBeEncodedAsPCRelOffset(size_t addr);
    bool genCodeIndirAddrCanBeEncodedAsZeroRelOffset(size_t addr);
    bool genCodeIndirAddrNeedsReloc(size_t addr);
    bool genCodeAddrNeedsReloc(size_t addr);
#endif

    // If both isFramePointerRequired() and isFrameRequired() are false, the method is eligible
    // for Frame-Pointer-Omission (FPO).

    // The following property indicates whether the current method requires
    // an explicit stack frame, and all arguments and locals to be
    // accessible relative to the Frame Pointer. Prohibits double alignment
    // of the stack.
private:
    PhasedVar<bool> m_cgFramePointerRequired;

public:
    bool isFramePointerRequired() const
    {
        return m_cgFramePointerRequired;
    }

    void setFramePointerRequired(bool value)
    {
        m_cgFramePointerRequired = value;
    }

    //------------------------------------------------------------------------
    // resetWritePhaseForFramePointerRequired: Return m_cgFramePointerRequired into the write phase.
    // It is used only before the first phase, that locks this value, currently it is LSRA.
    // Use it if you want to skip checks that set this value to true if the value is already true.
    void resetWritePhaseForFramePointerRequired()
    {
        m_cgFramePointerRequired.ResetWritePhase();
    }

    void setFramePointerRequiredEH(bool value);

    void setFramePointerRequiredGCInfo(bool value)
    {
#ifdef JIT32_GCENCODER
        m_cgFramePointerRequired = value;
#endif
    }

#if DOUBLE_ALIGN
    // The following property indicates whether we going to double-align the frame.
    // Arguments are accessed relative to the Frame Pointer (EBP), and
    // locals are accessed relative to the Stack Pointer (ESP).
public:
    bool doDoubleAlign() const
    {
        return m_cgDoubleAlign;
    }
    void setDoubleAlign(bool value)
    {
        m_cgDoubleAlign = value;
    }
    bool doubleAlignOrFramePointerUsed() const
    {
        return isFramePointerUsed() || doDoubleAlign();
    }

private:
    bool m_cgDoubleAlign;
#else // !DOUBLE_ALIGN

public:
    bool doubleAlignOrFramePointerUsed() const
    {
        return isFramePointerUsed();
    }

#endif // !DOUBLE_ALIGN

#ifdef DEBUG
    // The following is used to make sure the value of 'genInterruptible' isn't
    // changed after it's been used by any logic that depends on its value.
public:
    bool isGCTypeFixed()
    {
        return genInterruptibleUsed;
    }

protected:
    bool genInterruptibleUsed;
#endif

public:
    unsigned InferStructOpSizeAlign(GenTree* op, unsigned* alignmentWB);
    unsigned InferOpSizeAlign(GenTree* op, unsigned* alignmentWB);

    void genMarkTreeInReg(GenTree* tree, regNumber reg);

    // Methods to abstract target information

    bool validImmForInstr(instruction ins, target_ssize_t val, insFlags flags = INS_FLAGS_DONT_CARE);
    bool validDispForLdSt(target_ssize_t disp, var_types type);
    bool validImmForAdd(target_ssize_t imm, insFlags flags);
    bool validImmForAlu(target_ssize_t imm);
    bool validImmForMov(target_ssize_t imm);
    bool validImmForBL(ssize_t addr);

    instruction ins_Load(var_types srcType, bool aligned = false);
    instruction ins_Store(var_types dstType, bool aligned = false);
    static instruction ins_FloatLoad(var_types type = TYP_DOUBLE);

    // Methods for spilling - used by RegSet
    void spillReg(var_types type, TempDsc* tmp, regNumber reg);
    void reloadReg(var_types type, TempDsc* tmp, regNumber reg);

    // The following method is used by xarch emitter for handling contained tree temps.
    TempDsc* getSpillTempDsc(GenTree* tree);

public:
    emitter* getEmitter() const
    {
        return m_cgEmitter;
    }

protected:
    emitter* m_cgEmitter;

#ifdef LATE_DISASM
public:
    DisAssembler& getDisAssembler()
    {
        return m_cgDisAsm;
    }

protected:
    DisAssembler m_cgDisAsm;
#endif // LATE_DISASM

public:
#ifdef DEBUG
    void setVerbose(bool value)
    {
        verbose = value;
    }
    bool verbose;
#endif // DEBUG

    // The following is set to true if we've determined that the current method
    // is to be fully interruptible.
    //
public:
    __declspec(property(get = getInterruptible, put = setInterruptible)) bool genInterruptible;
    bool getInterruptible()
    {
        return m_cgInterruptible;
    }
    void setInterruptible(bool value)
    {
        m_cgInterruptible = value;
    }

#ifdef _TARGET_ARMARCH_
    __declspec(property(get = getHasTailCalls, put = setHasTailCalls)) bool hasTailCalls;
    bool getHasTailCalls()
    {
        return m_cgHasTailCalls;
    }
    void setHasTailCalls(bool value)
    {
        m_cgHasTailCalls = value;
    }
#endif // _TARGET_ARMARCH_

private:
    bool m_cgInterruptible;
#ifdef _TARGET_ARMARCH_
    bool m_cgHasTailCalls;
#endif // _TARGET_ARMARCH_

    //  The following will be set to true if we've determined that we need to
    //  generate a full-blown pointer register map for the current method.
    //  Currently it is equal to (genInterruptible || !isFramePointerUsed())
    //  (i.e. We generate the full-blown map for EBP-less methods and
    //        for fully interruptible methods)
    //
public:
    __declspec(property(get = doFullPtrRegMap, put = setFullPtrRegMap)) bool genFullPtrRegMap;
    bool doFullPtrRegMap()
    {
        return m_cgFullPtrRegMap;
    }
    void setFullPtrRegMap(bool value)
    {
        m_cgFullPtrRegMap = value;
    }

private:
    bool m_cgFullPtrRegMap;

public:
#ifdef USING_SCOPE_INFO
    virtual void siUpdate() = 0;
#endif // USING_SCOPE_INFO

    /* These are the different addressing modes used to access a local var.
     * The JIT has to report the location of the locals back to the EE
     * for debugging purposes.
     */

    enum siVarLocType
    {
        VLT_REG,
        VLT_REG_BYREF, // this type is currently only used for value types on X64
        VLT_REG_FP,
        VLT_STK,
        VLT_STK_BYREF, // this type is currently only used for value types on X64
        VLT_REG_REG,
        VLT_REG_STK,
        VLT_STK_REG,
        VLT_STK2,
        VLT_FPSTK,
        VLT_FIXED_VA,

        VLT_COUNT,
        VLT_INVALID
    };

    struct siVarLoc
    {
        siVarLocType vlType;

        union {
            // VLT_REG/VLT_REG_FP -- Any pointer-sized enregistered value (TYP_INT, TYP_REF, etc)
            // eg. EAX
            // VLT_REG_BYREF -- the specified register contains the address of the variable
            // eg. [EAX]

            struct
            {
                regNumber vlrReg;
            } vlReg;

            // VLT_STK       -- Any 32 bit value which is on the stack
            // eg. [ESP+0x20], or [EBP-0x28]
            // VLT_STK_BYREF -- the specified stack location contains the address of the variable
            // eg. mov EAX, [ESP+0x20]; [EAX]

            struct
            {
                regNumber     vlsBaseReg;
                NATIVE_OFFSET vlsOffset;
            } vlStk;

            // VLT_REG_REG -- TYP_LONG/TYP_DOUBLE with both DWords enregistered
            // eg. RBM_EAXEDX

            struct
            {
                regNumber vlrrReg1;
                regNumber vlrrReg2;
            } vlRegReg;

            // VLT_REG_STK -- Partly enregistered TYP_LONG/TYP_DOUBLE
            // eg { LowerDWord=EAX UpperDWord=[ESP+0x8] }

            struct
            {
                regNumber vlrsReg;

                struct
                {
                    regNumber     vlrssBaseReg;
                    NATIVE_OFFSET vlrssOffset;
                } vlrsStk;
            } vlRegStk;

            // VLT_STK_REG -- Partly enregistered TYP_LONG/TYP_DOUBLE
            // eg { LowerDWord=[ESP+0x8] UpperDWord=EAX }

            struct
            {
                struct
                {
                    regNumber     vlsrsBaseReg;
                    NATIVE_OFFSET vlsrsOffset;
                } vlsrStk;

                regNumber vlsrReg;
            } vlStkReg;

            // VLT_STK2 -- Any 64 bit value which is on the stack, in 2 successsive DWords
            // eg 2 DWords at [ESP+0x10]

            struct
            {
                regNumber     vls2BaseReg;
                NATIVE_OFFSET vls2Offset;
            } vlStk2;

            // VLT_FPSTK -- enregisterd TYP_DOUBLE (on the FP stack)
            // eg. ST(3). Actually it is ST("FPstkHeight - vpFpStk")

            struct
            {
                unsigned vlfReg;
            } vlFPstk;

            // VLT_FIXED_VA -- fixed argument of a varargs function.
            // The argument location depends on the size of the variable
            // arguments (...). Inspecting the VARARGS_HANDLE indicates the
            // location of the first arg. This argument can then be accessed
            // relative to the position of the first arg

            struct
            {
                unsigned vlfvOffset;
            } vlFixedVarArg;

            // VLT_MEMORY

            struct
            {
                void* rpValue; // pointer to the in-process
                               // location of the value.
            } vlMemory;
        };

        // Helper functions

        bool vlIsInReg(regNumber reg) const;
        bool vlIsOnStack(regNumber reg, signed offset) const;
        bool vlIsOnStack() const;

        void storeVariableInRegisters(regNumber reg, regNumber otherReg);
        void storeVariableOnStack(regNumber stackBaseReg, NATIVE_OFFSET variableStackOffset);

        siVarLoc(const LclVarDsc* varDsc, regNumber baseReg, int offset, bool isFramePointerUsed);
        siVarLoc(){};

        // An overload for the equality comparator
        static bool Equals(const siVarLoc* lhs, const siVarLoc* rhs);

    private:
        // Fill "siVarLoc" properties indicating the register position of the variable
        // using "LclVarDsc" and "baseReg"/"offset" if it has a part in the stack (x64 bit float or long).
        void siFillRegisterVarLoc(
            const LclVarDsc* varDsc, var_types type, regNumber baseReg, int offset, bool isFramePointerUsed);

        // Fill "siVarLoc" properties indicating the register position of the variable
        // using "LclVarDsc" and "baseReg"/"offset" if it is a variable with part in a register and
        // part in thestack
        void siFillStackVarLoc(
            const LclVarDsc* varDsc, var_types type, regNumber baseReg, int offset, bool isFramePointerUsed);
    };

public:
    siVarLoc getSiVarLoc(const LclVarDsc* varDsc, unsigned int stackLevel) const;

#ifdef DEBUG
    void dumpSiVarLoc(const siVarLoc* varLoc) const;
#endif

    unsigned getCurrentStackLevel() const;

protected:
    //  Keeps track of how many bytes we've pushed on the processor's stack.
    unsigned genStackLevel;

public:
#ifdef USING_VARIABLE_LIVE_RANGE
    //--------------------------------------------
    //
    // VariableLiveKeeper: Holds an array of "VariableLiveDescriptor", one for each variable
    //  whose location we track. It provides start/end/update/count operations over the
    //  "LiveRangeList" of any variable.
    //
    // Notes:
    //  This method could be implemented on Compiler class too, but the intention is to move code
    //  out of that class, which is huge. With this solution the only code needed in Compiler is
    //  a getter and an initializer of this class.
    //  The index of each variable in this array corresponds to the one in "compiler->lvaTable".
    //  We care about tracking the variable locations of arguments, special arguments, and local IL
    //  variables, and we ignore any other variable (like JIT temporary variables).
    //
    class VariableLiveKeeper
    {
    public:
        //--------------------------------------------
        //
        // VariableLiveRange: Represent part of the life of a variable. A
        //      variable lives in a location (represented with struct "siVarLoc")
        //      between two native offsets.
        //
        // Notes:
        //    We use emitLocation and not NATTIVE_OFFSET because location
        //    is captured when code is being generated (genCodeForBBList
        //    and genGeneratePrologsAndEpilogs) but only after the whole
        //    method's code is generated can we obtain a final, fixed
        //    NATIVE_OFFSET representing the actual generated code offset.
        //    There is also a IL_OFFSET, but this is more accurate and the
        //    debugger is expecting assembly offsets.
        //    This class doesn't have behaviour attached to itself, it is
        //    just putting a name to a representation. It is used to build
        //    typedefs LiveRangeList and LiveRangeListIterator, which are
        //    basically a list of this class and a const_iterator of that
        //    list.
        //
        class VariableLiveRange
        {
        public:
            emitLocation               m_StartEmitLocation; // first position from where "m_VarLocation" becomes valid
            emitLocation               m_EndEmitLocation;   // last position where "m_VarLocation" is valid
            CodeGenInterface::siVarLoc m_VarLocation;       // variable location

            VariableLiveRange(CodeGenInterface::siVarLoc varLocation,
                              emitLocation               startEmitLocation,
                              emitLocation               endEmitLocation)
                : m_StartEmitLocation(startEmitLocation), m_EndEmitLocation(endEmitLocation), m_VarLocation(varLocation)
            {
            }

#ifdef DEBUG
            // Dump "VariableLiveRange" when code has not been generated. We don't have the native code offset,
            // but we do have "emitLocation"s and "siVarLoc".
            void dumpVariableLiveRange(const CodeGenInterface* codeGen) const;

            // Dump "VariableLiveRange" when code has been generated and we have the native code offset of each
            // "emitLocation"
            void dumpVariableLiveRange(emitter* emit, const CodeGenInterface* codeGen) const;
#endif // DEBUG
        };

        typedef jitstd::list<VariableLiveRange> LiveRangeList;
        typedef LiveRangeList::const_iterator   LiveRangeListIterator;

    private:
#ifdef DEBUG
        //--------------------------------------------
        //
        // LiveRangeDumper: Used for debugging purposes during code
        //  generation on genCodeForBBList. Keeps an iterator to the first
        //  edited/added "VariableLiveRange" of a variable during the
        //  generation of code of one block.
        //
        // Notes:
        //  The first "VariableLiveRange" reported for a variable during
        //  a BasicBlock is sent to "setDumperStartAt" so we can dump all
        //  the "VariableLiveRange"s from that one.
        //  After we dump all the "VariableLiveRange"s we call "reset" with
        //  the "liveRangeList" to set the barrier to nullptr or the last
        //  "VariableLiveRange" if it is opened.
        //  If no "VariableLiveRange" was edited/added during block,
        //  the iterator points to the end of variable's LiveRangeList.
        //
        class LiveRangeDumper
        {
            // Iterator to the first edited/added position during actual block code generation. If last
            // block had a closed "VariableLiveRange" (with a valid "m_EndEmitLocation") and not changes
            // were applied to variable liveness, it points to the end of variable's LiveRangeList.
            LiveRangeListIterator m_StartingLiveRange;
            bool                  m_hasLiveRangestoDump; // True if a live range for this variable has been
                                                         // reported from last call to EndBlock

        public:
            LiveRangeDumper(const LiveRangeList* liveRanges)
                : m_StartingLiveRange(liveRanges->end()), m_hasLiveRangestoDump(false){};

            // Make the dumper point to the last "VariableLiveRange" opened or nullptr if all are closed
            void resetDumper(const LiveRangeList* list);

            // Make "LiveRangeDumper" instance points the last "VariableLiveRange" added so we can
            // start dumping from there after the actual "BasicBlock"s code is generated.
            void setDumperStartAt(const LiveRangeListIterator liveRangeIt);

            // Return an iterator to the first "VariableLiveRange" edited/added during the current
            // "BasicBlock"
            LiveRangeListIterator getStartForDump() const;

            // Return whether at least a "VariableLiveRange" was alive during the current "BasicBlock"'s
            // code generation
            bool hasLiveRangesToDump() const;
        };
#endif // DEBUG

        //--------------------------------------------
        //
        // VariableLiveDescriptor: This class persist and update all the changes
        //  to the home of a variable. It has an instance of "LiveRangeList"
        //  and methods to report the start/end of a VariableLiveRange.
        //
        class VariableLiveDescriptor
        {
            LiveRangeList* m_VariableLiveRanges; // the variable locations of this variable
            INDEBUG(LiveRangeDumper* m_VariableLifeBarrier);

        public:
            VariableLiveDescriptor(CompAllocator allocator);

            bool           hasVariableLiveRangeOpen() const;
            LiveRangeList* getLiveRanges() const;

            void startLiveRangeFromEmitter(CodeGenInterface::siVarLoc varLocation, emitter* emit) const;
            void endLiveRangeAtEmitter(emitter* emit) const;
            void updateLiveRangeAtEmitter(CodeGenInterface::siVarLoc varLocation, emitter* emit) const;

#ifdef DEBUG
            void dumpAllRegisterLiveRangesForBlock(emitter* emit, const CodeGenInterface* codeGen) const;
            void dumpRegisterLiveRangesForBlockBeforeCodeGenerated(const CodeGenInterface* codeGen) const;
            bool hasVarLiveRangesToDump() const;
            bool hasVarLiveRangesFromLastBlockToDump() const;
            void endBlockLiveRanges();
#endif // DEBUG
        };

        unsigned int m_LiveDscCount;  // count of args, special args, and IL local variables to report home
        unsigned int m_LiveArgsCount; // count of arguments to report home

        Compiler* m_Compiler;

        VariableLiveDescriptor* m_vlrLiveDsc; // Array of descriptors that manage VariableLiveRanges.
                                              // Its indices correspond to lvaTable indexes (or lvSlotNum).

        VariableLiveDescriptor* m_vlrLiveDscForProlog; // Array of descriptors that manage VariableLiveRanges.
                                                       // Its indices correspond to lvaTable indexes (or lvSlotNum).

        bool m_LastBasicBlockHasBeenEmited; // When true no more siEndVariableLiveRange is considered.
                                            // No update/start happens when code has been generated.

    public:
        VariableLiveKeeper(unsigned int  totalLocalCount,
                           unsigned int  argsCount,
                           Compiler*     compiler,
                           CompAllocator allocator);

        // For tracking locations during code generation
        void siStartOrCloseVariableLiveRange(const LclVarDsc* varDsc, unsigned int varNum, bool isBorn, bool isDying);
        void siStartOrCloseVariableLiveRanges(VARSET_VALARG_TP varsIndexSet, bool isBorn, bool isDying);
        void siStartVariableLiveRange(const LclVarDsc* varDsc, unsigned int varNum);
        void siEndVariableLiveRange(unsigned int varNum);
        void siUpdateVariableLiveRange(const LclVarDsc* varDsc, unsigned int varNum);
        void siEndAllVariableLiveRange(VARSET_VALARG_TP varsToClose);
        void siEndAllVariableLiveRange();

        LiveRangeList* getLiveRangesForVarForBody(unsigned int varNum) const;
        LiveRangeList* getLiveRangesForVarForProlog(unsigned int varNum) const;
        size_t getLiveRangesCount() const;

        // For parameters locations on prolog
        void psiStartVariableLiveRange(CodeGenInterface::siVarLoc varLocation, unsigned int varNum);
        void psiClosePrologVariableRanges();

#ifdef DEBUG
        void dumpBlockVariableLiveRanges(const BasicBlock* block);
        void dumpLvaVariableLiveRanges() const;
#endif // DEBUG
    };

    void initializeVariableLiveKeeper();

    VariableLiveKeeper* getVariableLiveKeeper() const;

protected:
    VariableLiveKeeper* varLiveKeeper; // Used to manage VariableLiveRanges of variables
#endif                                 // USING_VARIABLE_LIVE_RANGE

#ifdef LATE_DISASM
public:
    virtual const char* siRegVarName(size_t offs, size_t size, unsigned reg) = 0;

    virtual const char* siStackVarName(size_t offs, size_t size, unsigned reg, unsigned stkOffs) = 0;
#endif // LATE_DISASM
};

#endif // _CODEGEN_INTERFACE_H_