summaryrefslogtreecommitdiff
path: root/src/jit/unwind.h
blob: 06396f2a9af774289696ba777d3bcf85594d708a (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
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
// 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.

/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XX                                                                           XX
XX                              Unwind Info                                  XX
XX                                                                           XX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/

#ifdef _TARGET_ARMARCH_

// Windows no longer imposes a maximum prolog size. However, we still have an
// assert here just to inform us if we increase the size of the prolog
// accidentally, as there is still a slight performance advantage in the
// OS unwinder to having as few unwind codes as possible.
// You can increase this "max" number if necessary.

#if defined(_TARGET_ARM_)
const unsigned MAX_PROLOG_SIZE_BYTES = 44;
const unsigned MAX_EPILOG_SIZE_BYTES = 44;
#define UWC_END 0xFF // "end" unwind code
#define UW_MAX_FRAGMENT_SIZE_BYTES (1U << 19)
#define UW_MAX_CODE_WORDS_COUNT 15      // Max number that can be encoded in the "Code Words" field of the .pdata record
#define UW_MAX_EPILOG_START_INDEX 0xFFU // Max number that can be encoded in the "Epilog Start Index" field
                                        // of the .pdata record
#elif defined(_TARGET_ARM64_)
const unsigned MAX_PROLOG_SIZE_BYTES = 100;
const unsigned MAX_EPILOG_SIZE_BYTES = 100;
#define UWC_END 0xE4   // "end" unwind code
#define UWC_END_C 0xE5 // "end_c" unwind code
#define UW_MAX_FRAGMENT_SIZE_BYTES (1U << 20)
#define UW_MAX_CODE_WORDS_COUNT 31
#define UW_MAX_EPILOG_START_INDEX 0x3FFU
#endif // _TARGET_ARM64_

#define UW_MAX_EPILOG_COUNT 31                 // Max number that can be encoded in the "Epilog count" field
                                               // of the .pdata record
#define UW_MAX_EXTENDED_CODE_WORDS_COUNT 0xFFU // Max number that can be encoded in the "Extended Code Words"
                                               // field of the .pdata record
#define UW_MAX_EXTENDED_EPILOG_COUNT 0xFFFFU   // Max number that can be encoded in the "Extended Epilog Count"
                                               // field of the .pdata record
#define UW_MAX_EPILOG_START_OFFSET 0x3FFFFU    // Max number that can be encoded in the "Epilog Start Offset"
                                               // field of the .pdata record

//
// Forward declaration of class defined in emit.h
//

class emitLocation;

//
// Forward declarations of classes defined in this file
//

class UnwindCodesBase;
class UnwindPrologCodes;
class UnwindEpilogCodes;
class UnwindEpilogInfo;
class UnwindFragmentInfo;
class UnwindInfo;

// UnwindBase: A base class shared by the the unwind classes that require
// a Compiler* for memory allocation.

class UnwindBase
{
protected:
    UnwindBase(Compiler* comp) : uwiComp(comp)
    {
    }

    UnwindBase()
    {
    }
    ~UnwindBase()
    {
    }

// TODO: How do we get the ability to access uwiComp without error on Clang?
#if defined(DEBUG) && !defined(__GNUC__)

    template <typename T>
    T dspPtr(T p)
    {
        return uwiComp->dspPtr(p);
    }

    template <typename T>
    T dspOffset(T o)
    {
        return uwiComp->dspOffset(o);
    }

    static const char* dspBool(bool b)
    {
        return (b) ? "true" : "false";
    }

#endif // DEBUG

    //
    // Data
    //

    Compiler* uwiComp;
};

// UnwindCodesBase: A base class shared by the the classes used to represent the prolog
// and epilog unwind codes.

class UnwindCodesBase
{
public:
    // Add a single unwind code.

    virtual void AddCode(BYTE b1) = 0;
    virtual void AddCode(BYTE b1, BYTE b2) = 0;
    virtual void AddCode(BYTE b1, BYTE b2, BYTE b3) = 0;
    virtual void AddCode(BYTE b1, BYTE b2, BYTE b3, BYTE b4) = 0;

    // Get access to the unwind codes

    virtual BYTE* GetCodes() = 0;

    bool IsEndCode(BYTE b)
    {
#if defined(_TARGET_ARM_)
        return b >= 0xFD;
#elif defined(_TARGET_ARM64_)
        return (b == UWC_END); // TODO-ARM64-Bug?: what about the "end_c" code?
#endif // _TARGET_ARM64_
    }

#ifdef DEBUG

    unsigned GetCodeSizeFromUnwindCodes(bool isProlog);

#endif // DEBUG
};

// UnwindPrologCodes: represents the unwind codes for a prolog sequence.
// Prolog unwind codes arrive in reverse order from how they will be emitted.
// Store them as a stack, storing from the end of an array towards the beginning.
// This class is also re-used as the final location of the consolidated unwind
// information for a function, including unwind info header, the prolog codes,
// and any epilog codes.

class UnwindPrologCodes : public UnwindBase, public UnwindCodesBase
{
    // UPC_LOCAL_COUNT is the amount of memory local to this class. For ARM mscorlib.dll, the maximum size is 34.
    // Here is a histogram of other interesting sizes:
    //     <=16  79%
    //     <=24  96%
    //     <=32  99%
    // From this data, we choose to use 24.

    static const int UPC_LOCAL_COUNT = 24;

public:
    UnwindPrologCodes(Compiler* comp)
        : UnwindBase(comp)
        , upcMem(upcMemLocal)
        , upcMemSize(UPC_LOCAL_COUNT)
        , upcCodeSlot(UPC_LOCAL_COUNT)
        , upcHeaderSlot(-1)
        , upcEpilogSlot(-1)
    {
        // Assume we've got a normal end code.
        // Push four so we can generate an array that is a multiple of 4 bytes in size with the
        // end codes (and padding) already in place. One is the end code for the prolog codes,
        // three are end-of-array alignment padding.
        PushByte(UWC_END);
        PushByte(UWC_END);
        PushByte(UWC_END);
        PushByte(UWC_END);
    }

    //
    // Implementation of UnwindCodesBase
    //

    virtual void AddCode(BYTE b1)
    {
        PushByte(b1);
    }

    virtual void AddCode(BYTE b1, BYTE b2)
    {
        PushByte(b2);
        PushByte(b1);
    }

    virtual void AddCode(BYTE b1, BYTE b2, BYTE b3)
    {
        PushByte(b3);
        PushByte(b2);
        PushByte(b1);
    }

    virtual void AddCode(BYTE b1, BYTE b2, BYTE b3, BYTE b4)
    {
        PushByte(b4);
        PushByte(b3);
        PushByte(b2);
        PushByte(b1);
    }

    // Return a pointer to the first unwind code byte
    virtual BYTE* GetCodes()
    {
        assert(upcCodeSlot < upcMemSize); // There better be at least one code!
        return &upcMem[upcCodeSlot];
    }

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

    BYTE GetByte(int index)
    {
        assert(upcCodeSlot <= index && index < upcMemSize);
        return upcMem[index];
    }

    // Push a single byte on the unwind code stack
    void PushByte(BYTE b)
    {
        if (upcCodeSlot == 0)
        {
            // We've run out of space! Reallocate, and copy everything to a new array.
            EnsureSize(upcMemSize + 1);
        }

        --upcCodeSlot;
        noway_assert(0 <= upcCodeSlot && upcCodeSlot < upcMemSize);

        upcMem[upcCodeSlot] = b;
    }

    // Return the size of the unwind codes, in bytes. The size is the exact size, not an aligned size.
    // The size includes exactly one "end" code.
    int Size()
    {
        // -3 because we put 4 "end" codes at the end in the constructor, and we shouldn't count that here
        return upcMemSize - upcCodeSlot - 3;
    }

    void SetFinalSize(int headerBytes, int epilogBytes);

    void AddHeaderWord(DWORD d);

    void GetFinalInfo(/* OUT */ BYTE** ppUnwindBlock, /* OUT */ ULONG* pUnwindBlockSize);

    // AppendEpilog: copy the epilog bytes to the next epilog bytes slot
    void AppendEpilog(UnwindEpilogInfo* pEpi);

    // Match the prolog codes to a set of epilog codes
    int Match(UnwindEpilogInfo* pEpi);

    // Copy the prolog codes from another prolog
    void CopyFrom(UnwindPrologCodes* pCopyFrom);

    UnwindPrologCodes()
    {
    }
    ~UnwindPrologCodes()
    {
    }

#ifdef DEBUG
    void Dump(int indent = 0);
#endif // DEBUG

private:
    void EnsureSize(int requiredSize);

    // No copy constructor or operator=
    UnwindPrologCodes(const UnwindPrologCodes& info);
    UnwindPrologCodes& operator=(const UnwindPrologCodes&);

    //
    // Data
    //

    // To store the unwind codes, we first use a local array that should satisfy almost all cases.
    // If there are more unwind codes, we dynamically allocate memory.
    BYTE  upcMemLocal[UPC_LOCAL_COUNT];
    BYTE* upcMem;

    // upcMemSize is the number of bytes in upcMem. This is equal to UPC_LOCAL_COUNT unless
    // we've dynamically allocated memory to store the codes.
    int upcMemSize;

    // upcCodeSlot points to the last unwind code added to the array. The array is filled in from
    // the end, so it starts pointing one beyond the array end.
    int upcCodeSlot;

    // upcHeaderSlot points to the last header byte prepended to the array. Headers bytes are
    // filled in from the beginning, and only after SetFinalSize() is called.
    int upcHeaderSlot;

    // upcEpilogSlot points to the next epilog location to fill
    int upcEpilogSlot;

    // upcUnwindBlockSlot is only set after SetFinalSize() is called. It is the index of the first
    // byte of the final unwind data, namely the first byte of the header.
    int upcUnwindBlockSlot;
};

// UnwindEpilogCodes: represents the unwind codes for a single epilog sequence.
// Epilog unwind codes arrive in the order they will be emitted. Store them as an array,
// adding new ones to the end of the array.

class UnwindEpilogCodes : public UnwindBase, public UnwindCodesBase
{
    // UEC_LOCAL_COUNT is the amount of memory local to this class. For ARM mscorlib.dll, the maximum size is 6,
    // while 89% of epilogs fit in 4. So, set it to 4 to maintain array alignment and hit most cases.
    static const int UEC_LOCAL_COUNT = 4;

public:
    UnwindEpilogCodes(Compiler* comp)
        : UnwindBase(comp)
        , uecMem(uecMemLocal)
        , firstByteOfLastCode(0)
        , uecMemSize(UEC_LOCAL_COUNT)
        , uecCodeSlot(-1)
        , uecFinalized(false)
    {
    }

    //
    // Implementation of UnwindCodesBase
    //

    virtual void AddCode(BYTE b1)
    {
        AppendByte(b1);

        firstByteOfLastCode = b1;
    }

    virtual void AddCode(BYTE b1, BYTE b2)
    {
        AppendByte(b1);
        AppendByte(b2);

        firstByteOfLastCode = b1;
    }

    virtual void AddCode(BYTE b1, BYTE b2, BYTE b3)
    {
        AppendByte(b1);
        AppendByte(b2);
        AppendByte(b3);

        firstByteOfLastCode = b1;
    }

    virtual void AddCode(BYTE b1, BYTE b2, BYTE b3, BYTE b4)
    {
        AppendByte(b1);
        AppendByte(b2);
        AppendByte(b3);
        AppendByte(b4);

        firstByteOfLastCode = b1;
    }

    // Return a pointer to the first unwind code byte
    virtual BYTE* GetCodes()
    {
        assert(uecFinalized);

        // Codes start at the beginning
        return uecMem;
    }

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

    BYTE GetByte(int index)
    {
        assert(0 <= index && index <= uecCodeSlot);
        return uecMem[index];
    }

    // Add a single byte on the unwind code array
    void AppendByte(BYTE b)
    {
        if (uecCodeSlot == uecMemSize - 1)
        {
            // We've run out of space! Reallocate, and copy everything to a new array.
            EnsureSize(uecMemSize + 1);
        }

        ++uecCodeSlot;
        noway_assert(0 <= uecCodeSlot && uecCodeSlot < uecMemSize);

        uecMem[uecCodeSlot] = b;
    }

    // Return the size of the unwind codes, in bytes. The size is the exact size, not an aligned size.
    int Size()
    {
        if (uecFinalized)
        {
            // Add one because uecCodeSlot is 0-based
            return uecCodeSlot + 1;
        }
        else
        {
            // Add one because uecCodeSlot is 0-based, and one for an "end" code that isn't stored (yet).
            return uecCodeSlot + 2;
        }
    }

    void FinalizeCodes()
    {
        assert(!uecFinalized);
        noway_assert(0 <= uecCodeSlot && uecCodeSlot < uecMemSize); // There better be at least one code!

        if (!IsEndCode(firstByteOfLastCode)) // If the last code is an end code, we don't need to append one.
        {
            AppendByte(UWC_END);           // Add a default "end" code to the end of the array of unwind codes
            firstByteOfLastCode = UWC_END; // Update firstByteOfLastCode in case we use it later
        }

        uecFinalized = true; // With the "end" code in place, now we're done

#ifdef DEBUG
        unsigned codeSize = GetCodeSizeFromUnwindCodes(false);
        assert(codeSize <= MAX_EPILOG_SIZE_BYTES);
#endif // DEBUG
    }

    UnwindEpilogCodes()
    {
    }
    ~UnwindEpilogCodes()
    {
    }

#ifdef DEBUG
    void Dump(int indent = 0);
#endif // DEBUG

private:
    void EnsureSize(int requiredSize);

    // No destructor, copy constructor or operator=
    UnwindEpilogCodes(const UnwindEpilogCodes& info);
    UnwindEpilogCodes& operator=(const UnwindEpilogCodes&);

    //
    // Data
    //

    // To store the unwind codes, we first use a local array that should satisfy almost all cases.
    // If there are more unwind codes, we dynamically allocate memory.
    BYTE  uecMemLocal[UEC_LOCAL_COUNT];
    BYTE* uecMem;
    BYTE  firstByteOfLastCode;

    // uecMemSize is the number of bytes/slots in uecMem. This is equal to UEC_LOCAL_COUNT unless
    // we've dynamically allocated memory to store the codes.
    int uecMemSize;

    // uecCodeSlot points to the last unwind code added to the array. The array is filled in from
    // the beginning, so it starts at -1.
    int uecCodeSlot;

    // Is the unwind information finalized? Finalized info has an end code appended.
    bool uecFinalized;
};

// UnwindEpilogInfo: represents the unwind information for a single epilog sequence. Epilogs for a
// single function/funclet are in a linked list.

class UnwindEpilogInfo : public UnwindBase
{
    friend class UnwindFragmentInfo;

    static const unsigned EPI_ILLEGAL_OFFSET = 0xFFFFFFFF;

public:
    UnwindEpilogInfo(Compiler* comp)
        : UnwindBase(comp)
        , epiNext(NULL)
        , epiEmitLocation(NULL)
        , epiCodes(comp)
        , epiStartOffset(EPI_ILLEGAL_OFFSET)
        , epiMatches(false)
        , epiStartIndex(-1)
    {
    }

    void CaptureEmitLocation();

    void FinalizeOffset();

    void FinalizeCodes()
    {
        epiCodes.FinalizeCodes();
    }

    UNATIVE_OFFSET GetStartOffset()
    {
        assert(epiStartOffset != EPI_ILLEGAL_OFFSET);
        return epiStartOffset;
    }

    int GetStartIndex()
    {
        assert(epiStartIndex != -1);
        return epiStartIndex; // The final "Epilog Start Index" of this epilog's unwind codes
    }

    void SetStartIndex(int index)
    {
        assert(epiStartIndex == -1);
        epiStartIndex = (int)index;
    }

    void SetMatches()
    {
        epiMatches = true;
    }

    bool Matches()
    {
        return epiMatches;
    }

    // Size of epilog unwind codes in bytes
    int Size()
    {
        return epiCodes.Size();
    }

    // Return a pointer to the first unwind code byte
    BYTE* GetCodes()
    {
        return epiCodes.GetCodes();
    }

    // Match the codes to a set of epilog codes
    int Match(UnwindEpilogInfo* pEpi);

    UnwindEpilogInfo()
    {
    }
    ~UnwindEpilogInfo()
    {
    }

#ifdef DEBUG
    void Dump(int indent = 0);
#endif // DEBUG

private:
    // No copy constructor or operator=
    UnwindEpilogInfo(const UnwindEpilogInfo& info);
    UnwindEpilogInfo& operator=(const UnwindEpilogInfo&);

    //
    // Data
    //

    UnwindEpilogInfo* epiNext;
    emitLocation*     epiEmitLocation; // The emitter location of the beginning of the epilog
    UnwindEpilogCodes epiCodes;
    UNATIVE_OFFSET    epiStartOffset; // Actual offset of the epilog, in bytes, from the start of the function. Set in
                                      // FinalizeOffset().
    bool epiMatches;   // Do the epilog unwind codes match some other set of codes? If so, we don't copy these to the
                       // final set; we just point to another set.
    int epiStartIndex; // The final "Epilog Start Index" of this epilog's unwind codes
};

// UnwindFragmentInfo: represents all the unwind information for a single fragment of a function or funclet.
// A fragment is a section with a code size less than the maximum unwind code size: either 512K bytes, or
// that specified by COMPlus_JitSplitFunctionSize. In most cases, there will be exactly one fragment.

class UnwindFragmentInfo : public UnwindBase
{
    friend class UnwindInfo;

    static const unsigned UFI_ILLEGAL_OFFSET = 0xFFFFFFFF;

public:
    UnwindFragmentInfo(Compiler* comp, emitLocation* emitLoc, bool hasPhantomProlog);

    void FinalizeOffset();

    UNATIVE_OFFSET GetStartOffset()
    {
        assert(ufiStartOffset != UFI_ILLEGAL_OFFSET);
        return ufiStartOffset;
    }

    // Add an unwind code. It could be for a prolog, or for the current epilog.
    // A single unwind code can be from 1 to 4 bytes.

    void AddCode(BYTE b1)
    {
        assert(ufiInitialized == UFI_INITIALIZED_PATTERN);
        ufiCurCodes->AddCode(b1);
    }

    void AddCode(BYTE b1, BYTE b2)
    {
        assert(ufiInitialized == UFI_INITIALIZED_PATTERN);
        ufiCurCodes->AddCode(b1, b2);
    }

    void AddCode(BYTE b1, BYTE b2, BYTE b3)
    {
        assert(ufiInitialized == UFI_INITIALIZED_PATTERN);
        ufiCurCodes->AddCode(b1, b2, b3);
    }

    void AddCode(BYTE b1, BYTE b2, BYTE b3, BYTE b4)
    {
        assert(ufiInitialized == UFI_INITIALIZED_PATTERN);
        ufiCurCodes->AddCode(b1, b2, b3, b4);
    }

    unsigned EpilogCount()
    {
        unsigned count = 0;
        for (UnwindEpilogInfo* pEpi = ufiEpilogList; pEpi != NULL; pEpi = pEpi->epiNext)
        {
            ++count;
        }
        return count;
    }

    void AddEpilog();

    void MergeCodes();

    void CopyPrologCodes(UnwindFragmentInfo* pCopyFrom);

    void SplitEpilogCodes(emitLocation* emitLoc, UnwindFragmentInfo* pSplitFrom);

    bool IsAtFragmentEnd(UnwindEpilogInfo* pEpi);

    // Return the full, final size of unwind block. This will be used to allocate memory for
    // the unwind block. This is called before the code offsets are finalized.
    // Size is in bytes.
    ULONG Size()
    {
        assert(ufiSize != 0);
        return ufiSize;
    }

    void Finalize(UNATIVE_OFFSET functionLength);

    // GetFinalInfo: return a pointer to the final unwind info to hand to the VM, and the size of this info in bytes
    void GetFinalInfo(/* OUT */ BYTE** ppUnwindBlock, /* OUT */ ULONG* pUnwindBlockSize)
    {
        ufiPrologCodes.GetFinalInfo(ppUnwindBlock, pUnwindBlockSize);
    }

    void Reserve(BOOL isFunclet, bool isHotCode);

    void Allocate(
        CorJitFuncKind funKind, void* pHotCode, void* pColdCode, UNATIVE_OFFSET funcEndOffset, bool isHotCode);

    UnwindFragmentInfo()
    {
    }
    ~UnwindFragmentInfo()
    {
    }

#ifdef DEBUG
    void Dump(int indent = 0);
#endif // DEBUG

private:
    // No copy constructor or operator=
    UnwindFragmentInfo(const UnwindFragmentInfo& info);
    UnwindFragmentInfo& operator=(const UnwindFragmentInfo&);

    //
    // Data
    //

    UnwindFragmentInfo* ufiNext;             // The next fragment
    emitLocation*       ufiEmitLoc;          // Emitter location for start of fragment
    bool                ufiHasPhantomProlog; // Are the prolog codes for a phantom prolog, or a real prolog?
                                             //   (For a phantom prolog, this code fragment represents a fragment in
                                             //   the sense of the unwind info spec; something without a real prolog.)
    UnwindPrologCodes ufiPrologCodes;        // The unwind codes for the prolog
    UnwindEpilogInfo  ufiEpilogFirst;        // In-line the first epilog to avoid separate memory allocation, since
                                             //   almost all functions will have at least one epilog. It is pointed
                                             //   to by ufiEpilogList when the first epilog is added.
    UnwindEpilogInfo* ufiEpilogList;         // The head of the epilog list
    UnwindEpilogInfo* ufiEpilogLast;         // The last entry in the epilog list (the last epilog added)
    UnwindCodesBase*  ufiCurCodes;           // Pointer to current unwind codes, either prolog or epilog

    // Some data computed when merging the unwind codes, and used when finalizing the
    // unwind block for emission.
    unsigned       ufiSize; // The size of the unwind data for this fragment, in bytes
    bool           ufiSetEBit;
    bool           ufiNeedExtendedCodeWordsEpilogCount;
    unsigned       ufiCodeWords;
    unsigned       ufiEpilogScopes;
    UNATIVE_OFFSET ufiStartOffset;

#ifdef DEBUG

    unsigned ufiNum;

    // Are we processing the prolog? The prolog must come first, followed by a (possibly empty)
    // set of epilogs, for this function/funclet.
    bool ufiInProlog;

    static const unsigned UFI_INITIALIZED_PATTERN = 0x0FACADE0; // Something unlikely to be the fill pattern for
                                                                // uninitialized memory
    unsigned ufiInitialized;

#endif // DEBUG
};

// UnwindInfo: represents all the unwind information for a single function or funclet

class UnwindInfo : public UnwindBase
{
public:
    void InitUnwindInfo(Compiler* comp, emitLocation* startLoc, emitLocation* endLoc);

    void HotColdSplitCodes(UnwindInfo* puwi);

    // The following act on all the fragments that make up the unwind info for this function or funclet.

    void Split();

    static void EmitSplitCallback(void* context, emitLocation* emitLoc);

    void Reserve(BOOL isFunclet, bool isHotCode);

    void Allocate(CorJitFuncKind funKind, void* pHotCode, void* pColdCode, bool isHotCode);

    // The following act on the current fragment (the one pointed to by 'uwiFragmentLast').

    // Add an unwind code. It could be for a prolog, or for the current epilog.
    // A single unwind code can be from 1 to 4 bytes.

    void AddCode(BYTE b1)
    {
        assert(uwiInitialized == UWI_INITIALIZED_PATTERN);
        assert(uwiFragmentLast != NULL);
        INDEBUG(CheckOpsize(b1));

        uwiFragmentLast->AddCode(b1);
        CaptureLocation();
    }

    void AddCode(BYTE b1, BYTE b2)
    {
        assert(uwiInitialized == UWI_INITIALIZED_PATTERN);
        assert(uwiFragmentLast != NULL);
        INDEBUG(CheckOpsize(b1));

        uwiFragmentLast->AddCode(b1, b2);
        CaptureLocation();
    }

    void AddCode(BYTE b1, BYTE b2, BYTE b3)
    {
        assert(uwiInitialized == UWI_INITIALIZED_PATTERN);
        assert(uwiFragmentLast != NULL);
        INDEBUG(CheckOpsize(b1));

        uwiFragmentLast->AddCode(b1, b2, b3);
        CaptureLocation();
    }

    void AddCode(BYTE b1, BYTE b2, BYTE b3, BYTE b4)
    {
        assert(uwiInitialized == UWI_INITIALIZED_PATTERN);
        assert(uwiFragmentLast != NULL);
        INDEBUG(CheckOpsize(b1));

        uwiFragmentLast->AddCode(b1, b2, b3, b4);
        CaptureLocation();
    }

    void AddEpilog();

    emitLocation* GetCurrentEmitterLocation()
    {
        return uwiCurLoc;
    }

#if defined(_TARGET_ARM_)
    unsigned GetInstructionSize();
#endif // defined(_TARGET_ARM_)

    void CaptureLocation();

    UnwindInfo()
    {
    }
    ~UnwindInfo()
    {
    }

#ifdef DEBUG

#if defined(_TARGET_ARM_)
    // Given the first byte of the unwind code, check that its opsize matches
    // the last instruction added in the emitter.
    void CheckOpsize(BYTE b1);
#elif defined(_TARGET_ARM64_)
    void CheckOpsize(BYTE b1)
    {
    } // nothing to do; all instructions are 4 bytes
#endif // defined(_TARGET_ARM64_)

    void Dump(bool isHotCode, int indent = 0);

    bool uwiAddingNOP;

#endif // DEBUG

private:
    void AddFragment(emitLocation* emitLoc);

    // No copy constructor or operator=
    UnwindInfo(const UnwindInfo& info);
    UnwindInfo& operator=(const UnwindInfo&);

    //
    // Data
    //

    UnwindFragmentInfo uwiFragmentFirst; // The first fragment is directly here, so it doesn't need to be separately
                                         // allocated.
    UnwindFragmentInfo* uwiFragmentLast; // The last entry in the fragment list (the last fragment added)
    emitLocation*       uwiEndLoc;       // End emitter location of this function/funclet (NULL == end of all code)
    emitLocation*       uwiCurLoc; // The current emitter location (updated after an unwind code is added), used for NOP
                                   // padding, and asserts.

#ifdef DEBUG

    static const unsigned UWI_INITIALIZED_PATTERN = 0x0FACADE1; // Something unlikely to be the fill pattern for
                                                                // uninitialized memory
    unsigned uwiInitialized;

#endif // DEBUG
};

#ifdef DEBUG

// Forward declaration
void DumpUnwindInfo(Compiler*         comp,
                    bool              isHotCode,
                    UNATIVE_OFFSET    startOffset,
                    UNATIVE_OFFSET    endOffset,
                    const BYTE* const pHeader,
                    ULONG             unwindBlockSize);

#endif // DEBUG

#endif // _TARGET_ARMARCH_