aboutsummaryrefslogtreecommitdiff
path: root/slang_rs_reflection_state.h
blob: d937be8476a9e35cd631f7f8c541f13a542525ee (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
/*
 * Copyright 2017, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _FRAMEWORKS_COMPILE_SLANG_REFLECTION_STATE_H_  // NOLINT
#define _FRAMEWORKS_COMPILE_SLANG_REFLECTION_STATE_H_

#include <string>
#include <utility>

#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "clang/AST/APValue.h"

#include "slang_assert.h"

namespace slang {

class RSContext;
class RSExportForEach;
class RSExportFunc;
class RSExportRecordType;
class RSExportReduce;
class RSExportType;
class RSExportVar;

// ---------------------
// class ReflectionState
// ---------------------
//
// This class is used to collect data from 32-bit compilation for use
// during the reflected code generation that occurs during 64-bit
// compilation.  The data is used for two purposes:
//
// 1) Accommodating rs object handle size differences when laying out
//    data (in particular, variables and records).
// 2) Emitting errors when differences between 32-bit and 64-bit
//    compilation cannot be tolerated in the reflected code (for
//    example, size_t has different sizes, and so cannot be part
//    of any exportable).
//
// The model for using this class is as follows:
// a) Instantiate a class instance.  The instance is in the S_Initial
//    state.
// b) Call openJava32() to move the instance to the S_OpenJava32
//    ("Collecting") state.
// c) Run the reflection pass on all files in 32-bit mode.  Do not
//    actually generate reflected code; but call various methods on
//    the instance (begin*(), declare*(), end*(), etc.) to collect
//    information.
// d) Call closeJava32() to move the instance to the S_ClosedJava32
//    state.
// e) Call openJava64() to move the instance to the S_OpenJava64
//    ("Using") state.
// f) Run the reflection pass on all files in 64-bit mode.  Call the
//    same methods as in step (c), as well as some further methods to
//    query the information collected in step (c) in order to handle
//    layout differences.  All error reporting for 32-bit versus
//    64-bit differences is handled in the methods themselves.
// g) Call closeJava64 to move the instance to the S_ClosedJava64
//    state.
// h) Destroy the instance.
//
// There are two exceptions to this model:
//
// 1) If not doing both 32-bit and 64-bit compilation, then skip steps
//    (b), (d), (e), and (g).  (This is what happens if reflecting C++
//    instead of Java, or reflecting Java but using the -m32 or -m64
//    option.)  In this case, the methods called in steps (c) and (f)
//    are no-ops: They do not collect information, they do not report
//    errors, and they return "no information available" when step (f)
//    asks for 32-bit layout related information.
// 2) The class instance can be moved to the S_Bad state by class
//    ReflectionState::Tentative (see that class for more information)
//    when reflection itself aborts due to some error.  The only legal
//    thing to do with an instance in this state is invoke its
//    destructor.
//
// All exported entities except for Records have slot numbers assigned
// in reflection order.  These slot numbers must match up between
// 32-bit and 64-bit compilation.  Therefore, we (generally) require
// that entities be presented to ReflectionState (via begin*() or
// declare*()) in the same order during the Collecting and Using
// phases.  This presentation order is generally the same as lexical
// order in the user code, which makes it simple to emit meaningful
// diagnostics when the order is inconsistent (for example, 32-bit and
// 64-bit compilation disagree on the name of the kernel in a
// particular slot).  ReflectionState generally builds up an array of
// each sort of entity, in the presentation order.  There are two
// exceptions:
//
// a) Records, as mentioned above.  Exported Records have no slot
//    number, and therefore reflection order doesn't matter.  In
//    practice, Records aren't necessarily reflected in consistent
//    order, because they are determined to be exported as a
//    consequence of determining that other entities are to be
//    exported; and variations between 32-bit and 64-bit compilation
//    can therefore result in inconsistent Record reflection order.
//    Therefore, ReflectionState builds up a map of Records.
// b) ForEach kernels.  ForEach kernels are not necessarily reflected
//    in lexical order (there is some sorting to segregate root
//    kernel, old-style kernels, and new-style kernels).  In order to
//    give meaningful diagnostics for slot order mismatches, it's
//    enough to solve the simpler problem of giving meaningful
//    diagnostics for lexical order mismatches (although this is
//    stricter than necessary because of the sorting that occurs
//    before slot assignment).  Therefore, ReflectionState builds up
//    an array of ForEaches in lexical order rather than in
//    presentation (slot) order, and accesses the array randomly
//    rather than sequentially.
//
class ReflectionState {
 private:
  // Set this to true to turn everything into a no-op, just as if none
  // of the open*() or close*() methods were ever called.
  static const bool kDisabled = false;

 public:
  ReflectionState() :
      mRSC(nullptr),
      mState(S_Initial),
      mForEachOpen(-1),
      mOutputClassOpen(false),
      mRecordsState(RS_Initial),
      mStringSet(nullptr) { }
  ~ReflectionState();

  ReflectionState(const ReflectionState &) = delete;
  void operator=(const ReflectionState &) = delete;

  // For use in the debugger.
  void dump();

  // A possibly-present value describing a property for a 32-bit target.
  // When .first is false, the value is absent, and .second is unspecified.
  typedef std::pair<bool, size_t> Val32;
  static Val32 NoVal32() { return Val32(false, ~size_t(0)); }

  void openJava32(size_t NumFiles);
  void closeJava32();
  void openJava64();
  void closeJava64();

  bool isCollecting() const { return mState==S_OpenJava32; }

  // ----------------------------------------------------------------------

  // Use these methods during the "Collecting" phase to track
  // information about a class being generated -- a script class or a
  // type class.  We call such a class "Divergent" if it needs to have
  // at least one runtime check to distinguish between 32-bit and
  // 64-bit targets.
  //
  //   Indicate that we are beginning to generate the class.
  //
  void beginOutputClass() {
    slangAssert(!mOutputClassOpen && !isClosed());
    mOutputClassOpen = true;
    mOutputClassDivergent = false;
  }
  //
  //   Record the fact that we've learned the class is divergent.
  //
  void setOutputClassDivergent() {
    slangAssert(mOutputClassOpen);
    mOutputClassDivergent = true;
  }
  //
  //   Indicate that we've finished generating the class.  Returns
  //   true IFF we've learned the class is divergent.
  //
  bool endOutputClass() {
    slangAssert(mOutputClassOpen);
    mOutputClassOpen = false;
    return mOutputClassDivergent;
  }

  // ----------------------------------------------------------------------

  // --------------------------------
  // class ReflectionState::Tentative
  // --------------------------------
  //
  // This class aids in error handling.  The model is as follows:
  // a) Instantiate the class with a pointer to a ReflectionState
  //    instance.
  // b) Before destroying the class instance, if there have been no
  //    errors, call the ok() method on the instance.
  // c) When the instance is destroyed, if ok() has not been called on
  //    it, this class will put the ReflectionState into the S_Bad
  //    state.
  //
  // The idea is to "poison" the ReflectionState if we quit reflection
  // early because of some error -- we don't want to get in a
  // situation where we only have partial information from the
  // Collecting phase (because of quitting early) but try to use it
  // during the Using phase.
  //
  friend class Tentative;
  class Tentative {
   public:
    Tentative(ReflectionState *state) : mState(state) { }
    ~Tentative() { if (mState) mState->mState = ReflectionState::S_Bad; }

    void ok() { mState = nullptr; }

    Tentative(const Tentative &) = delete;
    void operator=(const Tentative &) = delete;

   private:
    ReflectionState *mState;
  };

  // ----------------------------------------------------------------------

  // Model for ForEach kernels (per File):
  //
  // a) beginForEaches(number_of_non_dummy_root_kernels_in_file)
  // b) mixture of declareForEachDummyRoot() calls and
  //    beginForEach()..endForEach() calls
  // c) endForEaches()
  //
  // For a given ForEach kernel:
  //
  // b1) beginForEach()
  // b2) call any number of addForEachIn() (one per input)
  // b3) call any number of addForEachParam() (one per param)
  // b4) call addForEachSignatureMetadata() (if it's reflected)
  // b5) call endForEach()
  //
  // b2, b3, b4 can occur in any order

  void beginForEaches(size_t Count);

  void declareForEachDummyRoot(const RSExportForEach *) { /* we don't care */ };

  void beginForEach(const RSExportForEach *EF);

  void addForEachIn(const RSExportForEach *EF, const RSExportType *Type);

  void addForEachParam(const RSExportForEach *EF, const RSExportType *Type);

  void addForEachSignatureMetadata(const RSExportForEach *EF, unsigned Metadata);

  void endForEach();

  void endForEaches();

  // ----------------------------------------------------------------------

  // Model for Invokable functions (per File):
  //
  // a) beginInvokables(number_of_invokables_in_file)
  // b) declareInvokable() for each Invokable (order must be
  //    consistent between 32-bit and 64-bit compile)
  // c) endInvokables()

  void beginInvokables(size_t Count) {
    mInvokablesOrderFatal = false;
    begin(&File::mInvokables, Count);
  }

  void declareInvokable(const RSExportFunc *EF);

  void endInvokables();

  // ----------------------------------------------------------------------

  // Model for reduction kernels (per File):
  //
  // a) beginReduces(number_of_reduction_kernels_in_file)
  // b) declareReduce() for each reduction kernel (order must be
  //    consistent between 32-bit and 64-bit compile)
  // c) endReduces()

  void beginReduces(size_t Count) {
    mReducesOrderFatal = false;
    begin(&File::mReduces, Count);
  }

  void declareReduce(const RSExportReduce *ER, bool IsExportable);

  void endReduces();

  // ----------------------------------------------------------------------

  // Model for records (per File):
  //
  // a) beginRecords()
  // b) declareRecord() for each Record (order doesn't matter)
  // c) endRecords()
  //
  // And at any time during the Using phase, can call getRecord32() to
  // get information from the 32-bit compile (Collecting phase).

  void beginRecords();

  // An "Ordinary" record is anything other than an
  // internally-synthesized helper record.  We do not emit diagnostics
  // for mismatched helper records -- we assume that the constructs
  // from which those helper records were derived are also mismatched,
  // and that we'll get diagnostics for those constructs.
  void declareRecord(const RSExportRecordType *ERT, bool Ordinary = true);

  void endRecords();

  class Record32;

  // During the Using phase, obtain information about a Record from
  // the Collecting phase.  ERT should be from the Using phase, not
  // the Collecting phase.  The value returned from this function is
  // valid for the lifetime of the ReflectionState instance.
  Record32 getRecord32(const RSExportRecordType *ERT);

  // ----------------------------------------------------------------------

  // Model for Variables (per file):
  //
  // a) beginVariables(number_of_exported_variables_in_file)
  // b) declareVariable() for each Variable (order must be consistent
  //    between 32-bit and 64-bit); in the Using phase, returns some
  //    information about the Variable from 32-bit compilation
  // c) endVariables()

  void beginVariables(size_t Count) {
    mVariablesOrderFatal = false;
    begin(&File::mVariables, Count);
  }

  // If isUsing(), returns variable's 32-bit AllocSize; otherwise, returns NoVal32().
  Val32 declareVariable(const RSExportVar *EV);

  void endVariables();

  // ----------------------------------------------------------------------

  // ReflectionState has a notion of "current file".  After an
  // openJava*() or closeJava*() call, there is no current file.
  // Calling the nextFile() method when in the Collecting or Using
  // state "advances" to the next file in the list of files being
  // compiled, whose properties are specified by the arguments to
  // nextFile().  All of the various begin*(), declare*(), end*()
  // etc. calls implicitly refer to entities in the current file.
  //
  // RSC must remain valid until the next call to nextFile() or the
  // next S_* state change.
  void nextFile(const RSContext *RSC, const std::string &PackageName, const std::string &RSSourceFileName);

  // ----------------------------------------------------------------------

 private:
  enum State {
    S_Initial,          // No captured information
    S_OpenJava32,       // Capturing information for 32-bit Java
    S_ClosedJava32,     // Captured  information for 32-bit Java
    S_OpenJava64,       // Capturing information for 64-bit Java
    S_ClosedJava64,     // Captured  information for 64-bit Java
    S_Bad,              // Abnormal termination
  };

  // context associated with compilation of the current file
  const RSContext *mRSC;

  State mState;

  /*== ForEach ==================================================================*/

  // The data in this section is transient during ForEach processing
  // for each File.

  int mForEachOpen;    // if nonnegative, then ordinal of beginForEach() without matching endForEach()
  bool mForEachFatal;  // fatal mismatch in comparing ForEach; do no further comparisons for it

  // Tracks mismatches discovered during the Use phase.
  // There are two possibilities:
  // - if (ordinal + 1) is greater than the number of ForEaches from the Collecting phase,
  //   then this is an "extra" ForEach discovered during the Use phase
  // - otherwise the Collecting phase and the Use phase disagree on the name of the
  //   ForEach at this ordinal position (the Collecting phase's kernel name is
  //   available in mFiles.Current().mForEaches[ordinal].mName)
  llvm::SmallVector<const RSExportForEach *, 0> mForEachesBad;

  // During the Use phase, keep track of how many ForEach ordinals we
  // have seen that correspond to ordinals seen during the Collect
  // phase.  This helps determine whether we have to issue errors at
  // endForEaches().
  size_t mNumForEachesMatchedByOrdinal;

  /*== Invokable ================================================================*/

  // 32-bit and 64-bit compiles need to see invokables in the same
  // order, because of slot number assignment.  Once we see the first
  // name mismatch in the sequence of invokables for a given File, it
  // doesn't make sense to issue further diagnostics regarding
  // invokables for that File.
  bool mInvokablesOrderFatal;

  /*== OutputClass ==============================================================*/

  // This data tracks information about a class being generated -- a
  // script class or a type class.  We call such a class "Divergent"
  // if it needs to have at least one runtime check to distinguish
  // between 32-bit and 64-bit targets.

  bool mOutputClassOpen;  // beginOutputClass() without matching endOutputClass()
  bool mOutputClassDivergent;  // has class been marked divergent?

  /*== Record ===================================================================*/

  // This field enforces necessary discipline on the use of
  // beginRecords()/declareRecord()/endRecord().
  enum {
    RS_Initial,  // no beginRecords() yet for current File
    RS_Open,     // beginRecords() but no endRecords() for current File
    RS_Closed    // endRecords() for current File
  } mRecordsState;

  // During the Use phase, keep track of how many records we have seen
  // that have same-named counterparts seen during the Collect phase.
  // This helps determine whether we have to issue errors at
  // endRecords().
  size_t mNumRecordsMatchedByName;

  /*== Reduce ===================================================================*/

  // 32-bit and 64-bit compiles need to see reduction kernels in the
  // same order, because of slot number assignment.  Once we see the
  // first name mismatch in the sequence of reduction kernels for a
  // given File, it doesn't make sense to issue further diagnostics
  // regarding reduction kernels for that File.
  bool mReducesOrderFatal;

  /*== Variable =================================================================*/

  // 32-bit and 64-bit compiles need to see variables in the same
  // order, because of slot number assignment.  Once we see the first
  // name mismatch in the sequence of variables for a given File, it
  // doesn't make sense to issue further diagnostics regarding
  // variables for that File.
  bool mVariablesOrderFatal;

  /*=============================================================================*/

  bool isActive() const { return isCollecting() || isUsing(); }
  bool isClosed() const { return mState==S_ClosedJava32 || mState==S_ClosedJava64; }
  bool isUsing() const { return mState==S_OpenJava64; }

  // For anything with a type (such as a Variable or a Record field),
  // the type is represented via its name.  To save space, we don't
  // create multiple instances of the same name -- we have a canonical
  // instance in mStringSet, and use a StringRef to refer to it.  The
  // method canon() returns a StringRef to the canonical
  // instance, creating the instance if necessary.
  llvm::StringRef canon(const std::string &String);
  llvm::StringSet<> *mStringSet;

  // Synthesize a name for the specified type.  There should be a
  // one-to-one correspondence between the name and a C type (after
  // typedefs and integer expressions have been "flattened", and
  // considering a struct type to be identified solely by its name).
  static std::string getUniqueTypeName(const RSExportType *T);

  // ------------------------------
  // template class ArrayWithCursor
  // ------------------------------
  //
  // This class represents a fixed-length dynamically-allocated array
  // (length is specified by a method call after instantiation) along
  // with a cursor that traverses the array.  The behavior of the
  // class is very specific to the needs of ReflectionState.
  //
  // The model for using this class is as follows:
  // a) Instantiate a class instance.  The instance is in the
  //    S_Initial state.
  // b) Call BeginCollecting() with an array capacity.  This allocates
  //    the array members and moves the instance to the S_Collecting
  //    state.  The array size (contrast with capacity) is zero, and
  //    the cursor has not been placed.
  // c) Call CollectNext() a number of times equal to the capacity.
  //    Each time CollectNext() is called, it extends the array size
  //    by 1, and advances the cursor to the "new" member.  The idea
  //    is to set the value of the "new" member at this time.
  // d) Call BeginUsing().  This moves the instance to the S_Using
  //    state and "unplaces" the cursor.
  // e) Call UseNext() a number of times equal to the capacity.  Each
  //    time UseNext() is called, it advances the cursor to the next
  //    member (first member, the first time it is called).
  //    The cursor is stepping through the members that were "created"
  //    by CollectNext() during the S_Collecting state; the idea is to
  //    look at their values.
  // f) Destroy the instance.
  //
  template <typename Member> class ArrayWithCursor {
   public:
    ArrayWithCursor() : mState(S_Initial), mMembers(nullptr), mCapacity(0), mSize(0), mCursor(~size_t(0)) { }

    ~ArrayWithCursor() { delete [] mMembers; }

    ArrayWithCursor(const ArrayWithCursor &) = delete;
    void operator=(const ArrayWithCursor &) = delete;

    void BeginCollecting(size_t Size) {
      slangAssert(mState == S_Initial);
      mState = S_Collecting;
      mMembers = new Member[Size];
      mCapacity = Size;
    }
    // Increments the array size, advances the cursor to the new
    // member, and returns a reference to that member.
    Member &CollectNext() {
      slangAssert((mState == S_Collecting) && (mCursor + 1 == mSize) && (mSize < mCapacity));
      ++mSize;
      return mMembers[++mCursor];
    }

    void BeginUsing() {
      slangAssert((mState == S_Collecting) && (mCursor + 1 == mSize) && (mSize == mCapacity));
      mState = S_Using;
      mCursor = ~size_t(0);
    }
    // Advances the cursor to the next member, and returns a reference
    // to that member.
    Member &UseNext() {
      slangAssert((mState == S_Using) && (mCursor + 1 < mSize));
      return mMembers[++mCursor];
    }

    // Is the cursor on the last array member?
    bool isFinished() const {
      return mCursor + 1 == mSize;
    }

    size_t Size() const { return mSize; }

    // Return a reference to the member under the cursor.
    Member &Current() {
      slangAssert(mCursor < mSize);
      return mMembers[mCursor];
    }
    const Member &Current() const {
      slangAssert(mCursor < mSize);
      return mMembers[mCursor];
    }
    // Return the cursor position (zero-based).  Cursor must have been
    // placed (i.e., if we're Collecting, we must have called
    // CollectNext() at least once; and if we're Using, we must have
    // called UseNext() at least once).
    size_t CurrentIdx() const {
      slangAssert(mCursor < mSize);
      return mCursor;
    }

    // Return a reference to the specified member.  Must be within the
    // array size (not merely within its capacity).
    Member &operator[](size_t idx) {
      slangAssert(idx < mSize);
      return mMembers[idx];
    }
    const Member &operator[](size_t idx) const {
      slangAssert(idx < mSize);
      return mMembers[idx];
    }

   private:
    enum State { S_Initial, S_Collecting, S_Using };
    State mState;

    Member *mMembers;
    size_t mCapacity;
    size_t mSize;
    size_t mCursor;
  };


  struct File {
    File() : mForEaches(nullptr) { }
    ~File() { delete [] mForEaches; }

    File(const File &) = delete;
    void operator=(const File &) = delete;

    std::string mPackageName;
    std::string mRSSourceFileName;

    struct ForEach {
      ForEach() : mState(S_Initial) { }
      ForEach(const ForEach &) = delete;
      void operator=(const ForEach &) = delete;

      enum {
        S_Initial,    // ForEach has been instantiated
        S_Collected,  // beginForEach() has been called while Collecting
        S_UseMatched  // beginForEach() has been called while Using,
                      //   and found this ForEach
      } mState;

      std::string mName;

      // Types.  mIns[] and mOut can be null in case we have an
      // old-style kernel with a void* input or output.
      ArrayWithCursor<llvm::StringRef> mIns;
      ArrayWithCursor<llvm::StringRef> mParams;
      llvm::StringRef mOut;
      bool mHasOut;  // to distinguish between no output and void* output.

      unsigned mSignatureMetadata;
      bool mIsKernel;  // new-style (by-value) rather than old-style
    };
    ForEach *mForEaches;  // indexed by ordinal (lexical order)
    size_t mForEachCount;

    struct Invokable {
      Invokable() : mParams(nullptr) { }
      ~Invokable() { delete [] mParams; }

      Invokable(const Invokable &) = delete;
      void operator=(const Invokable &) = delete;

      std::string mName;
      llvm::StringRef *mParams;  // Types
      size_t mParamCount;
    };
    ArrayWithCursor<Invokable> mInvokables;

    // There are two things we need to do with a Record:
    // - Support structure sizes and layouts that differ between
    //   32-bit and 64-bit compilation.
    // - Do consistency checking between 32-bit and 64-bit compilation.
    //
    // TODO: Move this out of File to avoid duplication?  That is,
    //       instead of tracking Records on a per-File basis, instead
    //       track them globally?
    //
    //       (Because of ODR, we shouldn't have inconsistencies
    //       between Files.)
    //
    struct Record {
      Record() : mFields(nullptr) { }
      ~Record() { delete [] mFields; }

      Record(const Record &) = delete;
      void operator=(const Record &) = delete;

      struct Field {
        std::string mName;
        llvm::StringRef mType;
        size_t mPrePadding;   // this.OffsetInParent - (prev.OffsetInParent + prev.AllocSize)
        size_t mPostPadding;  // this.AllocSize - this.StoreSize
        size_t mOffset;       // this.OffsetInParent
        size_t mStoreSize;    // this.StoreSize
      };
      Field *mFields;
      size_t mFieldCount;
      size_t mPostPadding;    // padding after the end of the padded
                              //   last field
      size_t mAllocSize;
      bool mOrdinary;         // anything other than an
                              //   internally-synthesized helper
                              //   record.  We do not emit diagnostics
                              //   for inconsistent helper records.
      bool mMatchedByName;    // has declareRecord() been called on
                              //   this record during the Using phase?
    };
    llvm::StringMap<Record> mRecords;

    struct Reduce {
      Reduce() : mAccumIns(nullptr) { }
      ~Reduce() { delete [] mAccumIns; }

      Reduce(const Reduce &) = delete;
      void operator=(const Reduce &) = delete;

      std::string mName;

      // only apply to exportable
      llvm::StringRef *mAccumIns;  // Types
      size_t mAccumInCount;
      llvm::StringRef mResult;  // Type

      bool mIsExportable;
    };
    ArrayWithCursor<Reduce> mReduces;

    struct Variable {
      Variable() : mInitializers(nullptr) { }
      ~Variable() { delete [] mInitializers; }

      Variable(const Variable &) = delete;
      void operator=(const Variable &) = delete;

      std::string mName;
      llvm::StringRef mType;
      clang::APValue *mInitializers;
      size_t mInitializerCount;
      size_t mAllocSize;
      bool mIsConst;
    };
    ArrayWithCursor<Variable> mVariables;

  };
  ArrayWithCursor<File> mFiles;

  // Utility template -- common pattern used by many begin*() methods.
  template <typename Member>
  void begin(ArrayWithCursor<Member> File::*Array, size_t Count) {
    slangAssert(!isClosed());
    if (!isActive())
      return;

    auto &file = mFiles.Current();
    if (isCollecting())
      (file.*Array).BeginCollecting(Count);
    if (isUsing())
      (file.*Array).BeginUsing();
  }

 public:

  // This class represents 32-bit layout information built up during
  // the Collecting phase, for use during the Using phase.  It
  // provides an interface between class ReflectionState and client
  // code that actually performs reflection.
  class Record32 {
    friend class ReflectionState;

   public:
    Record32() : mRecord(nullptr) { }

    Val32 getRecordPostPadding() const {
      if (!mRecord)
        return NoVal32();
      return Val32(true, mRecord->mPostPadding);
    }

    Val32 getRecordAllocSize() const {
      if (!mRecord)
        return NoVal32();
      return Val32(true, mRecord->mAllocSize);
    }

    std::pair<Val32, Val32> getFieldPreAndPostPadding(unsigned idx) const {
      if (!mRecord || idx >= mRecord->mFieldCount)
        return std::make_pair(NoVal32(), NoVal32());
      const File::Record::Field &field = mRecord->mFields[idx];
      return std::make_pair(Val32(true, field.mPrePadding), Val32(true, field.mPostPadding));
    }

    std::pair<Val32, Val32> getFieldOffsetAndStoreSize(unsigned idx) const {
      if (!mRecord || idx >= mRecord->mFieldCount)
        return std::make_pair(NoVal32(), NoVal32());
      const File::Record::Field &field = mRecord->mFields[idx];
      return std::make_pair(Val32(true, field.mOffset), Val32(true, field.mStoreSize));
    }

   private:
    Record32(const File::Record *Record) : mRecord(Record) { }
    const File::Record *mRecord;
  };
};

}

#endif  // _FRAMEWORKS_COMPILE_SLANG_REFLECTION_STATE_H_  NOLINT