Coverage Report

Created: 2026-04-16 03:41

/__w/slang/slang/source/slang/slang-ir-legalize-types.cpp
Line
Count
Source (jump to first uncovered line)
1
// slang-ir-legalize-types.cpp
2
3
// This file implements type legalization for the IR.
4
// It uses the core legalization logic in
5
// `legalize-types.{h,cpp}` to decide what to do with
6
// the types, while this file handles the actual
7
// rewriting of the IR to use the new types.
8
//
9
// This pass should only be applied to IR that has been
10
// fully specialized (no more generics/interfaces), so
11
// that the concrete type of everything is known.
12
13
#include "../compiler-core/slang-name.h"
14
#include "../core/slang-performance-profiler.h"
15
#include "slang-ir-clone.h"
16
#include "slang-ir-insert-debug-value-store.h"
17
#include "slang-ir-insts.h"
18
#include "slang-ir-util.h"
19
#include "slang-ir.h"
20
#include "slang-legalize-types.h"
21
#include "slang-mangle.h"
22
#include "slang-rich-diagnostics.h"
23
24
namespace Slang
25
{
26
27
LegalVal LegalVal::tuple(RefPtr<TuplePseudoVal> tupleVal)
28
1.00k
{
29
1.00k
    SLANG_ASSERT(tupleVal->elements.getCount());
30
31
1.00k
    LegalVal result;
32
1.00k
    result.flavor = LegalVal::Flavor::tuple;
33
1.00k
    result.obj = tupleVal;
34
1.00k
    return result;
35
1.00k
}
36
37
LegalVal LegalVal::pair(RefPtr<PairPseudoVal> pairInfo)
38
551
{
39
551
    LegalVal result;
40
551
    result.flavor = LegalVal::Flavor::pair;
41
551
    result.obj = pairInfo;
42
551
    return result;
43
551
}
44
45
LegalVal LegalVal::pair(
46
    LegalVal const& ordinaryVal,
47
    LegalVal const& specialVal,
48
    RefPtr<PairInfo> pairInfo)
49
1.20k
{
50
1.20k
    if (ordinaryVal.flavor == LegalVal::Flavor::none)
51
311
        return specialVal;
52
53
892
    if (specialVal.flavor == LegalVal::Flavor::none)
54
369
        return ordinaryVal;
55
56
57
523
    RefPtr<PairPseudoVal> obj = new PairPseudoVal();
58
523
    obj->ordinaryVal = ordinaryVal;
59
523
    obj->specialVal = specialVal;
60
523
    obj->pairInfo = pairInfo;
61
62
523
    return LegalVal::pair(obj);
63
892
}
64
65
LegalVal LegalVal::implicitDeref(LegalVal const& val)
66
394
{
67
394
    RefPtr<ImplicitDerefVal> implicitDerefVal = new ImplicitDerefVal();
68
394
    implicitDerefVal->val = val;
69
70
394
    LegalVal result;
71
394
    result.flavor = LegalVal::Flavor::implicitDeref;
72
394
    result.obj = implicitDerefVal;
73
394
    return result;
74
394
}
75
76
LegalVal LegalVal::getImplicitDeref() const
77
568
{
78
568
    SLANG_ASSERT(flavor == Flavor::implicitDeref);
79
568
    return as<ImplicitDerefVal>(obj)->val;
80
568
}
81
82
LegalVal LegalVal::wrappedBuffer(LegalVal const& baseVal, LegalElementWrapping const& elementInfo)
83
0
{
84
0
    RefPtr<WrappedBufferPseudoVal> obj = new WrappedBufferPseudoVal();
85
0
    obj->base = baseVal;
86
0
    obj->elementInfo = elementInfo;
87
88
0
    LegalVal result;
89
0
    result.flavor = LegalVal::Flavor::wrappedBuffer;
90
0
    result.obj = obj;
91
0
    return result;
92
0
}
93
94
//
95
96
IRTypeLegalizationContext::IRTypeLegalizationContext(
97
    TargetProgram* target,
98
    IRModule* inModule,
99
    DiagnosticSink* sink)
100
29.5k
{
101
29.5k
    targetProgram = target;
102
103
29.5k
    session = inModule->getSession();
104
29.5k
    module = inModule;
105
106
29.5k
    builderStorage = IRBuilder(inModule);
107
29.5k
    builder = &builderStorage;
108
109
29.5k
    m_sink = sink;
110
29.5k
}
111
112
static void registerLegalizedValue(
113
    IRTypeLegalizationContext* context,
114
    IRInst* irValue,
115
    LegalVal const& legalVal)
116
41.9M
{
117
41.9M
    context->mapValToLegalVal[irValue] = legalVal;
118
41.9M
}
119
120
/// Structure to pass information from the original/old global param to
121
/// composite members during tuple flavored global param legalization.
122
struct IRGlobalParamInfo
123
{
124
    IRFunc* originatingEntryPoint = nullptr;
125
};
126
127
static LegalVal declareVars(
128
    IRTypeLegalizationContext* context,
129
    IROp op,
130
    LegalType type,
131
    IRTypeLayout* typeLayout,
132
    LegalVarChain const& varChain,
133
    UnownedStringSlice nameHint,
134
    IRInst* leafVar,
135
    IRGlobalParamInfo* globalParamInfo,
136
    bool isSpecial);
137
138
/// Unwrap a value with flavor `wrappedBuffer`
139
///
140
/// The original `legalPtrOperand` has a wrapped-buffer type
141
/// which encodes the way that, e.g., a `ConstantBuffer<Foo>`
142
/// where `Foo` includes interface types, got legalized
143
/// into a buffer that stores a `Foo` value plus addition
144
/// fields for the concrete types that got plugged in.
145
///
146
/// The `elementInfo` is the layout information for the
147
/// modified ("wrapped") buffer type, and specifies how
148
/// the logical element type was expanded into actual fields.
149
///
150
/// This function returns a new value that undoes all of
151
/// the wrapping and produces a new `LegalVal` that matches
152
/// the nominal type of the original buffer.
153
///
154
static LegalVal unwrapBufferValue(
155
    IRTypeLegalizationContext* context,
156
    LegalVal legalPtrOperand,
157
    LegalElementWrapping const& elementInfo);
158
159
/// Perform any actions required to materialize `val` into a usable value.
160
///
161
/// Certain case of `LegalVal` (currently just the `wrappedBuffer` case) are
162
/// suitable for use to represent a variable, but cannot be used directly
163
/// in computations, because their structured needs to be "unwrapped."
164
///
165
/// This function unwraps any `val` that needs it, which may involve
166
/// emitting additional IR instructions, and returns the unmodified
167
/// `val` otherwise.
168
///
169
static LegalVal maybeMaterializeWrappedValue(IRTypeLegalizationContext* context, LegalVal val)
170
30.8M
{
171
30.8M
    if (val.flavor != LegalVal::Flavor::wrappedBuffer)
172
30.8M
        return val;
173
174
18.4E
    auto wrappedBufferVal = val.getWrappedBuffer();
175
18.4E
    return unwrapBufferValue(context, wrappedBufferVal->base, wrappedBufferVal->elementInfo);
176
30.8M
}
177
178
// Take a value that is being used as an operand,
179
// and turn it into the equivalent legalized value.
180
static LegalVal legalizeOperand(IRTypeLegalizationContext* context, IRInst* irValue)
181
31.9M
{
182
31.9M
    LegalVal legalVal;
183
184
    // Special handling for type operands
185
31.9M
    if (auto oldType = as<IRType>(irValue))
186
1.09M
    {
187
        // e.g. ParameterBlock<Struct>, the inst. ParameterBlockType holds the operand `StructType`,
188
        // if we don't legalize it here and the same structType is legalized somewhere else, the
189
        // operand of ParameterBlockType might not get updated, and it would result in a type
190
        // mismatch.
191
1.09M
        auto legalType = legalizeType(context, oldType);
192
1.09M
        if (legalType.flavor == LegalType::Flavor::simple)
193
1.08M
            return LegalVal::simple(legalType.getSimple());
194
        // legalType is not simple, fallback to the original value
195
1.09M
    }
196
197
30.8M
    if (context->mapValToLegalVal.tryGetValue(irValue, legalVal))
198
30.8M
    {
199
30.8M
        return maybeMaterializeWrappedValue(context, legalVal);
200
30.8M
    }
201
202
    // For now, assume that anything not covered
203
    // by the type legalization or val mapping is legal as-is.
204
11.8k
    return LegalVal::simple(irValue);
205
30.8M
}
206
207
/// Helper type for legalization an IR `call` instruction
208
struct LegalCallBuilder
209
{
210
    LegalCallBuilder(IRTypeLegalizationContext* context, IRCall* call)
211
5.00k
        : m_context(context), m_call(call)
212
5.00k
    {
213
5.00k
    }
214
215
    /// The context for legalization
216
    IRTypeLegalizationContext* m_context = nullptr;
217
218
    /// The `call` instruction we are legalizing
219
    IRCall* m_call = nullptr;
220
221
    /// The legalized arguments for the call
222
    ShortList<IRInst*> m_args;
223
224
    /// Add a logical argument to the call (which may map to zero or mmore actual arguments)
225
    void addArg(LegalVal const& val)
226
10.1k
    {
227
        // In order to add the argument(s) for `val`,
228
        // we will recurse over its structure.
229
230
10.1k
        switch (val.flavor)
231
10.1k
        {
232
4.14k
        case LegalVal::Flavor::none:
233
4.14k
            break;
234
235
5.47k
        case LegalVal::Flavor::simple:
236
5.47k
            m_args.add(val.getSimple());
237
5.47k
            break;
238
239
2
        case LegalVal::Flavor::implicitDeref:
240
2
            addArg(val.getImplicitDeref());
241
2
            break;
242
243
139
        case LegalVal::Flavor::pair:
244
139
            {
245
139
                auto pairVal = val.getPair();
246
139
                addArg(pairVal->ordinaryVal);
247
139
                addArg(pairVal->specialVal);
248
139
            }
249
139
            break;
250
251
348
        case LegalVal::Flavor::tuple:
252
348
            {
253
348
                auto tuplePsuedoVal = val.getTuple();
254
348
                for (auto elem : val.getTuple()->elements)
255
529
                {
256
529
                    addArg(elem.val);
257
529
                }
258
348
            }
259
348
            break;
260
261
0
        default:
262
0
            SLANG_UNEXPECTED("uhandled val flavor");
263
0
            break;
264
10.1k
        }
265
10.1k
    }
266
267
    /// Build a new call based on the original, given the expected `resultType`.
268
    ///
269
    /// Returns a value representing the result of the call.
270
    LegalVal build(LegalType const& resultType)
271
5.03k
    {
272
        // We can recursively decompose the cases for
273
        // how to legalize a call based on the expected
274
        // result type.
275
        //
276
5.03k
        switch (resultType.flavor)
277
5.03k
        {
278
3.01k
        case LegalType::Flavor::simple:
279
            // In the case where the result type is simple,
280
            // we can directly emit the `call` instruction
281
            // and use the result as our single result value.
282
            //
283
3.01k
            return LegalVal::simple(_emitCall(resultType.getSimple()));
284
285
1.96k
        case LegalType::Flavor::none:
286
            // In the case where there is no result type,
287
            // that is equivalent to the call returning `void`.
288
            //
289
            // We directly emit the call and then return an
290
            // empty value to represent the result.
291
            //
292
1.96k
            _emitCall(m_context->builder->getVoidType());
293
1.96k
            return LegalVal();
294
295
0
        case LegalVal::Flavor::implicitDeref:
296
            // An `implicitDeref` wraps a single value, so we can simply
297
            // unwrap, recurse on the innter value, and then wrap up
298
            // the result.
299
            //
300
0
            return LegalVal::implicitDeref(build(resultType.getImplicitDeref()->valueType));
301
302
28
        case LegalVal::Flavor::pair:
303
28
            {
304
                // A `pair` type consists of both an ordinary part and a special part.
305
                //
306
28
                auto pairType = resultType.getPair();
307
308
                // The ordinary part will be used as the direct result of the call,
309
                // while the special part will need to be returned via an `out`
310
                // argument.
311
                //
312
                // We will start by emitting the declaration(s) needed for those
313
                // `out` arguments that represent the special part, and adding
314
                // them to the argument list. Basically this step is declaring
315
                // local variables that will hold the special part of the result,
316
                // and it returns a value that repsents those variables.
317
                //
318
28
                auto specialVal = _addOutArg(pairType->specialType);
319
320
                // Once the argument values for the special part are set up,
321
                // we can recurse on the ordinary part and emit the actual
322
                // call operation (which will include our new arguments).
323
                //
324
28
                auto ordinaryVal = build(pairType->ordinaryType);
325
326
                // The resulting value will be a pair of the ordinary value
327
                // (returned from the `call` instruction) and the special value
328
                // (declared as zero or more local variables).
329
                //
330
28
                RefPtr<PairPseudoVal> pairVal = new PairPseudoVal();
331
28
                pairVal->pairInfo = pairType->pairInfo;
332
28
                pairVal->ordinaryVal = ordinaryVal;
333
28
                pairVal->specialVal = specialVal;
334
28
                return LegalVal::pair(pairVal);
335
0
            }
336
0
            break;
337
338
30
        case LegalVal::Flavor::tuple:
339
30
            {
340
                // A `tuple` value consists of zero or more elements
341
                // that are each of a "special" type. We will handle
342
                // *all* of those values as `out` arguments akin to
343
                // what we did for the special half of a pair type
344
                // above.
345
                //
346
30
                auto resultVal = _addOutArg(resultType);
347
348
                // In this case there was no "ordinary" part to the
349
                // result type of the function, so we know that
350
                // the legalization funciton/call will use a `void`
351
                // result type.
352
                //
353
30
                _emitCall(m_context->builder->getVoidType());
354
30
                return resultVal;
355
0
            }
356
0
            break;
357
358
0
        default:
359
            // TODO: implement legalization of non-simple return types
360
0
            SLANG_UNEXPECTED("unimplemented legalized return type for IRCall.");
361
5.03k
        }
362
5.03k
    }
363
364
private:
365
    /// Add an `out` argument to the call, to capture the given `resultType`.
366
    LegalVal _addOutArg(LegalType const& resultType)
367
168
    {
368
168
        switch (resultType.flavor)
369
168
        {
370
94
        case LegalType::Flavor::simple:
371
94
            {
372
                // In the leaf case we have a simple type, and
373
                // we just want to declare a local variable based on it.
374
                //
375
94
                auto simpleType = resultType.getSimple();
376
94
                auto builder = m_context->builder;
377
378
                // Recall that a local variable in our IR represents a *pointer*
379
                // to storage of the appropriate type.
380
                //
381
94
                auto varPtr = builder->emitVar(simpleType);
382
383
                // We need to pass that pointer as an argument to our new
384
                // `call` instruction, so that it can receive the value
385
                // written by the callee.
386
                //
387
94
                m_args.add(varPtr);
388
389
                // Note: Because `varPtr` is a pointer to the value we want,
390
                // we have the small problem of needing to return a `LegalVal`
391
                // that has dereferenced the value after the call.
392
                //
393
                // We solve this problem by inserting as `load` from our
394
                // new variable immediately after the call, before going
395
                // and resetting the insertion point to continue inserting
396
                // stuff before the call (which is where we wnat the local
397
                // variable declarations to go).
398
                //
399
                // TODO: Confirm that this logic can't go awry if (somehow)
400
                // there is no instruction after `m_call`. That should not
401
                // be possible inside of a function body, but it could in
402
                // theory be a problem if we ever have top-level module-scope
403
                // code representing initialization of constants and/or globals.
404
                //
405
94
                builder->setInsertBefore(m_call->getNextInst());
406
94
                auto val = builder->emitLoad(simpleType, varPtr);
407
94
                builder->setInsertBefore(m_call);
408
409
94
                return LegalVal::simple(val);
410
0
            }
411
0
            break;
412
413
            // The remaining cases are a straightforward structural recursion
414
            // on top of the base case above.
415
416
0
        case LegalType::Flavor::none:
417
0
            return LegalVal();
418
419
0
        case LegalVal::Flavor::implicitDeref:
420
0
            return LegalVal::implicitDeref(_addOutArg(resultType.getImplicitDeref()->valueType));
421
422
0
        case LegalVal::Flavor::pair:
423
0
            {
424
0
                auto pairType = resultType.getPair();
425
0
                auto specialVal = _addOutArg(pairType->specialType);
426
0
                auto ordinaryVal = _addOutArg(pairType->ordinaryType);
427
428
0
                RefPtr<PairPseudoVal> pairVal = new PairPseudoVal();
429
0
                pairVal->pairInfo = pairType->pairInfo;
430
0
                pairVal->ordinaryVal = ordinaryVal;
431
0
                pairVal->specialVal = specialVal;
432
433
0
                return LegalVal::pair(pairVal);
434
0
            }
435
0
            break;
436
437
74
        case LegalVal::Flavor::tuple:
438
74
            {
439
74
                auto tuplePsuedoType = resultType.getTuple();
440
441
74
                RefPtr<TuplePseudoVal> tupleVal = new TuplePseudoVal();
442
74
                for (auto typeElement : tuplePsuedoType->elements)
443
110
                {
444
110
                    TuplePseudoVal::Element valElement;
445
110
                    valElement.key = typeElement.key;
446
110
                    valElement.val = _addOutArg(typeElement.type);
447
110
                    tupleVal->elements.add(valElement);
448
110
                }
449
450
74
                return LegalVal::tuple(tupleVal);
451
0
            }
452
0
            break;
453
454
0
        default:
455
            // TODO: implement legalization of non-simple return types
456
0
            SLANG_UNEXPECTED("unimplemented legalized return type for IRCall.");
457
168
        }
458
168
    }
459
460
    /// Emit the actual `call` instruction given an IR result type
461
    IRInst* _emitCall(IRType* resultType)
462
5.00k
    {
463
        // The generated call will include all of the arguments that have
464
        // been added up to this point, which includes those that were
465
        // added to represent legalized parts of the result type.
466
        //
467
5.00k
        return m_context->builder->emitCallInst(
468
5.00k
            resultType,
469
5.00k
            m_call->getCallee(),
470
5.00k
            m_args.getCount(),
471
5.00k
            m_args.getArrayView().getBuffer());
472
5.00k
    }
473
};
474
475
476
static LegalVal legalizeCall(IRTypeLegalizationContext* context, IRCall* callInst)
477
5.00k
{
478
5.00k
    LegalCallBuilder builder(context, callInst);
479
480
5.00k
    auto argCount = callInst->getArgCount();
481
14.3k
    for (UInt i = 0; i < argCount; i++)
482
9.29k
    {
483
9.29k
        auto legalArg = legalizeOperand(context, callInst->getArg(i));
484
9.29k
        builder.addArg(legalArg);
485
9.29k
    }
486
487
5.00k
    auto legalResultType = legalizeType(context, callInst->getFullType());
488
5.00k
    return builder.build(legalResultType);
489
5.00k
}
490
491
/// Helper type for legalizing a `returnVal` instruction
492
struct LegalReturnBuilder
493
{
494
    LegalReturnBuilder(IRTypeLegalizationContext* context, IRReturn* returnInst)
495
1.50k
        : m_context(context), m_returnInst(returnInst)
496
1.50k
    {
497
1.50k
    }
498
499
    /// Emit code to perform a return of `val`
500
    void returnVal(LegalVal val)
501
1.53k
    {
502
1.53k
        auto builder = m_context->builder;
503
504
1.53k
        switch (val.flavor)
505
1.53k
        {
506
31
        case LegalVal::Flavor::simple:
507
            // The case of a simple value is easy: just emit a `returnVal`.
508
            //
509
31
            builder->emitReturn(val.getSimple());
510
31
            break;
511
512
1.44k
        case LegalVal::Flavor::none:
513
            // The case of an empty/void value is also easy: emit a `return`.
514
            //
515
1.44k
            builder->emitReturn();
516
1.44k
            break;
517
518
0
        case LegalVal::Flavor::implicitDeref:
519
0
            returnVal(val.getImplicitDeref());
520
0
            break;
521
522
31
        case LegalVal::Flavor::pair:
523
31
            {
524
                // The case for a pair value is the main interesting one.
525
                // We need to write the special part of the return value
526
                // to the `out` parameters that were declared to capture
527
                // it, and then return the ordinary part of the value
528
                // like normal.
529
                //
530
                // Note that the order here matters, because we need to
531
                // emit the code that writes to the `out` parameters
532
                // before the `return` instruction.
533
                //
534
31
                auto pairVal = val.getPair();
535
31
                _writeResultParam(pairVal->specialVal);
536
31
                returnVal(pairVal->ordinaryVal);
537
31
            }
538
31
            break;
539
540
32
        case LegalVal::Flavor::tuple:
541
32
            {
542
                // The tuple case is kind of a degenerate combination
543
                // of the `pair` and `none` cases: we need to emit
544
                // writes to the `out` parameters declared to capture
545
                // the tuple (all of it), and then we do a `return`
546
                // of `void` because there is no ordinary result to
547
                // capture.
548
                //
549
32
                _writeResultParam(val);
550
32
                builder->emitReturn();
551
32
            }
552
32
            break;
553
554
0
        default:
555
            // TODO: implement legalization of non-simple return types
556
0
            SLANG_UNEXPECTED("unimplemented legalized return type for IRReturn.");
557
1.53k
        }
558
1.53k
    }
559
560
private:
561
    /// Write `val` to the `out` parameters of the enclosing function
562
    void _writeResultParam(LegalVal const& val)
563
172
    {
564
172
        switch (val.flavor)
565
172
        {
566
95
        case LegalVal::Flavor::simple:
567
95
            {
568
                // The leaf case here is the interesting one.
569
                //
570
                // We know that if we are writing to `out` parameters to
571
                // represent the function result then the function must
572
                // have been legalized in a way that introduced those parameters.
573
                // We thus need to look up the information on how the
574
                // function got legalized so that we can identify the
575
                // new parameters.
576
                //
577
                // TODO: One detail worth confirming here is whether there
578
                // could ever be a case where a `return` instruction gets legalized
579
                // before its outer function does.
580
                //
581
95
                if (!m_parentFuncInfo)
582
63
                {
583
                    // We start by searching for the ancestor instruction
584
                    // that represents the function (or other code-bearing value)
585
                    // that holds this instruction.
586
                    //
587
63
                    auto p = m_returnInst->getParent();
588
126
                    while (p && !as<IRGlobalValueWithCode>(p))
589
63
                    {
590
63
                        p = p->parent;
591
63
                    }
592
593
                    // We expect that the parent is actually an IR function.
594
                    //
595
                    // TODO: What about the case where we have an `IRGlobalVar`
596
                    // of a type that needs legalization, and the variable has
597
                    // an initializer? For now, I believe that case is disallowed
598
                    // in the legalization for global variables.
599
                    //
600
63
                    auto parentFunc = as<IRFunc>(p);
601
63
                    SLANG_ASSERT(parentFunc);
602
63
                    if (!parentFunc)
603
0
                        return;
604
605
                    // We also expect that extended legalization information was
606
                    // recorded for the function.
607
                    //
608
63
                    RefPtr<LegalFuncInfo> parentFuncInfo;
609
63
                    if (!m_context->mapFuncToInfo.tryGetValue(parentFunc, parentFuncInfo))
610
0
                    {
611
                        // If we fail to find the extended information then either:
612
                        //
613
                        // * The parent function has not been legalized yet. This would
614
                        //   be a violation of our assumption about ordering of legalization.
615
                        //
616
                        // * The parent function was legalized, but didn't require any
617
                        //   additional IR parameters to represent its result. This would
618
                        //   be a violation of our assumption that the declared result type
619
                        //   of a function and the type at `return` sites inside the function
620
                        //   need to match.
621
                        //
622
0
                        SLANG_ASSERT(parentFuncInfo);
623
0
                        return;
624
0
                    }
625
626
                    // If we find the extended information, then this is the first
627
                    // leaf parameter we are dealing with, so we set up to read through
628
                    // the parameters starting at index zero.
629
                    //
630
63
                    m_parentFuncInfo = parentFuncInfo;
631
63
                    m_resultParamCounter = 0;
632
63
                }
633
95
                SLANG_ASSERT(m_parentFuncInfo);
634
635
                // The recursion through the result `val` will iterate over the
636
                // leaf parameters in the same order they should have been declared,
637
                // so the parameter we need to write to will be the next one in order.
638
                //
639
                // We expect that the parameter index must be in range, beacuse otherwise
640
                // the recursion here and the recursion that declared the parameters are
641
                // mismatched in terms of how they traversed the hierarchical representation
642
                // of `LegalVal` / `LegalType`.
643
                //
644
95
                Index resultParamIndex = m_resultParamCounter++;
645
95
                SLANG_ASSERT(resultParamIndex >= 0);
646
95
                SLANG_ASSERT(resultParamIndex < m_parentFuncInfo->resultParamVals.getCount());
647
648
                // Once we've identified the right parameter, we can emit a `store`
649
                // to write the value that the function wants to output.
650
                //
651
                // Note that an `out` parameter is represented with a pointer type
652
                // in the IR, so that the `IRParam` here represents a pointer to
653
                // the value that will receive the result.
654
                //
655
95
                auto resultParamPtr = m_parentFuncInfo->resultParamVals[resultParamIndex];
656
95
                m_context->builder->emitStore(resultParamPtr, val.getSimple());
657
95
            }
658
0
            break;
659
660
            // The remaining cases are just a straightforward recursion
661
            // over the structure of the `val`.
662
663
0
        case LegalVal::Flavor::none:
664
0
            break;
665
666
0
        case LegalVal::Flavor::implicitDeref:
667
0
            _writeResultParam(val.getImplicitDeref());
668
0
            break;
669
670
0
        case LegalVal::Flavor::pair:
671
0
            {
672
0
                auto pairVal = val.getPair();
673
0
                _writeResultParam(pairVal->ordinaryVal);
674
0
                _writeResultParam(pairVal->specialVal);
675
0
            }
676
0
            break;
677
678
77
        case LegalVal::Flavor::tuple:
679
77
            {
680
77
                auto tupleVal = val.getTuple();
681
77
                for (auto element : tupleVal->elements)
682
109
                {
683
109
                    _writeResultParam(element.val);
684
109
                }
685
77
            }
686
77
            break;
687
688
0
        default:
689
            // TODO: implement legalization of non-simple return types
690
0
            SLANG_UNEXPECTED("unimplemented legalized return type for IRReturn.");
691
172
        }
692
172
    }
693
694
    IRTypeLegalizationContext* m_context = nullptr;
695
    IRReturn* m_returnInst = nullptr;
696
697
    RefPtr<LegalFuncInfo> m_parentFuncInfo;
698
    Index m_resultParamCounter = 0;
699
};
700
701
static LegalVal legalizeRetVal(
702
    IRTypeLegalizationContext* context,
703
    LegalVal retVal,
704
    IRReturn* returnInst)
705
1.50k
{
706
1.50k
    LegalReturnBuilder builder(context, returnInst);
707
1.50k
    builder.returnVal(retVal);
708
1.50k
    return LegalVal();
709
1.50k
}
710
711
static void _addVal(ShortList<IRInst*>& rs, const LegalVal& legalVal)
712
0
{
713
0
    switch (legalVal.flavor)
714
0
    {
715
0
    case LegalVal::Flavor::simple:
716
0
        rs.add(legalVal.getSimple());
717
0
        break;
718
0
    case LegalVal::Flavor::tuple:
719
0
        for (auto element : legalVal.getTuple()->elements)
720
0
            _addVal(rs, element.val);
721
0
        break;
722
0
    case LegalVal::Flavor::pair:
723
0
        _addVal(rs, legalVal.getPair()->ordinaryVal);
724
0
        _addVal(rs, legalVal.getPair()->specialVal);
725
0
        break;
726
0
    case LegalVal::Flavor::none:
727
0
        break;
728
0
    default:
729
0
        SLANG_UNEXPECTED("unhandled legalized val flavor");
730
0
    }
731
0
}
732
733
static LegalVal legalizeUnconditionalBranch(
734
    IRTypeLegalizationContext* context,
735
    ArrayView<LegalVal> args,
736
    IRUnconditionalBranch* branchInst)
737
34
{
738
34
    ShortList<IRInst*> newArgs;
739
34
    for (auto arg : args)
740
116
    {
741
116
        switch (arg.flavor)
742
116
        {
743
48
        case LegalVal::Flavor::none:
744
48
            break;
745
68
        case LegalVal::Flavor::simple:
746
68
            newArgs.add(arg.getSimple());
747
68
            break;
748
0
        case LegalVal::Flavor::pair:
749
0
            _addVal(newArgs, arg.getPair()->ordinaryVal);
750
0
            _addVal(newArgs, arg.getPair()->specialVal);
751
0
            break;
752
0
        case LegalVal::Flavor::tuple:
753
0
            for (auto element : arg.getTuple()->elements)
754
0
            {
755
0
                _addVal(newArgs, element.val);
756
0
            }
757
0
            break;
758
0
        default:
759
0
            SLANG_UNIMPLEMENTED_X("Unknown legalized val flavor.");
760
116
        }
761
116
    }
762
34
    context->builder->emitIntrinsicInst(
763
34
        nullptr,
764
34
        branchInst->getOp(),
765
34
        newArgs.getCount(),
766
34
        newArgs.getArrayView().getBuffer());
767
34
    return LegalVal();
768
34
}
769
770
static LegalVal legalizeLoad(IRTypeLegalizationContext* context, LegalVal legalPtrVal)
771
1.11k
{
772
1.11k
    switch (legalPtrVal.flavor)
773
1.11k
    {
774
312
    case LegalVal::Flavor::none:
775
312
        return LegalVal();
776
777
223
    case LegalVal::Flavor::simple:
778
223
        {
779
223
            return LegalVal::simple(context->builder->emitLoad(legalPtrVal.getSimple()));
780
0
        }
781
0
        break;
782
783
322
    case LegalVal::Flavor::implicitDeref:
784
        // We have turne a pointer(-like) type into its pointed-to (value)
785
        // type, and so the operation of loading goes away; we just use
786
        // the underlying value.
787
322
        return legalPtrVal.getImplicitDeref();
788
789
95
    case LegalVal::Flavor::pair:
790
95
        {
791
95
            auto ptrPairVal = legalPtrVal.getPair();
792
793
95
            auto ordinaryVal = legalizeLoad(context, ptrPairVal->ordinaryVal);
794
95
            auto specialVal = legalizeLoad(context, ptrPairVal->specialVal);
795
95
            return LegalVal::pair(ordinaryVal, specialVal, ptrPairVal->pairInfo);
796
0
        }
797
798
160
    case LegalVal::Flavor::tuple:
799
160
        {
800
            // We need to emit a load for each element of
801
            // the tuple.
802
160
            auto ptrTupleVal = legalPtrVal.getTuple();
803
160
            RefPtr<TuplePseudoVal> tupleVal = new TuplePseudoVal();
804
805
160
            for (auto ee : legalPtrVal.getTuple()->elements)
806
210
            {
807
210
                TuplePseudoVal::Element element;
808
210
                element.key = ee.key;
809
210
                element.val = legalizeLoad(context, ee.val);
810
811
210
                tupleVal->elements.add(element);
812
210
            }
813
160
            return LegalVal::tuple(tupleVal);
814
0
        }
815
0
        break;
816
817
0
    default:
818
0
        SLANG_UNEXPECTED("unhandled case");
819
0
        break;
820
1.11k
    }
821
1.11k
}
822
823
static LegalVal legalizePrintf(IRTypeLegalizationContext* context, ArrayView<LegalVal> args)
824
13
{
825
13
    ShortList<IRInst*> legalArgs;
826
13
    for (auto arg : args)
827
26
    {
828
26
        switch (arg.flavor)
829
26
        {
830
13
        case LegalVal::Flavor::none:
831
13
            break;
832
13
        case LegalVal::Flavor::simple:
833
13
            legalArgs.add(arg.getSimple());
834
13
            break;
835
0
        case LegalVal::Flavor::pair:
836
0
            legalArgs.add(arg.getPair()->ordinaryVal.getSimple());
837
0
            break;
838
0
        default:
839
0
            SLANG_UNIMPLEMENTED_X("Unknown legalized val flavor for printf operand");
840
26
        }
841
26
    }
842
13
    return LegalVal::simple(context->builder->emitIntrinsicInst(
843
13
        context->builder->getVoidType(),
844
13
        kIROp_Printf,
845
13
        (UInt)legalArgs.getCount(),
846
13
        legalArgs.getArrayView().getBuffer()));
847
13
}
848
849
static LegalVal legalizeDebugVar(
850
    IRTypeLegalizationContext* context,
851
    LegalType type,
852
    IRDebugVar* originalInst)
853
913
{
854
    // For now we just discard any special part and keep the ordinary part.
855
856
913
    switch (type.flavor)
857
913
    {
858
1
    case LegalType::Flavor::simple:
859
1
        {
860
1
            auto pointedToType = tryGetPointedToType(context->builder, type.getSimple());
861
862
            // Check if the type is debuggable before creating DebugVar
863
1
            DebugValueStoreContext debugContext;
864
1
            if (!debugContext.isDebuggableType(pointedToType))
865
1
            {
866
1
                return LegalVal();
867
1
            }
868
869
0
            auto legalVal = context->builder->emitDebugVar(
870
0
                pointedToType,
871
0
                originalInst->getSource(),
872
0
                originalInst->getLine(),
873
0
                originalInst->getCol(),
874
0
                originalInst->getArgIndex());
875
0
            copyNameHintAndDebugDecorations(legalVal, originalInst);
876
0
            return LegalVal::simple(legalVal);
877
1
        }
878
911
    case LegalType::Flavor::none:
879
911
        return LegalVal();
880
0
    case LegalType::Flavor::pair:
881
0
        {
882
0
            auto pairType = type.getPair();
883
0
            auto ordinaryVal = legalizeDebugVar(context, pairType->ordinaryType, originalInst);
884
0
            return ordinaryVal;
885
1
        }
886
1
    case LegalType::Flavor::tuple:
887
1
        {
888
1
            auto tupleType = type.getTuple();
889
1
            for (auto ee : tupleType->elements)
890
1
            {
891
1
                auto innerResult = legalizeDebugVar(context, ee.type, originalInst);
892
1
                if (innerResult.flavor != LegalVal::Flavor::none)
893
0
                    return innerResult;
894
1
            }
895
1
            return LegalVal();
896
1
        }
897
0
    default:
898
0
        return LegalVal();
899
913
    }
900
913
}
901
902
static LegalVal legalizeDebugValue(
903
    IRTypeLegalizationContext* context,
904
    LegalVal debugVar,
905
    LegalVal debugValue,
906
    IRDebugValue* originalInst)
907
794
{
908
794
    if (debugVar.flavor == LegalVal::Flavor::none)
909
794
        return LegalVal();
910
911
    // For now we just discard any special part and keep the ordinary part.
912
0
    switch (debugValue.flavor)
913
0
    {
914
0
    case LegalType::Flavor::simple:
915
0
        return LegalVal::simple(
916
0
            context->builder->emitDebugValue(debugVar.getSimple(), debugValue.getSimple()));
917
0
    case LegalType::Flavor::none:
918
0
        return LegalVal();
919
0
    case LegalType::Flavor::pair:
920
0
        {
921
            // The var should be legalized as a simple value, because we discard the special part
922
            // for debug info insts.
923
            //
924
0
            SLANG_ASSERT(debugVar.flavor == LegalVal::Flavor::simple);
925
0
            auto ordinaryVal = legalizeDebugValue(
926
0
                context,
927
0
                debugVar,
928
0
                debugValue.getPair()->ordinaryVal,
929
0
                originalInst);
930
0
            return ordinaryVal;
931
0
        }
932
0
    case LegalType::Flavor::tuple:
933
0
        {
934
0
            auto tupleVal = debugValue.getTuple();
935
0
            for (auto ee : tupleVal->elements)
936
0
            {
937
0
                auto innerResult = legalizeDebugValue(context, debugVar, ee.val, originalInst);
938
0
                if (innerResult.flavor != LegalVal::Flavor::none)
939
0
                    return innerResult;
940
0
            }
941
0
            return LegalVal();
942
0
        }
943
0
    default:
944
0
        return LegalVal();
945
0
    }
946
0
}
947
948
static LegalVal legalizeStore(
949
    IRTypeLegalizationContext* context,
950
    LegalVal legalPtrVal,
951
    LegalVal legalVal)
952
1.79k
{
953
1.79k
    switch (legalPtrVal.flavor)
954
1.79k
    {
955
1.61k
    case LegalVal::Flavor::none:
956
1.61k
        return LegalVal();
957
958
101
    case LegalVal::Flavor::simple:
959
101
        {
960
101
            if (legalVal.flavor == LegalVal::Flavor::none)
961
0
                return LegalVal();
962
101
            context->builder->emitStore(legalPtrVal.getSimple(), legalVal.getSimple());
963
101
            return legalVal;
964
101
        }
965
0
        break;
966
967
0
    case LegalVal::Flavor::implicitDeref:
968
        // TODO: what is the right behavior here?
969
        //
970
        // The crux of the problem is that we may legalize a pointer-to-pointer
971
        // type in cases where one of the two needs to become an implicit-deref,
972
        // so that we have `PtrA<PtrB<Thing>>` become, say, `PtrA<Thing>` with
973
        // an `implicitDeref` wrapper. When we encounter a store to that
974
        // wrapped value, we seemingly need to know whether the original code
975
        // meant to store to `*ptrPtr` or `**ptrPtr`, and need to legalize
976
        // the result accordingly...
977
        //
978
0
        if (legalVal.flavor == LegalVal::Flavor::implicitDeref)
979
0
            return legalizeStore(
980
0
                context,
981
0
                legalPtrVal.getImplicitDeref(),
982
0
                legalVal.getImplicitDeref());
983
0
        else
984
0
            return legalizeStore(context, legalPtrVal.getImplicitDeref(), legalVal);
985
986
33
    case LegalVal::Flavor::pair:
987
33
        {
988
33
            auto destPair = legalPtrVal.getPair();
989
33
            auto valPair = legalVal.getPair();
990
33
            legalizeStore(context, destPair->ordinaryVal, valPair->ordinaryVal);
991
33
            legalizeStore(context, destPair->specialVal, valPair->specialVal);
992
33
            return LegalVal();
993
0
        }
994
995
52
    case LegalVal::Flavor::tuple:
996
52
        {
997
            // We need to emit a store for each element of
998
            // the tuple.
999
52
            auto destTuple = legalPtrVal.getTuple();
1000
52
            auto valTuple = legalVal.getTuple();
1001
52
            SLANG_ASSERT(destTuple->elements.getCount() == valTuple->elements.getCount());
1002
1003
134
            for (Index i = 0; i < valTuple->elements.getCount(); i++)
1004
82
            {
1005
82
                legalizeStore(context, destTuple->elements[i].val, valTuple->elements[i].val);
1006
82
            }
1007
52
            return legalVal;
1008
0
        }
1009
0
        break;
1010
1011
0
    default:
1012
0
        SLANG_UNEXPECTED("unhandled case");
1013
0
        break;
1014
1.79k
    }
1015
1.79k
}
1016
1017
static LegalVal legalizeFieldExtract(
1018
    IRTypeLegalizationContext* context,
1019
    LegalType type,
1020
    LegalVal legalStructOperand,
1021
    IRStructKey* fieldKey)
1022
3.39k
{
1023
3.39k
    auto builder = context->builder;
1024
1025
3.39k
    if (type.flavor == LegalType::Flavor::none)
1026
1.82k
        return LegalVal();
1027
1028
1.56k
    switch (legalStructOperand.flavor)
1029
1.56k
    {
1030
0
    case LegalVal::Flavor::none:
1031
0
        return LegalVal();
1032
1033
177
    case LegalVal::Flavor::simple:
1034
177
        return LegalVal::simple(
1035
177
            builder->emitFieldExtract(type.getSimple(), legalStructOperand.getSimple(), fieldKey));
1036
1037
262
    case LegalVal::Flavor::pair:
1038
262
        {
1039
            // There are two sides, the ordinary and the special,
1040
            // and we basically just dispatch to both of them.
1041
262
            auto pairVal = legalStructOperand.getPair();
1042
262
            auto pairInfo = pairVal->pairInfo;
1043
262
            auto pairElement = pairInfo->findElement(fieldKey);
1044
262
            if (!pairElement)
1045
0
            {
1046
0
                SLANG_UNEXPECTED("didn't find tuple element");
1047
0
                UNREACHABLE_RETURN(LegalVal());
1048
0
            }
1049
1050
            // If the field we are extracting has a pair type,
1051
            // that means it exists on both the ordinary and
1052
            // special sides.
1053
262
            RefPtr<PairInfo> fieldPairInfo;
1054
262
            LegalType ordinaryType = type;
1055
262
            LegalType specialType = type;
1056
262
            if (type.flavor == LegalType::Flavor::pair)
1057
82
            {
1058
82
                auto fieldPairType = type.getPair();
1059
82
                fieldPairInfo = fieldPairType->pairInfo;
1060
82
                ordinaryType = fieldPairType->ordinaryType;
1061
82
                specialType = fieldPairType->specialType;
1062
82
            }
1063
1064
262
            LegalVal ordinaryVal;
1065
262
            LegalVal specialVal;
1066
1067
262
            if (pairElement->flags & PairInfo::kFlag_hasOrdinary)
1068
177
            {
1069
177
                ordinaryVal =
1070
177
                    legalizeFieldExtract(context, ordinaryType, pairVal->ordinaryVal, fieldKey);
1071
177
            }
1072
1073
262
            if (pairElement->flags & PairInfo::kFlag_hasSpecial)
1074
165
            {
1075
165
                specialVal =
1076
165
                    legalizeFieldExtract(context, specialType, pairVal->specialVal, fieldKey);
1077
165
            }
1078
262
            return LegalVal::pair(ordinaryVal, specialVal, fieldPairInfo);
1079
262
        }
1080
0
        break;
1081
1082
1.12k
    case LegalVal::Flavor::tuple:
1083
1.12k
        {
1084
            // The operand is a tuple of pointer-like
1085
            // values, we want to extract the element
1086
            // corresponding to a field. We will handle
1087
            // this by simply returning the corresponding
1088
            // element from the operand.
1089
1.12k
            auto ptrTupleInfo = legalStructOperand.getTuple();
1090
1.12k
            for (auto ee : ptrTupleInfo->elements)
1091
1.68k
            {
1092
1.68k
                if (ee.key == fieldKey)
1093
1.12k
                {
1094
1.12k
                    return ee.val;
1095
1.12k
                }
1096
1.68k
            }
1097
1098
            // TODO: we can legally reach this case now
1099
            // when the field is "ordinary".
1100
1101
0
            SLANG_UNEXPECTED("didn't find tuple element");
1102
0
            UNREACHABLE_RETURN(LegalVal());
1103
0
        }
1104
1105
0
    default:
1106
0
        SLANG_UNEXPECTED("unhandled");
1107
0
        UNREACHABLE_RETURN(LegalVal());
1108
1.56k
    }
1109
1.56k
}
1110
1111
static LegalVal legalizeFieldExtract(
1112
    IRTypeLegalizationContext* context,
1113
    LegalType type,
1114
    LegalVal legalPtrOperand,
1115
    LegalVal legalFieldOperand)
1116
2.81k
{
1117
    // We don't expect any legalization to affect
1118
    // the "field" argument.
1119
2.81k
    auto fieldKey = legalFieldOperand.getSimple();
1120
1121
2.81k
    return legalizeFieldExtract(context, type, legalPtrOperand, (IRStructKey*)fieldKey);
1122
2.81k
}
1123
1124
/// Take a value of some buffer/pointer type and unwrap it according to provided info.
1125
static LegalVal unwrapBufferValue(
1126
    IRTypeLegalizationContext* context,
1127
    LegalVal legalPtrOperand,
1128
    LegalElementWrapping const& elementInfo)
1129
0
{
1130
    // The `elementInfo` tells us how a non-simple element
1131
    // type was wrapped up into a new structure types used
1132
    // as the element type of the buffer.
1133
    //
1134
    // This function will recurse through the structure of
1135
    // `elementInfo` to pull out all the required data from
1136
    // the buffer represented by `legalPtrOperand`.
1137
1138
0
    switch (elementInfo.flavor)
1139
0
    {
1140
0
    default:
1141
0
        SLANG_UNEXPECTED("unhandled");
1142
0
        UNREACHABLE_RETURN(LegalVal());
1143
0
        break;
1144
1145
0
    case LegalElementWrapping::Flavor::none:
1146
0
        return LegalVal();
1147
1148
0
    case LegalElementWrapping::Flavor::simple:
1149
0
        {
1150
            // In the leaf case, we just had to store some
1151
            // data of a simple type in the buffer. We can
1152
            // produce a valid result by computing the
1153
            // address of the field used to represent the
1154
            // element, and then returning *that* as if
1155
            // it were the buffer type itself.
1156
            //
1157
            // (Basically instead of `someBuffer` we will
1158
            // end up with `&(someBuffer->field)`.
1159
            //
1160
0
            auto builder = context->getBuilder();
1161
1162
0
            auto simpleElementInfo = elementInfo.getSimple();
1163
0
            auto valPtr = builder->emitFieldAddress(
1164
0
                builder->getPtrType(simpleElementInfo->type),
1165
0
                legalPtrOperand.getSimple(),
1166
0
                simpleElementInfo->key);
1167
1168
0
            return LegalVal::simple(valPtr);
1169
0
        }
1170
1171
0
    case LegalElementWrapping::Flavor::implicitDeref:
1172
0
        {
1173
            // If the element type was logically `ImplicitDeref<T>`,
1174
            // then we declared actual fields based on `T`, and
1175
            // we need to extract references to those fields and
1176
            // wrap them up in an `implicitDeref` value.
1177
            //
1178
0
            auto derefField = elementInfo.getImplicitDeref();
1179
0
            auto baseVal = unwrapBufferValue(context, legalPtrOperand, derefField->field);
1180
0
            return LegalVal::implicitDeref(baseVal);
1181
0
        }
1182
1183
0
    case LegalElementWrapping::Flavor::pair:
1184
0
        {
1185
            // If the element type was logically a `Pair<O,S>`
1186
            // then we encoded fields for both `O` and `S` into
1187
            // the actual element type, and now we need to
1188
            // extract references to both and pair them up.
1189
            //
1190
0
            auto pairField = elementInfo.getPair();
1191
0
            auto pairInfo = pairField->pairInfo;
1192
1193
0
            auto ordinaryVal = unwrapBufferValue(context, legalPtrOperand, pairField->ordinary);
1194
0
            auto specialVal = unwrapBufferValue(context, legalPtrOperand, pairField->special);
1195
0
            return LegalVal::pair(ordinaryVal, specialVal, pairInfo);
1196
0
        }
1197
1198
0
    case LegalElementWrapping::Flavor::tuple:
1199
0
        {
1200
            // If the element type was logically a `Tuple<E0, E1, ...>`
1201
            // then we encoded fields for each of the `Ei` and
1202
            // need to extract references to all of them and
1203
            // encode them as a tuple.
1204
            //
1205
0
            auto tupleField = elementInfo.getTuple();
1206
1207
0
            RefPtr<TuplePseudoVal> obj = new TuplePseudoVal();
1208
0
            for (auto ee : tupleField->elements)
1209
0
            {
1210
0
                auto elementVal = unwrapBufferValue(context, legalPtrOperand, ee.field);
1211
1212
0
                TuplePseudoVal::Element element;
1213
0
                element.key = ee.key;
1214
0
                element.val = unwrapBufferValue(context, legalPtrOperand, ee.field);
1215
0
                obj->elements.add(element);
1216
0
            }
1217
1218
0
            return LegalVal::tuple(obj);
1219
0
        }
1220
0
    }
1221
0
}
1222
1223
static IRType* getPointedToType(IRTypeLegalizationContext* context, IRType* ptrType)
1224
227
{
1225
227
    auto valueType = tryGetPointedToType(context->builder, ptrType);
1226
227
    if (!valueType)
1227
0
    {
1228
0
        SLANG_UNEXPECTED("expected a pointer type during type legalization");
1229
0
    }
1230
227
    return valueType;
1231
227
}
1232
1233
static LegalType getPointedToType(IRTypeLegalizationContext* context, LegalType type)
1234
316
{
1235
316
    switch (type.flavor)
1236
316
    {
1237
0
    case LegalType::Flavor::none:
1238
0
        return LegalType();
1239
1240
227
    case LegalType::Flavor::simple:
1241
227
        return LegalType::simple(getPointedToType(context, type.getSimple()));
1242
1243
20
    case LegalType::Flavor::implicitDeref:
1244
20
        return type.getImplicitDeref()->valueType;
1245
1246
0
    case LegalType::Flavor::pair:
1247
0
        {
1248
0
            auto pairType = type.getPair();
1249
0
            auto ordinary = getPointedToType(context, pairType->ordinaryType);
1250
0
            auto special = getPointedToType(context, pairType->specialType);
1251
0
            return LegalType::pair(ordinary, special, pairType->pairInfo);
1252
0
        }
1253
1254
69
    case LegalType::Flavor::tuple:
1255
69
        {
1256
69
            auto tupleType = type.getTuple();
1257
69
            RefPtr<TuplePseudoType> resultTuple = new TuplePseudoType();
1258
69
            for (auto ee : tupleType->elements)
1259
72
            {
1260
72
                TuplePseudoType::Element resultElement;
1261
72
                resultElement.key = ee.key;
1262
72
                resultElement.type = getPointedToType(context, ee.type);
1263
72
                resultTuple->elements.add(resultElement);
1264
72
            }
1265
69
            return LegalType::tuple(resultTuple);
1266
0
        }
1267
1268
0
    default:
1269
0
        SLANG_UNEXPECTED("unhandled case in type legalization");
1270
0
        UNREACHABLE_RETURN(LegalType());
1271
316
    }
1272
316
}
1273
1274
static LegalVal legalizeFieldAddress(
1275
    IRTypeLegalizationContext* context,
1276
    LegalType type,
1277
    LegalVal legalPtrOperand,
1278
    IRStructKey* fieldKey)
1279
2.73k
{
1280
2.73k
    auto builder = context->builder;
1281
2.73k
    if (type.flavor == LegalType::Flavor::none)
1282
1.39k
        return LegalVal();
1283
1284
1.33k
    switch (legalPtrOperand.flavor)
1285
1.33k
    {
1286
0
    case LegalVal::Flavor::none:
1287
0
        return LegalVal();
1288
1289
346
    case LegalVal::Flavor::simple:
1290
346
        switch (type.flavor)
1291
346
        {
1292
0
        case LegalType::Flavor::implicitDeref:
1293
            // TODO: Should this case be needed?
1294
0
            return legalizeFieldAddress(
1295
0
                context,
1296
0
                type.getImplicitDeref()->valueType,
1297
0
                legalPtrOperand,
1298
0
                fieldKey);
1299
1300
346
        default:
1301
346
            return LegalVal::simple(
1302
346
                builder->emitFieldAddress(legalPtrOperand.getSimple(), fieldKey));
1303
346
        }
1304
1305
572
    case LegalVal::Flavor::pair:
1306
572
        {
1307
            // There are two sides, the ordinary and the special,
1308
            // and we basically just dispatch to both of them.
1309
572
            auto pairVal = legalPtrOperand.getPair();
1310
572
            auto pairInfo = pairVal->pairInfo;
1311
572
            auto pairElement = pairInfo->findElement(fieldKey);
1312
572
            if (!pairElement)
1313
0
            {
1314
0
                SLANG_UNEXPECTED("didn't find tuple element");
1315
0
                UNREACHABLE_RETURN(LegalVal());
1316
0
            }
1317
1318
            // If the field we are extracting has a pair type,
1319
            // that means it exists on both the ordinary and
1320
            // special sides.
1321
572
            RefPtr<PairInfo> fieldPairInfo;
1322
572
            LegalType ordinaryType = type;
1323
572
            LegalType specialType = type;
1324
572
            if (type.flavor == LegalType::Flavor::pair)
1325
88
            {
1326
88
                auto fieldPairType = type.getPair();
1327
88
                fieldPairInfo = fieldPairType->pairInfo;
1328
88
                ordinaryType = fieldPairType->ordinaryType;
1329
88
                specialType = fieldPairType->specialType;
1330
88
            }
1331
1332
572
            LegalVal ordinaryVal;
1333
572
            LegalVal specialVal;
1334
1335
572
            if (pairElement->flags & PairInfo::kFlag_hasOrdinary)
1336
346
            {
1337
346
                ordinaryVal =
1338
346
                    legalizeFieldAddress(context, ordinaryType, pairVal->ordinaryVal, fieldKey);
1339
346
            }
1340
1341
572
            if (pairElement->flags & PairInfo::kFlag_hasSpecial)
1342
300
            {
1343
300
                specialVal =
1344
300
                    legalizeFieldAddress(context, specialType, pairVal->specialVal, fieldKey);
1345
300
            }
1346
572
            return LegalVal::pair(ordinaryVal, specialVal, fieldPairInfo);
1347
572
        }
1348
0
        break;
1349
1350
180
    case LegalVal::Flavor::tuple:
1351
180
        {
1352
            // The operand is a tuple of pointer-like
1353
            // values, we want to extract the element
1354
            // corresponding to a field. We will handle
1355
            // this by simply returning the corresponding
1356
            // element from the operand.
1357
180
            auto ptrTupleInfo = legalPtrOperand.getTuple();
1358
180
            for (auto ee : ptrTupleInfo->elements)
1359
247
            {
1360
247
                if (ee.key == fieldKey)
1361
180
                {
1362
180
                    return ee.val;
1363
180
                }
1364
247
            }
1365
1366
            // TODO: we can legally reach this case now
1367
            // when the field is "ordinary".
1368
1369
0
            SLANG_UNEXPECTED("didn't find tuple element");
1370
0
            UNREACHABLE_RETURN(LegalVal());
1371
0
        }
1372
1373
240
    case LegalVal::Flavor::implicitDeref:
1374
240
        {
1375
            // The original value had a level of indirection
1376
            // that is now being removed, so should not be
1377
            // able to get at the *address* of the field any
1378
            // more, and need to resign ourselves to just
1379
            // getting at the field *value* and then
1380
            // adding an implicit dereference on top of that.
1381
            //
1382
240
            auto implicitDerefVal = legalPtrOperand.getImplicitDeref();
1383
240
            auto valueType = getPointedToType(context, type);
1384
240
            return LegalVal::implicitDeref(
1385
240
                legalizeFieldExtract(context, valueType, implicitDerefVal, fieldKey));
1386
0
        }
1387
1388
0
    default:
1389
0
        SLANG_UNEXPECTED("unhandled");
1390
0
        UNREACHABLE_RETURN(LegalVal());
1391
1.33k
    }
1392
1.33k
}
1393
1394
static LegalVal legalizeFieldAddress(
1395
    IRTypeLegalizationContext* context,
1396
    LegalType type,
1397
    LegalVal legalPtrOperand,
1398
    LegalVal legalFieldOperand)
1399
2.08k
{
1400
    // We don't expect any legalization to affect
1401
    // the "field" argument.
1402
2.08k
    auto fieldKey = legalFieldOperand.getSimple();
1403
1404
2.08k
    return legalizeFieldAddress(context, type, legalPtrOperand, (IRStructKey*)fieldKey);
1405
2.08k
}
1406
1407
static LegalVal legalizeGetElement(
1408
    IRTypeLegalizationContext* context,
1409
    LegalType type,
1410
    LegalVal legalPtrOperand,
1411
    IRInst* indexOperand)
1412
103
{
1413
103
    auto builder = context->builder;
1414
1415
103
    switch (legalPtrOperand.flavor)
1416
103
    {
1417
0
    case LegalVal::Flavor::none:
1418
0
        return LegalVal();
1419
1420
63
    case LegalVal::Flavor::simple:
1421
63
        return LegalVal::simple(builder->emitElementExtract(
1422
63
            type.getSimple(),
1423
63
            legalPtrOperand.getSimple(),
1424
63
            indexOperand));
1425
1426
6
    case LegalVal::Flavor::pair:
1427
6
        {
1428
            // There are two sides, the ordinary and the special,
1429
            // and we basically just dispatch to both of them.
1430
6
            auto pairVal = legalPtrOperand.getPair();
1431
6
            auto pairInfo = pairVal->pairInfo;
1432
1433
6
            LegalType ordinaryType = type;
1434
6
            LegalType specialType = type;
1435
6
            if (type.flavor == LegalType::Flavor::pair)
1436
6
            {
1437
6
                auto pairType = type.getPair();
1438
6
                ordinaryType = pairType->ordinaryType;
1439
6
                specialType = pairType->specialType;
1440
6
            }
1441
1442
6
            LegalVal ordinaryVal =
1443
6
                legalizeGetElement(context, ordinaryType, pairVal->ordinaryVal, indexOperand);
1444
1445
6
            LegalVal specialVal =
1446
6
                legalizeGetElement(context, specialType, pairVal->specialVal, indexOperand);
1447
1448
6
            return LegalVal::pair(ordinaryVal, specialVal, pairInfo);
1449
0
        }
1450
0
        break;
1451
1452
34
    case LegalVal::Flavor::tuple:
1453
34
        {
1454
            // The operand is a tuple of pointer-like
1455
            // values, we want to extract the element
1456
            // corresponding to a field. We will handle
1457
            // this by simply returning the corresponding
1458
            // element from the operand.
1459
34
            auto ptrTupleInfo = legalPtrOperand.getTuple();
1460
1461
34
            RefPtr<TuplePseudoVal> resTupleInfo = new TuplePseudoVal();
1462
1463
34
            auto tupleType = type.getTuple();
1464
34
            SLANG_ASSERT(tupleType);
1465
1466
34
            auto elemCount = ptrTupleInfo->elements.getCount();
1467
34
            SLANG_ASSERT(elemCount == tupleType->elements.getCount());
1468
1469
99
            for (Index ee = 0; ee < elemCount; ++ee)
1470
65
            {
1471
65
                auto ptrElem = ptrTupleInfo->elements[ee];
1472
65
                auto elemType = tupleType->elements[ee].type;
1473
1474
65
                TuplePseudoVal::Element resElem;
1475
65
                resElem.key = ptrElem.key;
1476
65
                resElem.val = legalizeGetElement(context, elemType, ptrElem.val, indexOperand);
1477
1478
65
                resTupleInfo->elements.add(resElem);
1479
65
            }
1480
1481
34
            return LegalVal::tuple(resTupleInfo);
1482
0
        }
1483
1484
0
    default:
1485
0
        SLANG_UNEXPECTED("unhandled");
1486
0
        UNREACHABLE_RETURN(LegalVal());
1487
103
    }
1488
103
}
1489
1490
static LegalVal legalizeGetElement(
1491
    IRTypeLegalizationContext* context,
1492
    LegalType type,
1493
    LegalVal legalPtrOperand,
1494
    LegalVal legalIndexOperand)
1495
22
{
1496
    // We don't expect any legalization to affect
1497
    // the "index" argument.
1498
22
    auto indexOperand = legalIndexOperand.getSimple();
1499
1500
22
    if (type.flavor == LegalType::Flavor::none)
1501
0
        return LegalVal();
1502
1503
22
    return legalizeGetElement(context, type, legalPtrOperand, indexOperand);
1504
22
}
1505
1506
static LegalVal legalizeGetElementPtr(
1507
    IRTypeLegalizationContext* context,
1508
    LegalType type,
1509
    LegalVal legalPtrOperand,
1510
    IRInst* indexOperand)
1511
4
{
1512
4
    auto builder = context->builder;
1513
1514
4
    switch (legalPtrOperand.flavor)
1515
4
    {
1516
0
    case LegalVal::Flavor::none:
1517
0
        return LegalVal();
1518
1519
0
    case LegalVal::Flavor::simple:
1520
0
        return LegalVal::simple(builder->emitElementAddress(
1521
0
            type.getSimple(),
1522
0
            legalPtrOperand.getSimple(),
1523
0
            indexOperand));
1524
1525
0
    case LegalVal::Flavor::pair:
1526
0
        {
1527
            // There are two sides, the ordinary and the special,
1528
            // and we basically just dispatch to both of them.
1529
0
            auto pairVal = legalPtrOperand.getPair();
1530
0
            auto pairInfo = pairVal->pairInfo;
1531
1532
0
            LegalType ordinaryType = type;
1533
0
            LegalType specialType = type;
1534
0
            if (type.flavor == LegalType::Flavor::pair)
1535
0
            {
1536
0
                auto pairType = type.getPair();
1537
0
                ordinaryType = pairType->ordinaryType;
1538
0
                specialType = pairType->specialType;
1539
0
            }
1540
1541
0
            LegalVal ordinaryVal =
1542
0
                legalizeGetElementPtr(context, ordinaryType, pairVal->ordinaryVal, indexOperand);
1543
1544
0
            LegalVal specialVal =
1545
0
                legalizeGetElementPtr(context, specialType, pairVal->specialVal, indexOperand);
1546
1547
0
            return LegalVal::pair(ordinaryVal, specialVal, pairInfo);
1548
0
        }
1549
0
        break;
1550
1551
0
    case LegalVal::Flavor::tuple:
1552
0
        {
1553
            // The operand is a tuple of pointer-like
1554
            // values, we want to extract the element
1555
            // corresponding to a field. We will handle
1556
            // this by simply returning the corresponding
1557
            // element from the operand.
1558
0
            auto ptrTupleInfo = legalPtrOperand.getTuple();
1559
1560
0
            RefPtr<TuplePseudoVal> resTupleInfo = new TuplePseudoVal();
1561
1562
0
            auto tupleType = type.getTuple();
1563
0
            SLANG_ASSERT(tupleType);
1564
1565
0
            auto elemCount = ptrTupleInfo->elements.getCount();
1566
0
            SLANG_ASSERT(elemCount == tupleType->elements.getCount());
1567
1568
0
            for (Index ee = 0; ee < elemCount; ++ee)
1569
0
            {
1570
0
                auto ptrElem = ptrTupleInfo->elements[ee];
1571
0
                auto elemType = tupleType->elements[ee].type;
1572
1573
0
                TuplePseudoVal::Element resElem;
1574
0
                resElem.key = ptrElem.key;
1575
0
                resElem.val = legalizeGetElementPtr(context, elemType, ptrElem.val, indexOperand);
1576
1577
0
                resTupleInfo->elements.add(resElem);
1578
0
            }
1579
1580
0
            return LegalVal::tuple(resTupleInfo);
1581
0
        }
1582
1583
4
    case LegalVal::Flavor::implicitDeref:
1584
4
        {
1585
            // The original value used to be a pointer to an array,
1586
            // and somebody is trying to get at an element pointer.
1587
            // Now we just have an array (wrapped with an implicit
1588
            // dereference) and need to just fetch the chosen element
1589
            // instead (and then wrap the element value with an
1590
            // implicit dereference).
1591
            //
1592
            // The result type for our `getElement` instruction needs
1593
            // to be the type *pointed to* by `type`, and not `type.
1594
            //
1595
4
            auto valueType = getPointedToType(context, type);
1596
1597
4
            auto implicitDerefVal = legalPtrOperand.getImplicitDeref();
1598
4
            return LegalVal::implicitDeref(
1599
4
                legalizeGetElement(context, valueType, implicitDerefVal, indexOperand));
1600
0
        }
1601
1602
0
    default:
1603
0
        SLANG_UNEXPECTED("unhandled");
1604
0
        UNREACHABLE_RETURN(LegalVal());
1605
4
    }
1606
4
}
1607
1608
static LegalVal legalizeGetElementPtr(
1609
    IRTypeLegalizationContext* context,
1610
    LegalType type,
1611
    LegalVal legalPtrOperand,
1612
    LegalVal legalIndexOperand)
1613
4
{
1614
    // We don't expect any legalization to affect
1615
    // the "index" argument.
1616
4
    auto indexOperand = legalIndexOperand.getSimple();
1617
1618
4
    return legalizeGetElementPtr(context, type, legalPtrOperand, indexOperand);
1619
4
}
1620
1621
static LegalVal legalizeMakeStruct(
1622
    IRTypeLegalizationContext* context,
1623
    LegalType legalType,
1624
    LegalVal const* legalArgs,
1625
    UInt argCount)
1626
5.03k
{
1627
5.03k
    auto builder = context->builder;
1628
1629
5.03k
    switch (legalType.flavor)
1630
5.03k
    {
1631
3.00k
    case LegalType::Flavor::none:
1632
3.00k
        return LegalVal();
1633
1634
1.96k
    case LegalType::Flavor::simple:
1635
1.96k
        {
1636
1.96k
            List<IRInst*> args;
1637
6.31k
            for (UInt aa = 0; aa < argCount; ++aa)
1638
4.35k
            {
1639
                // Ignore none values.
1640
4.35k
                if (legalArgs[aa].flavor == LegalVal::Flavor::none)
1641
2.10k
                {
1642
2.10k
                    if (!legalType.getSimple()->findDecoration<IROptimizableTypeDecoration>())
1643
905
                        args.add(builder->getVoidValue());
1644
2.10k
                    continue;
1645
2.10k
                }
1646
1647
                // Note: we assume that all the arguments
1648
                // must be simple here, because otherwise
1649
                // the `struct` type with them as fields
1650
                // would not be simple...
1651
                //
1652
2.24k
                args.add(legalArgs[aa].getSimple());
1653
2.24k
            }
1654
1.96k
            return LegalVal::simple(
1655
1.96k
                builder->emitMakeStruct(legalType.getSimple(), args.getCount(), args.getBuffer()));
1656
0
        }
1657
1658
27
    case LegalType::Flavor::pair:
1659
27
        {
1660
            // There are two sides, the ordinary and the special,
1661
            // and we basically just dispatch to both of them.
1662
27
            auto pairType = legalType.getPair();
1663
27
            auto pairInfo = pairType->pairInfo;
1664
27
            LegalType ordinaryType = pairType->ordinaryType;
1665
27
            LegalType specialType = pairType->specialType;
1666
1667
27
            List<LegalVal> ordinaryArgs;
1668
27
            List<LegalVal> specialArgs;
1669
27
            UInt argCounter = 0;
1670
27
            for (auto ee : pairInfo->elements)
1671
56
            {
1672
56
                UInt argIndex = argCounter++;
1673
56
                LegalVal arg = legalArgs[argIndex];
1674
1675
56
                if (arg.flavor == LegalVal::Flavor::pair)
1676
20
                {
1677
                    // The argument is itself a pair
1678
20
                    auto argPair = arg.getPair();
1679
20
                    ordinaryArgs.add(argPair->ordinaryVal);
1680
20
                    specialArgs.add(argPair->specialVal);
1681
20
                }
1682
36
                else if (ee.flags & Slang::PairInfo::kFlag_hasOrdinary)
1683
20
                {
1684
20
                    ordinaryArgs.add(arg);
1685
20
                }
1686
16
                else if (ee.flags & Slang::PairInfo::kFlag_hasSpecial)
1687
16
                {
1688
16
                    specialArgs.add(arg);
1689
16
                }
1690
56
            }
1691
1692
27
            LegalVal ordinaryVal = legalizeMakeStruct(
1693
27
                context,
1694
27
                ordinaryType,
1695
27
                ordinaryArgs.getBuffer(),
1696
27
                ordinaryArgs.getCount());
1697
1698
27
            LegalVal specialVal = legalizeMakeStruct(
1699
27
                context,
1700
27
                specialType,
1701
27
                specialArgs.getBuffer(),
1702
27
                specialArgs.getCount());
1703
1704
27
            return LegalVal::pair(ordinaryVal, specialVal, pairInfo);
1705
0
        }
1706
0
        break;
1707
1708
39
    case LegalType::Flavor::tuple:
1709
39
        {
1710
            // We are constructing a tuple of values from
1711
            // the individual fields. We need to identify
1712
            // for each tuple element what field it uses,
1713
            // and then extract that field's value.
1714
1715
39
            auto tupleType = legalType.getTuple();
1716
1717
39
            RefPtr<TuplePseudoVal> resTupleInfo = new TuplePseudoVal();
1718
39
            UInt argCounter = 0;
1719
39
            for (auto typeElem : tupleType->elements)
1720
56
            {
1721
56
                auto elemKey = typeElem.key;
1722
56
                UInt argIndex = argCounter++;
1723
56
                SLANG_ASSERT(argIndex < argCount);
1724
1725
56
                LegalVal argVal = legalArgs[argIndex];
1726
1727
56
                TuplePseudoVal::Element resElem;
1728
56
                resElem.key = elemKey;
1729
56
                resElem.val = argVal;
1730
1731
56
                resTupleInfo->elements.add(resElem);
1732
56
            }
1733
39
            return LegalVal::tuple(resTupleInfo);
1734
0
        }
1735
1736
0
    default:
1737
0
        SLANG_UNEXPECTED("unhandled");
1738
0
        UNREACHABLE_RETURN(LegalVal());
1739
5.03k
    }
1740
5.03k
}
1741
1742
static LegalVal legalizeMakeArray(
1743
    IRTypeLegalizationContext* context,
1744
    LegalType legalType,
1745
    LegalVal const* legalArgs,
1746
    UInt argCount,
1747
    IROp constructOp)
1748
28
{
1749
28
    auto builder = context->builder;
1750
1751
28
    switch (legalType.flavor)
1752
28
    {
1753
0
    case LegalType::Flavor::none:
1754
0
        return LegalVal();
1755
1756
16
    case LegalType::Flavor::simple:
1757
16
        {
1758
16
            List<IRInst*> args;
1759
            // We need a valid default val for elements that are legalized to `none`.
1760
            // We grab the first non-none value from the legalized args and use it.
1761
            // If all args are none (althoguh this shouldn't happen, since the entire array
1762
            // would have been legalized to none in this case.), we use defaultConstruct op.
1763
            // Use of defaultConstruct may lead to invalid HLSL/GLSL code, so we want to
1764
            // avoid that if possible.
1765
16
            IRInst* defaultVal = nullptr;
1766
16
            for (UInt aa = 0; aa < argCount; ++aa)
1767
16
            {
1768
16
                if (legalArgs[aa].flavor == LegalVal::Flavor::simple)
1769
16
                {
1770
16
                    defaultVal = legalArgs[aa].getSimple();
1771
16
                    break;
1772
16
                }
1773
16
            }
1774
16
            if (!defaultVal)
1775
0
            {
1776
0
                defaultVal = builder->emitDefaultConstruct(
1777
0
                    as<IRArrayTypeBase>(legalType.getSimple())->getElementType());
1778
0
            }
1779
57
            for (UInt aa = 0; aa < argCount; ++aa)
1780
41
            {
1781
41
                if (legalArgs[aa].flavor == LegalVal::Flavor::none)
1782
0
                    args.add(defaultVal);
1783
41
                else
1784
41
                    args.add(legalArgs[aa].getSimple());
1785
41
            }
1786
16
            return LegalVal::simple(builder->emitIntrinsicInst(
1787
16
                legalType.getSimple(),
1788
16
                constructOp,
1789
16
                args.getCount(),
1790
16
                args.getBuffer()));
1791
0
        }
1792
1793
2
    case LegalType::Flavor::pair:
1794
2
        {
1795
            // There are two sides, the ordinary and the special,
1796
            // and we basically just dispatch to both of them.
1797
2
            auto pairType = legalType.getPair();
1798
2
            auto pairInfo = pairType->pairInfo;
1799
2
            LegalType ordinaryType = pairType->ordinaryType;
1800
2
            LegalType specialType = pairType->specialType;
1801
1802
2
            List<LegalVal> ordinaryArgs;
1803
2
            List<LegalVal> specialArgs;
1804
2
            bool hasValidOrdinaryArgs = false;
1805
2
            bool hasValidSpecialArgs = false;
1806
7
            for (UInt argIndex = 0; argIndex < argCount; argIndex++)
1807
5
            {
1808
5
                LegalVal arg = legalArgs[argIndex];
1809
1810
                // The argument must be a pair.
1811
5
                if (arg.flavor == LegalVal::Flavor::pair)
1812
5
                {
1813
5
                    auto argPair = arg.getPair();
1814
5
                    ordinaryArgs.add(argPair->ordinaryVal);
1815
5
                    specialArgs.add(argPair->specialVal);
1816
5
                    hasValidOrdinaryArgs = true;
1817
5
                    hasValidSpecialArgs = true;
1818
5
                }
1819
0
                else if (arg.flavor == LegalVal::Flavor::simple)
1820
0
                {
1821
0
                    if (arg.getSimple()->getFullType() == ordinaryType.irType)
1822
0
                    {
1823
0
                        ordinaryArgs.add(arg);
1824
0
                        specialArgs.add(LegalVal());
1825
0
                        hasValidOrdinaryArgs = true;
1826
0
                    }
1827
0
                    else
1828
0
                    {
1829
0
                        ordinaryArgs.add(LegalVal());
1830
0
                        specialArgs.add(arg);
1831
0
                        hasValidSpecialArgs = true;
1832
0
                    }
1833
0
                }
1834
0
                else if (arg.flavor == LegalVal::Flavor::none)
1835
0
                {
1836
0
                    ordinaryArgs.add(arg);
1837
0
                    specialArgs.add(arg);
1838
0
                }
1839
0
                else
1840
0
                {
1841
0
                    SLANG_UNEXPECTED("unhandled");
1842
0
                }
1843
5
            }
1844
1845
2
            LegalVal ordinaryVal = LegalVal();
1846
2
            if (hasValidOrdinaryArgs)
1847
2
                ordinaryVal = legalizeMakeArray(
1848
2
                    context,
1849
2
                    ordinaryType,
1850
2
                    ordinaryArgs.getBuffer(),
1851
2
                    ordinaryArgs.getCount(),
1852
2
                    constructOp);
1853
1854
2
            LegalVal specialVal = LegalVal();
1855
2
            if (hasValidSpecialArgs)
1856
2
                specialVal = legalizeMakeArray(
1857
2
                    context,
1858
2
                    specialType,
1859
2
                    specialArgs.getBuffer(),
1860
2
                    specialArgs.getCount(),
1861
2
                    constructOp);
1862
1863
2
            return LegalVal::pair(ordinaryVal, specialVal, pairInfo);
1864
0
        }
1865
0
        break;
1866
1867
10
    case LegalType::Flavor::tuple:
1868
10
        {
1869
            // For array types that are legalized as tuples,
1870
            // we expect each element of the array to be legalized as the same tuples.
1871
            // We want to return a tuple, where i-th element is an array containing
1872
            // the i-th tuple-element of each legalized array-element.
1873
1874
10
            auto tupleType = legalType.getTuple();
1875
1876
10
            RefPtr<TuplePseudoVal> resTupleInfo = new TuplePseudoVal();
1877
10
            UInt elementCounter = 0;
1878
10
            for (auto typeElem : tupleType->elements)
1879
19
            {
1880
19
                auto elemKey = typeElem.key;
1881
19
                UInt elementIndex = elementCounter++;
1882
19
                List<LegalVal> subArray;
1883
67
                for (UInt i = 0; i < argCount; i++)
1884
48
                {
1885
48
                    LegalVal argVal = legalArgs[i];
1886
48
                    SLANG_RELEASE_ASSERT(argVal.flavor == LegalVal::Flavor::tuple);
1887
48
                    auto argTuple = argVal.getTuple();
1888
48
                    SLANG_RELEASE_ASSERT(
1889
48
                        argTuple->elements.getCount() == tupleType->elements.getCount());
1890
48
                    subArray.add(argTuple->elements[elementIndex].val);
1891
48
                }
1892
1893
19
                auto legalSubArray = legalizeMakeArray(
1894
19
                    context,
1895
19
                    typeElem.type,
1896
19
                    subArray.getBuffer(),
1897
19
                    subArray.getCount(),
1898
19
                    constructOp);
1899
1900
19
                TuplePseudoVal::Element resElem;
1901
19
                resElem.key = elemKey;
1902
19
                resElem.val = legalSubArray;
1903
19
                resTupleInfo->elements.add(resElem);
1904
19
            }
1905
10
            return LegalVal::tuple(resTupleInfo);
1906
0
        }
1907
1908
0
    default:
1909
0
        SLANG_UNEXPECTED("unhandled");
1910
0
        UNREACHABLE_RETURN(LegalVal());
1911
28
    }
1912
28
}
1913
1914
static LegalVal legalizeDefaultConstruct(IRTypeLegalizationContext* context, LegalType legalType)
1915
0
{
1916
0
    auto builder = context->builder;
1917
1918
0
    switch (legalType.flavor)
1919
0
    {
1920
0
    case LegalType::Flavor::none:
1921
0
        return LegalVal();
1922
1923
0
    case LegalType::Flavor::simple:
1924
0
        {
1925
0
            return LegalVal::simple(builder->emitDefaultConstruct(legalType.getSimple()));
1926
0
        }
1927
1928
0
    case LegalType::Flavor::pair:
1929
0
        {
1930
0
            auto pairType = legalType.getPair();
1931
0
            auto pairInfo = pairType->pairInfo;
1932
0
            LegalType ordinaryType = pairType->ordinaryType;
1933
0
            LegalType specialType = pairType->specialType;
1934
1935
0
            LegalVal ordinaryVal = legalizeDefaultConstruct(context, ordinaryType);
1936
1937
0
            LegalVal specialVal = legalizeDefaultConstruct(context, specialType);
1938
1939
0
            return LegalVal::pair(ordinaryVal, specialVal, pairInfo);
1940
0
        }
1941
0
        break;
1942
1943
0
    case LegalType::Flavor::tuple:
1944
0
        {
1945
0
            auto tupleType = legalType.getTuple();
1946
1947
0
            RefPtr<TuplePseudoVal> resTupleInfo = new TuplePseudoVal();
1948
0
            for (auto typeElem : tupleType->elements)
1949
0
            {
1950
0
                auto elemKey = typeElem.key;
1951
0
                TuplePseudoVal::Element resElem;
1952
0
                resElem.key = elemKey;
1953
0
                resElem.val = legalizeDefaultConstruct(context, typeElem.type);
1954
0
                resTupleInfo->elements.add(resElem);
1955
0
            }
1956
0
            return LegalVal::tuple(resTupleInfo);
1957
0
        }
1958
1959
0
    default:
1960
0
        SLANG_UNEXPECTED("unhandled");
1961
0
        UNREACHABLE_RETURN(LegalVal());
1962
0
    }
1963
0
}
1964
1965
static LegalVal legalizeUndefined(IRTypeLegalizationContext* context, IRInst* inst)
1966
1.44k
{
1967
    // HACK: We check here if an undefined value we are trying to legalize is
1968
    // of an opaque type (such as a texture, buffer, or sampler), and issue
1969
    // a diagnostic (since there is really no way to make such code compile
1970
    // correctly for may of our downstream compilers).
1971
    //
1972
    // TODO(tfoley): This is emphatically *not* the right place for a diagnostic
1973
    // like this to be issued. We need rigorous checking for use of undefined
1974
    // values in the front-end. Once the front-end's checking can be trusted,
1975
    // the back-end should be able to remove any code that appears to use
1976
    // an uninitialized value of opaque type, since such code would have undefined
1977
    // behavior anyway.
1978
    //
1979
1.44k
    IRType* opaqueType = nullptr;
1980
1.44k
    if (isOpaqueType(inst->getFullType(), &opaqueType))
1981
1
    {
1982
1
        SourceLoc loc = findBestSourceLocFromUses(inst);
1983
1984
1
        if (!loc.isValid())
1985
0
            loc = getDiagnosticPos(opaqueType);
1986
1987
1
        context->m_sink->diagnose(
1988
1
            Diagnostics::UseOfUninitializedOpaqueHandle{.handleType = opaqueType, .location = loc});
1989
1
    }
1990
1991
    // It is not ideal, but this pass legalizes an undefined value to... nothing.
1992
    //
1993
    // As a result, in any context that tries to consume a `LegalVal` based on its type,
1994
    // we need to be prepared not only for a value that matches the structure of that type,
1995
    // but also the possibility of an empty value like this.
1996
    //
1997
    // TODO(tfoley): We should *either* have a distinct case of `LegalVal` to represent
1998
    // undefined-ness, or this code should follow the same flow as the other cases by
1999
    // recursively decomposing the structure of the instruction's type and building up
2000
    // a `LegalVal` that just happens to have undefined values at its leaves.
2001
    //
2002
1.44k
    return LegalVal();
2003
1.44k
}
2004
2005
static LegalVal legalizeInst(
2006
    IRTypeLegalizationContext* context,
2007
    IRInst* inst,
2008
    LegalType type,
2009
    ArrayView<LegalVal> args)
2010
21.9k
{
2011
21.9k
    LegalVal result = LegalVal();
2012
21.9k
    switch (inst->getOp())
2013
21.9k
    {
2014
712
    case kIROp_Load:
2015
712
        result = legalizeLoad(context, args[0]);
2016
712
        break;
2017
2018
0
    case kIROp_GetValueFromBoundInterface:
2019
0
        result = args[0];
2020
0
        break;
2021
2022
2.08k
    case kIROp_FieldAddress:
2023
2.08k
        result = legalizeFieldAddress(context, type, args[0], args[1]);
2024
2.08k
        break;
2025
2026
2.81k
    case kIROp_FieldExtract:
2027
2.81k
        result = legalizeFieldExtract(context, type, args[0], args[1]);
2028
2.81k
        break;
2029
2030
22
    case kIROp_GetElement:
2031
22
        result = legalizeGetElement(context, type, args[0], args[1]);
2032
22
        break;
2033
2034
4
    case kIROp_GetElementPtr:
2035
4
        result = legalizeGetElementPtr(context, type, args[0], args[1]);
2036
4
        break;
2037
2038
1.65k
    case kIROp_Store:
2039
1.65k
        result = legalizeStore(context, args[0], args[1]);
2040
1.65k
        break;
2041
2042
5.00k
    case kIROp_Call:
2043
5.00k
        result = legalizeCall(context, (IRCall*)inst);
2044
5.00k
        break;
2045
1.50k
    case kIROp_Return:
2046
1.50k
        result = legalizeRetVal(context, args[0], (IRReturn*)inst);
2047
1.50k
        break;
2048
0
    case kIROp_CastDescriptorHandleToResource:
2049
0
        result = LegalVal::simple(inst);
2050
0
        break;
2051
912
    case kIROp_DebugVar:
2052
912
        result = legalizeDebugVar(context, type, (IRDebugVar*)inst);
2053
912
        if (result.flavor == LegalVal::Flavor::none)
2054
912
        {
2055
912
            return result;
2056
912
        }
2057
0
        break;
2058
794
    case kIROp_DebugValue:
2059
794
        result = legalizeDebugValue(context, args[0], args[1], (IRDebugValue*)inst);
2060
794
        break;
2061
2062
4.97k
    case kIROp_MakeStruct:
2063
4.97k
        result = legalizeMakeStruct(context, type, args.getBuffer(), inst->getOperandCount());
2064
4.97k
        break;
2065
5
    case kIROp_MakeArray:
2066
5
    case kIROp_MakeArrayFromElement:
2067
5
        result = legalizeMakeArray(
2068
5
            context,
2069
5
            type,
2070
5
            args.getBuffer(),
2071
5
            inst->getOperandCount(),
2072
5
            inst->getOp());
2073
5
        break;
2074
0
    case kIROp_DefaultConstruct:
2075
0
        result = legalizeDefaultConstruct(context, type);
2076
0
        break;
2077
34
    case kIROp_UnconditionalBranch:
2078
34
    case kIROp_Loop:
2079
34
        result = legalizeUnconditionalBranch(context, args, (IRUnconditionalBranch*)inst);
2080
34
        break;
2081
13
    case kIROp_Printf:
2082
13
        result = legalizePrintf(context, args);
2083
13
        break;
2084
1.44k
    case kIROp_LoadFromUninitializedMemory:
2085
1.44k
    case kIROp_Poison:
2086
1.44k
        return legalizeUndefined(context, inst);
2087
0
    case kIROp_GpuForeach:
2088
        // This case should only happen when compiling for a target that does not support
2089
        // GpuForeach
2090
0
        return LegalVal();
2091
0
    case kIROp_StructuredBufferLoad:
2092
        // empty types are removed, so we need to make sure that we're still
2093
        // loading a none type when we try and load from a to-be-optimized
2094
        // out structured buffer
2095
0
        SLANG_ASSERT(type.flavor == LegalType::Flavor::none);
2096
0
        return LegalVal();
2097
0
    default:
2098
0
        if (type.flavor == LegalType::Flavor::none)
2099
0
        {
2100
            // If the result type of the instruction is `none`, then we can
2101
            // just legalize to `none` without worrying about the details of
2102
            // the instruction, since there will be no value to produce.
2103
0
            return LegalVal();
2104
0
        }
2105
        // TODO: produce a user-visible diagnostic here
2106
0
        SLANG_UNEXPECTED("non-simple operand(s)!");
2107
0
        break;
2108
21.9k
    }
2109
19.6k
    return result;
2110
21.9k
}
2111
2112
static UnownedStringSlice findNameHint(IRInst* inst)
2113
4.26k
{
2114
4.26k
    if (auto nameHintDecoration = inst->findDecoration<IRNameHintDecoration>())
2115
1.75k
    {
2116
1.75k
        return nameHintDecoration->getName();
2117
1.75k
    }
2118
2.51k
    return UnownedStringSlice();
2119
4.26k
}
2120
2121
static LegalVal legalizeLocalVar(IRTypeLegalizationContext* context, IRVar* irLocalVar)
2122
88.0k
{
2123
    // Legalize the type for the variable's value
2124
88.0k
    auto originalValueType = irLocalVar->getDataType()->getValueType();
2125
88.0k
    auto legalValueType = legalizeType(context, originalValueType);
2126
2127
88.0k
    auto originalRate = irLocalVar->getRate();
2128
2129
88.0k
    IRVarLayout* varLayout = findVarLayout(irLocalVar);
2130
88.0k
    IRTypeLayout* typeLayout = varLayout ? varLayout->getTypeLayout() : nullptr;
2131
2132
    // If we've decided to do implicit deref on the type,
2133
    // then go ahead and declare a value of the pointed-to type.
2134
88.0k
    LegalType maybeSimpleType = legalValueType;
2135
88.0k
    while (maybeSimpleType.flavor == LegalType::Flavor::implicitDeref)
2136
0
    {
2137
0
        maybeSimpleType = maybeSimpleType.getImplicitDeref()->valueType;
2138
0
    }
2139
2140
88.0k
    switch (maybeSimpleType.flavor)
2141
88.0k
    {
2142
87.6k
    case LegalType::Flavor::simple:
2143
87.6k
        {
2144
            // Easy case: the type is usable as-is, and we
2145
            // should just do that.
2146
87.6k
            auto type = maybeSimpleType.getSimple();
2147
87.6k
            type = context->builder->getPtrTypeWithAddressSpace(type, irLocalVar->getDataType());
2148
87.6k
            if (originalRate)
2149
0
            {
2150
0
                type = context->builder->getRateQualifiedType(originalRate, type);
2151
0
            }
2152
87.6k
            irLocalVar->setFullType(type);
2153
87.6k
            return LegalVal::simple(irLocalVar);
2154
0
        }
2155
2156
372
    default:
2157
372
        {
2158
            // TODO: We don't handle rates in this path.
2159
2160
372
            context->insertBeforeLocalVar = irLocalVar;
2161
2162
372
            LegalVarChainLink varChain(LegalVarChain(), varLayout);
2163
2164
372
            UnownedStringSlice nameHint = findNameHint(irLocalVar);
2165
372
            context->builder->setInsertBefore(irLocalVar);
2166
372
            LegalVal newVal = declareVars(
2167
372
                context,
2168
372
                kIROp_Var,
2169
372
                legalValueType,
2170
372
                typeLayout,
2171
372
                varChain,
2172
372
                nameHint,
2173
372
                irLocalVar,
2174
372
                nullptr,
2175
372
                context->isSpecialType(originalValueType));
2176
2177
            // Remove the old local var.
2178
372
            irLocalVar->removeFromParent();
2179
            // add old local var to list
2180
372
            context->replacedInstructions.add(irLocalVar);
2181
372
            return newVal;
2182
0
        }
2183
88.0k
    }
2184
88.0k
    UNREACHABLE_RETURN(LegalVal());
2185
0
}
2186
2187
static LegalVal legalizeParam(IRTypeLegalizationContext* context, IRParam* originalParam)
2188
432k
{
2189
432k
    auto legalParamType = legalizeType(context, originalParam->getFullType());
2190
432k
    if (legalParamType.flavor == LegalType::Flavor::simple)
2191
428k
    {
2192
        // Simple case: things were legalized to a simple type,
2193
        // so we can just use the original parameter as-is.
2194
428k
        originalParam->setFullType(legalParamType.getSimple());
2195
428k
        return LegalVal::simple(originalParam);
2196
428k
    }
2197
3.68k
    else
2198
3.68k
    {
2199
        // Complex case: we need to insert zero or more new parameters,
2200
        // which will replace the old ones.
2201
2202
3.68k
        context->insertBeforeParam = originalParam;
2203
2204
3.68k
        UnownedStringSlice nameHint = findNameHint(originalParam);
2205
2206
3.68k
        context->builder->setInsertBefore(originalParam);
2207
3.68k
        auto newVal = declareVars(
2208
3.68k
            context,
2209
3.68k
            kIROp_Param,
2210
3.68k
            legalParamType,
2211
3.68k
            nullptr,
2212
3.68k
            LegalVarChain(),
2213
3.68k
            nameHint,
2214
3.68k
            originalParam,
2215
3.68k
            nullptr,
2216
3.68k
            context->isSpecialType(originalParam->getDataType()));
2217
2218
3.68k
        originalParam->removeFromParent();
2219
3.68k
        context->replacedInstructions.add(originalParam);
2220
3.68k
        return newVal;
2221
3.68k
    }
2222
432k
}
2223
2224
static LegalVal legalizeFunc(IRTypeLegalizationContext* context, IRFunc* irFunc);
2225
2226
static LegalVal legalizeGlobalVar(IRTypeLegalizationContext* context, IRGlobalVar* irGlobalVar);
2227
2228
static LegalVal legalizeGlobalParam(
2229
    IRTypeLegalizationContext* context,
2230
    IRGlobalParam* irGlobalParam);
2231
2232
static LegalVal legalizeCoopMatMapElementIFunc(
2233
    IRTypeLegalizationContext* context,
2234
    IRCoopMatMapElementIFunc* inst)
2235
33
{
2236
    // When the functor object legalizes to something different, we need to
2237
    // update the MapElementIFunc inst to reflect its new form.
2238
33
    if (inst->hasIFuncThis())
2239
17
    {
2240
17
        auto legalArg = legalizeOperand(context, inst->getIFuncThis());
2241
17
        switch (legalArg.flavor)
2242
17
        {
2243
13
        case LegalVal::Flavor::simple:
2244
13
            {
2245
                // If the functor legalizes to a simple value,
2246
                // we just need to update the argument list of the mapElement inst.
2247
13
                if (legalArg.getSimple() != inst->getIFuncThis())
2248
0
                {
2249
0
                    IRBuilder builder{inst};
2250
0
                    builder.setInsertBefore(inst);
2251
0
                    auto newInst = builder.emitCoopMatMapElementFunc(
2252
0
                        inst->getFullType(),
2253
0
                        legalArg.getSimple(),
2254
0
                        inst->getOperand(1));
2255
0
                    inst->replaceUsesWith(newInst);
2256
0
                    inst->removeAndDeallocate();
2257
0
                    return LegalVal::simple(newInst);
2258
0
                }
2259
13
                break;
2260
13
            }
2261
13
        case LegalVal::Flavor::none:
2262
3
            {
2263
                // If the functor is a lambda with no captures,
2264
                // it will be removed as a part of the legalization process.
2265
                // We need to explicitly remove it from the argument list of the mapElement inst.
2266
3
                IRBuilder builder{inst};
2267
3
                builder.setInsertBefore(inst);
2268
3
                auto newInst = builder.emitCoopMatMapElementFunc(
2269
3
                    inst->getFullType(),
2270
3
                    inst->getOperand(0),
2271
3
                    inst->getOperand(1));
2272
2273
3
                inst->replaceUsesWith(newInst);
2274
3
                inst->removeAndDeallocate();
2275
3
                return LegalVal::simple(newInst);
2276
13
            }
2277
0
        case LegalVal::Flavor::pair:
2278
1
        case LegalVal::Flavor::tuple:
2279
1
            {
2280
                // If functor legalizes to one or many special (resource) values, we
2281
                // can't handle this case very easily at the moment, so diagnose an error
2282
                // instead of crashing.
2283
1
                context->m_sink->diagnose(Diagnostics::CooperativeMatrixUnsupportedCapture{
2284
1
                    .location = inst->getIFuncCall()->sourceLoc});
2285
1
                return LegalVal();
2286
0
            }
2287
17
        }
2288
17
    }
2289
29
    return LegalVal::simple(inst);
2290
33
}
2291
2292
static LegalVal legalizeInst(IRTypeLegalizationContext* context, IRInst* inst)
2293
41.7M
{
2294
41.7M
    LegalVal legalVal;
2295
41.7M
    if (context->mapValToLegalVal.tryGetValue(inst, legalVal))
2296
25.4M
        return legalVal;
2297
2298
    // Any additional instructions we need to emit
2299
    // in the process of legalizing `inst` should
2300
    // by default be insertied right before `inst`.
2301
    //
2302
16.3M
    context->builder->setInsertBefore(inst);
2303
2304
    // Special-case certain operations
2305
16.3M
    switch (inst->getOp())
2306
16.3M
    {
2307
33
    case kIROp_CoopMatMapElementIFunc:
2308
33
        return legalizeCoopMatMapElementIFunc(context, cast<IRCoopMatMapElementIFunc>(inst));
2309
2310
88.0k
    case kIROp_Var:
2311
88.0k
        return legalizeLocalVar(context, cast<IRVar>(inst));
2312
2313
432k
    case kIROp_Param:
2314
432k
        return legalizeParam(context, cast<IRParam>(inst));
2315
2316
33.9k
    case kIROp_WitnessTable:
2317
        // Just skip these.
2318
33.9k
        break;
2319
2320
199k
    case kIROp_Func:
2321
199k
        return legalizeFunc(context, cast<IRFunc>(inst));
2322
2323
2.05k
    case kIROp_GlobalVar:
2324
2.05k
        return legalizeGlobalVar(context, cast<IRGlobalVar>(inst));
2325
2326
45.4k
    case kIROp_GlobalParam:
2327
45.4k
        return legalizeGlobalParam(context, cast<IRGlobalParam>(inst));
2328
2329
750k
    case kIROp_Block:
2330
750k
        return LegalVal::simple(inst);
2331
2332
14.7M
    default:
2333
14.7M
        break;
2334
16.3M
    }
2335
2336
14.7M
    if (as<IRAttr>(inst))
2337
240k
        return LegalVal::simple(inst);
2338
2339
2340
    // We will iterate over all the operands, extract the legalized
2341
    // value of each, and collect them in an array for subsequent use.
2342
    //
2343
14.5M
    auto argCount = inst->getOperandCount();
2344
14.5M
    ShortList<LegalVal> legalArgs;
2345
    //
2346
    // Along the way we will also note whether there were any operands
2347
    // with non-simple legalized values.
2348
    //
2349
14.5M
    bool anyComplex = false;
2350
46.4M
    for (UInt aa = 0; aa < argCount; ++aa)
2351
31.9M
    {
2352
31.9M
        auto oldArg = inst->getOperand(aa);
2353
2354
31.9M
        LegalVal legalArg = legalizeOperand(context, oldArg);
2355
31.9M
        legalArgs.add(legalArg);
2356
2357
31.9M
        if (legalArg.flavor != LegalVal::Flavor::simple)
2358
16.7k
            anyComplex = true;
2359
31.9M
    }
2360
2361
    // We must also legalize the type of the instruction, since that
2362
    // is implicitly one of its operands.
2363
    //
2364
14.5M
    LegalType legalType = legalizeType(context, inst->getFullType());
2365
2366
    // If there was nothing interesting that occured for the operands
2367
    // then we can re-use this instruction as-is.
2368
    //
2369
14.5M
    if (!anyComplex && legalType.flavor == LegalType::Flavor::simple)
2370
14.5M
    {
2371
        // While the operands are all "simple," they might not necessarily
2372
        // be equal to the operands we started with.
2373
        //
2374
14.5M
        ShortList<IRInst*> newArgs;
2375
14.5M
        newArgs.setCount(argCount);
2376
14.5M
        bool recreate = false;
2377
46.4M
        for (UInt aa = 0; aa < argCount; ++aa)
2378
31.8M
        {
2379
31.8M
            auto legalArg = legalArgs[aa];
2380
31.8M
            newArgs[aa] = legalArg.getSimple();
2381
31.8M
            if (newArgs[aa] != inst->getOperand(aa))
2382
4.55k
                recreate = true;
2383
31.8M
        }
2384
14.5M
        if (inst->getFullType() != legalType.getSimple())
2385
4.83k
            recreate = true;
2386
14.5M
        if (recreate)
2387
9.37k
        {
2388
9.37k
            IRBuilder builder(inst->getModule());
2389
9.37k
            builder.setInsertBefore(inst);
2390
9.37k
            auto newInst = builder.emitIntrinsicInst(
2391
9.37k
                legalType.getSimple(),
2392
9.37k
                inst->getOp(),
2393
9.37k
                argCount,
2394
9.37k
                newArgs.getArrayView().getBuffer());
2395
9.37k
            inst->replaceUsesWith(newInst);
2396
9.37k
            inst->removeFromParent();
2397
9.37k
            context->replacedInstructions.add(inst);
2398
9.37k
            for (auto child : inst->getDecorationsAndChildren())
2399
578
            {
2400
578
                child->insertAtEnd(newInst);
2401
578
            }
2402
9.37k
            return LegalVal::simple(newInst);
2403
9.37k
        }
2404
14.5M
        return LegalVal::simple(inst);
2405
14.5M
    }
2406
2407
    // We have at least one "complex" operand, and we
2408
    // need to figure out what to do with it. The anwer
2409
    // will, in general, depend on what we are doing.
2410
2411
    // We will set up the IR builder so that any new
2412
    // instructions generated will be placed before
2413
    // the location of the original instruction.
2414
16.5k
    auto builder = context->builder;
2415
16.5k
    builder->setInsertBefore(inst);
2416
2417
16.5k
    legalVal = legalizeInst(context, inst, legalType, legalArgs.getArrayView().arrayView);
2418
2419
16.5k
    if (legalVal.flavor == LegalVal::Flavor::simple)
2420
6.39k
    {
2421
6.39k
        inst->replaceUsesWith(legalVal.getSimple());
2422
6.39k
    }
2423
    // After we are done, we will eliminate the
2424
    // original instruction by removing it from
2425
    // the IR.
2426
    //
2427
16.5k
    inst->removeFromParent();
2428
16.5k
    context->replacedInstructions.add(inst);
2429
2430
    // The value to be used when referencing
2431
    // the original instruction will now be
2432
    // whatever value(s) we created to replace it.
2433
16.5k
    return legalVal;
2434
14.5M
}
2435
2436
/// Helper type for legalizing the signature of an `IRFunc`
2437
struct LegalFuncBuilder
2438
{
2439
    LegalFuncBuilder(IRTypeLegalizationContext* context)
2440
199k
        : m_context(context)
2441
199k
    {
2442
199k
    }
2443
2444
    /// Construct a legalized value to represent `oldFunc`
2445
    LegalVal build(IRFunc* oldFunc)
2446
199k
    {
2447
        // We can start by computing what the type signature of the
2448
        // legalized function should be, based on the type signature
2449
        // of the original.
2450
        //
2451
199k
        IRFuncType* oldFuncType = oldFunc->getDataType();
2452
2453
        // Each parameter of the original function will translate into
2454
        // zero or more parameters in the legalized function signature.
2455
        //
2456
199k
        UInt oldParamCount = oldFuncType->getParamCount();
2457
514k
        for (UInt pp = 0; pp < oldParamCount; ++pp)
2458
315k
        {
2459
315k
            auto legalParamType = legalizeType(m_context, oldFuncType->getParamType(pp));
2460
315k
            _addParam(legalParamType);
2461
315k
        }
2462
2463
        // We will record how many parameters resulted from
2464
        // legalization of the original / "base" parameter list.
2465
        // This number will help us in computing how many parameters
2466
        // were added to capture the result type of the function.
2467
        //
2468
199k
        Index baseLegalParamCount = m_paramTypes.getCount();
2469
2470
        // Next we add a result type to the function based on the
2471
        // legalized result type of the original function.
2472
        //
2473
        // It is possible that this process will had one or more
2474
        // `out` parameters to represent parts of the result type
2475
        // that couldn't be passed via the ordinary function result.
2476
        //
2477
199k
        auto legalResultType = legalizeType(m_context, oldFuncType->getResultType());
2478
199k
        _addResult(legalResultType);
2479
2480
        // If any part of the result type required new function parameters
2481
        // to be introduced, then we want to know how many there were.
2482
        // These additional function paameters will always come after the original
2483
        // parameters, so that they don't shift around call sites too much.
2484
        //
2485
        // TODO: Where we put the added `out` parameters in the signature may
2486
        // have performance implications when it starts interacting with ABI
2487
        // (e.g., most ABIs assign parameters to registers from left to right,
2488
        // so parameters later in the list are more likely to be passed through
2489
        // memory; we'd need to decide whether the base parameters or the
2490
        // legalized result parameters should be prioritized for register
2491
        // allocation).
2492
        //
2493
199k
        Index resultParamCount = m_paramTypes.getCount() - baseLegalParamCount;
2494
2495
        // If we didn't bottom out on a result type for the legalized function,
2496
        // then we should default to returning `void`.
2497
        //
2498
199k
        auto irBuilder = m_context->builder;
2499
199k
        if (!m_resultType)
2500
1.37k
        {
2501
1.37k
            m_resultType = irBuilder->getVoidType();
2502
1.37k
        }
2503
2504
        // We will compute the new IR type for the function and install it
2505
        // as the data type of original function.
2506
        //
2507
        // Note: This is one of the few cases where the legalization pass
2508
        // prefers to modify an IR node in-place rather than create a distinct
2509
        // legalized copy of it.
2510
        //
2511
199k
        irBuilder->setInsertBefore(oldFunc);
2512
199k
        auto newFuncType =
2513
199k
            irBuilder->getFuncType(m_paramTypes.getCount(), m_paramTypes.getBuffer(), m_resultType);
2514
199k
        irBuilder->setDataType(oldFunc, newFuncType);
2515
2516
        // If the function required any new parameters to be created
2517
        // to represent the result/return type, then we need to
2518
        // actually add the appropriate IR parameters to represent
2519
        // that stuff as well.
2520
        //
2521
199k
        if (resultParamCount != 0)
2522
62
        {
2523
            // Only a function with a body will need this additonal
2524
            // step, since the function parameters are stored on the
2525
            // first block of the body.
2526
            //
2527
62
            auto firstBlock = oldFunc->getFirstBlock();
2528
62
            if (firstBlock)
2529
62
            {
2530
                // Because legalization of this function required us
2531
                // to introduce new parameters, we need to allocate
2532
                // a data structure to record the identities of those
2533
                // new parameters so that they can be looked up when
2534
                // legalizing the body of the function.
2535
                //
2536
                // In particular, we will use this information when
2537
                // legalizing `return` instructions in the function body,
2538
                // since those will need to store at least part of
2539
                // the reuslt value into the newly-declared parameter(s).
2540
                //
2541
62
                RefPtr<LegalFuncInfo> funcInfo = new LegalFuncInfo();
2542
62
                m_context->mapFuncToInfo.add(oldFunc, funcInfo);
2543
2544
                // We know that our new parameters need to come after
2545
                // those that were declared for the "base" parameters
2546
                // of the original function.
2547
                //
2548
62
                auto firstResultParamIndex = baseLegalParamCount;
2549
62
                auto firstOrdinaryInst = firstBlock->getFirstOrdinaryInst();
2550
156
                for (Index i = 0; i < resultParamCount; ++i)
2551
94
                {
2552
                    // Note: The parameter types that were added to
2553
                    // the `m_paramTypes` array already account for the
2554
                    // fact that these are `out` parameters, since that
2555
                    // impacts the function type signature as well.
2556
                    // We do *not* need to wrap `paramType` in an `Out<...>`
2557
                    // type here.
2558
                    //
2559
94
                    auto paramType = m_paramTypes[firstResultParamIndex + i];
2560
94
                    auto param = irBuilder->createParam(paramType);
2561
94
                    param->insertBefore(firstOrdinaryInst);
2562
2563
94
                    funcInfo->resultParamVals.add(param);
2564
94
                }
2565
62
            }
2566
62
        }
2567
2568
        // Note: at this point we do *not* apply legalization to the parameters
2569
        // of the function or its body; those are left for the recursive part
2570
        // of the overall legalization pass to handle.
2571
2572
199k
        return LegalVal::simple(oldFunc);
2573
199k
    }
2574
2575
2576
private:
2577
    IRTypeLegalizationContext* m_context = nullptr;
2578
    ;
2579
2580
    /// The types of the parameters of the legalized function
2581
    List<IRType*> m_paramTypes;
2582
2583
    /// The result type of the legalized function (can be null to represent `void`)
2584
    IRType* m_resultType = nullptr;
2585
2586
    /// Add a parameter of type `t` to the function signature
2587
    void _addParam(LegalType t)
2588
315k
    {
2589
        // This logic is a simple recursion over the structure of `t`,
2590
        // with the leaf case adding parameters of simple IR type.
2591
2592
315k
        switch (t.flavor)
2593
315k
        {
2594
3.44k
        case LegalType::Flavor::none:
2595
3.44k
            break;
2596
2597
311k
        case LegalType::Flavor::simple:
2598
311k
            m_paramTypes.add(t.getSimple());
2599
311k
            break;
2600
2601
2
        case LegalType::Flavor::implicitDeref:
2602
2
            {
2603
2
                auto imp = t.getImplicitDeref();
2604
2
                _addParam(imp->valueType);
2605
2
            }
2606
2
            break;
2607
119
        case LegalType::Flavor::pair:
2608
119
            {
2609
119
                auto pairInfo = t.getPair();
2610
119
                _addParam(pairInfo->ordinaryType);
2611
119
                _addParam(pairInfo->specialType);
2612
119
            }
2613
119
            break;
2614
298
        case LegalType::Flavor::tuple:
2615
298
            {
2616
298
                auto tup = t.getTuple();
2617
298
                for (auto& elem : tup->elements)
2618
441
                    _addParam(elem.type);
2619
298
            }
2620
298
            break;
2621
0
        default:
2622
0
            SLANG_UNEXPECTED("unknown legalized type flavor");
2623
315k
        }
2624
315k
    }
2625
2626
    /// Set the logical result type of the legalized function to `t`
2627
    void _addResult(LegalType t)
2628
199k
    {
2629
199k
        switch (t.flavor)
2630
199k
        {
2631
197k
        case LegalType::Flavor::simple:
2632
            // The simple case is when the result type is a simple IR
2633
            // type, and we can use it directly as the return type.
2634
            //
2635
197k
            m_resultType = t.getSimple();
2636
197k
            break;
2637
2638
2639
1.34k
        case LegalType::Flavor::none:
2640
            // The case where we have no result type is also simple,
2641
            // becaues we can leave `m_resultType` as null to represent
2642
            // a `void` result type.
2643
1.34k
            break;
2644
2645
0
        case LegalType::Flavor::implicitDeref:
2646
0
            {
2647
                // An `implicitDeref` is a wrapper around another legal
2648
                // type, so we can simply set the result type to the
2649
                // unwrapped inner type.
2650
                //
2651
0
                auto imp = t.getImplicitDeref();
2652
0
                _addResult(imp->valueType);
2653
0
            }
2654
0
            break;
2655
2656
31
        case LegalType::Flavor::pair:
2657
31
            {
2658
                // The `pair` case is the first interesting one.
2659
                //
2660
                // We will set the actual result type of the operation
2661
                // to the ordinary side of the pair, while any special
2662
                // part of the pair will be returned via fresh `out`
2663
                // parameters insteqad.
2664
                //
2665
31
                auto pairInfo = t.getPair();
2666
31
                _addResult(pairInfo->ordinaryType);
2667
31
                _addOutParam(pairInfo->specialType);
2668
31
            }
2669
31
            break;
2670
2671
31
        case LegalType::Flavor::tuple:
2672
31
            {
2673
                // In the `tuple` case we have zero or more types,
2674
                // and there is no distinguished primary one that
2675
                // should become the result type of the legalized function.
2676
                //
2677
                // We will instead declare fresh `out` parameters to
2678
                // capture all the outputs in the tuple.
2679
                //
2680
31
                auto tup = t.getTuple();
2681
31
                for (auto& elem : tup->elements)
2682
40
                {
2683
40
                    _addOutParam(elem.type);
2684
40
                }
2685
31
            }
2686
31
            break;
2687
2688
0
        default:
2689
0
            SLANG_UNEXPECTED("unknown legalized type flavor");
2690
199k
        }
2691
199k
    }
2692
2693
    /// Add a single `out` parameter based on type `t`.
2694
    void _addOutParam(LegalType t)
2695
138
    {
2696
138
        switch (t.flavor)
2697
138
        {
2698
94
        case LegalType::Flavor::simple:
2699
            // The simple case here is almost the same as `_addParam()`,
2700
            // except we wrap the simple type in `Out<...>` to indicate
2701
            // that we are producing an `out` parameter.
2702
            //
2703
94
            m_paramTypes.add(m_context->builder->getOutParamType(t.getSimple()));
2704
94
            break;
2705
2706
            // The remaining cases are all simple recursion on the
2707
            // structure of `t`.
2708
2709
0
        case LegalType::Flavor::none:
2710
0
            break;
2711
2712
0
        case LegalType::Flavor::implicitDeref:
2713
0
            {
2714
0
                auto imp = t.getImplicitDeref();
2715
0
                _addOutParam(imp->valueType);
2716
0
            }
2717
0
            break;
2718
0
        case LegalType::Flavor::pair:
2719
0
            {
2720
0
                auto pairInfo = t.getPair();
2721
0
                _addOutParam(pairInfo->ordinaryType);
2722
0
                _addOutParam(pairInfo->specialType);
2723
0
            }
2724
0
            break;
2725
44
        case LegalType::Flavor::tuple:
2726
44
            {
2727
44
                auto tup = t.getTuple();
2728
44
                for (auto& elem : tup->elements)
2729
67
                {
2730
67
                    _addOutParam(elem.type);
2731
67
                }
2732
44
            }
2733
44
            break;
2734
0
        default:
2735
0
            SLANG_UNEXPECTED("unknown legalized type flavor");
2736
138
        }
2737
138
    }
2738
};
2739
2740
static LegalVal legalizeFunc(IRTypeLegalizationContext* context, IRFunc* irFunc)
2741
199k
{
2742
199k
    LegalFuncBuilder builder(context);
2743
199k
    auto legalVal = builder.build(irFunc);
2744
199k
    registerLegalizedValue(context, irFunc, legalVal);
2745
199k
    return legalVal;
2746
199k
}
2747
2748
static void cloneDecorationToVar(IRInst* srcInst, IRInst* varInst)
2749
1.97k
{
2750
1.97k
    for (auto decoration : srcInst->getDecorations())
2751
2.29k
    {
2752
2.29k
        switch (decoration->getOp())
2753
2.29k
        {
2754
2
        case kIROp_FormatDecoration:
2755
4
        case kIROp_UserTypeNameDecoration:
2756
6
        case kIROp_SemanticDecoration:
2757
6
        case kIROp_MemoryQualifierSetDecoration:
2758
6
            cloneDecoration(decoration, varInst);
2759
6
            break;
2760
2761
2.28k
        default:
2762
2.28k
            break;
2763
2.29k
        }
2764
2.29k
    }
2765
1.97k
}
2766
2767
static LegalVal declareSimpleVar(
2768
    IRTypeLegalizationContext* context,
2769
    IROp op,
2770
    IRType* type,
2771
    IRTypeLayout* typeLayout,
2772
    LegalVarChain const& varChain,
2773
    UnownedStringSlice nameHint,
2774
    IRInst* leafVar,
2775
    IRGlobalParamInfo* globalParamInfo)
2776
1.08k
{
2777
1.08k
    IRVarLayout* varLayout = createVarLayout(context->builder, varChain, typeLayout);
2778
2779
1.08k
    IRBuilder* builder = context->builder;
2780
2781
1.08k
    IRInst* irVar = nullptr;
2782
1.08k
    LegalVal legalVarVal;
2783
2784
1.08k
    switch (op)
2785
1.08k
    {
2786
0
    case kIROp_GlobalVar:
2787
0
        {
2788
0
            auto globalVar = builder->createGlobalVar(type);
2789
0
            globalVar->removeFromParent();
2790
0
            globalVar->insertBefore(context->insertBeforeGlobal);
2791
2792
0
            irVar = globalVar;
2793
0
            legalVarVal = LegalVal::simple(irVar);
2794
0
        }
2795
0
        break;
2796
2797
410
    case kIROp_GlobalParam:
2798
410
        {
2799
410
            auto globalParam = builder->createGlobalParam(type);
2800
410
            globalParam->removeFromParent();
2801
410
            globalParam->insertBefore(context->insertBeforeGlobal);
2802
2803
            // Add originating entry point decoration if original global param
2804
            // comes from an entry point parameter. This is required in cases where the global
2805
            // param has to be linked back to the originating entry point, such as when
2806
            // emitting Metal where there global params have to be moved back to the
2807
            // entry point parameter.
2808
410
            SLANG_ASSERT(globalParamInfo);
2809
410
            if (globalParamInfo->originatingEntryPoint)
2810
105
            {
2811
105
                builder->addEntryPointParamDecoration(
2812
105
                    globalParam,
2813
105
                    globalParamInfo->originatingEntryPoint);
2814
105
            }
2815
2816
410
            irVar = globalParam;
2817
410
            legalVarVal = LegalVal::simple(globalParam);
2818
410
        }
2819
410
        break;
2820
2821
188
    case kIROp_Var:
2822
188
        {
2823
188
            builder->setInsertBefore(context->insertBeforeLocalVar);
2824
188
            auto localVar = builder->emitVar(type);
2825
2826
188
            irVar = localVar;
2827
188
            legalVarVal = LegalVal::simple(irVar);
2828
188
        }
2829
188
        break;
2830
2831
491
    case kIROp_Param:
2832
491
        {
2833
491
            auto param = builder->emitParam(type);
2834
491
            param->insertBefore(context->insertBeforeParam);
2835
2836
491
            irVar = param;
2837
491
            legalVarVal = LegalVal::simple(irVar);
2838
491
        }
2839
491
        break;
2840
2841
0
    default:
2842
0
        SLANG_UNEXPECTED("unexpected IR opcode");
2843
0
        break;
2844
1.08k
    }
2845
2846
1.08k
    if (irVar)
2847
1.08k
    {
2848
1.08k
        if (varLayout)
2849
410
        {
2850
410
            builder->addLayoutDecoration(irVar, varLayout);
2851
410
        }
2852
2853
1.08k
        if (nameHint.getLength())
2854
781
        {
2855
781
            context->builder->addNameHintDecoration(irVar, nameHint);
2856
781
        }
2857
2858
1.08k
        if (leafVar)
2859
1.08k
        {
2860
1.08k
            cloneDecorationToVar(leafVar, irVar);
2861
1.08k
            if (as<IRStructKey>(leafVar))
2862
859
            {
2863
                // Find the struct field and clone any decorations on the field over.
2864
3.55k
                for (auto use = leafVar->firstUse; use; use = use->nextUse)
2865
3.55k
                {
2866
3.55k
                    if (auto field = as<IRStructField>(use->getUser()))
2867
859
                    {
2868
859
                        cloneDecorationToVar(field, irVar);
2869
859
                        break;
2870
859
                    }
2871
3.55k
                }
2872
859
            }
2873
1.08k
        }
2874
1.08k
    }
2875
2876
1.08k
    return legalVarVal;
2877
1.08k
}
2878
2879
/// Add layout information for the fields of a wrapped buffer type.
2880
///
2881
/// A wrapped buffer type encodes a buffer like `ConstantBuffer<Foo>`
2882
/// where `Foo` might have interface-type fields that have been
2883
/// specialized to a concrete type. E.g.:
2884
///
2885
///     struct Car { IDriver driver; int mph; };
2886
///     ConstantBuffer<Car> machOne;
2887
///
2888
/// In a case where the `machOne.driver` field has been specialized
2889
/// to the type `SpeedRacer`, we need to generate a legalized
2890
/// buffer layout something like:
2891
///
2892
///     struct Car_0 { int mph; }
2893
///     struct Wrapped { Car_0 car; SpeedRacer card_d; }
2894
///     ConstantBuffer<Wrapped> machOne;
2895
///
2896
/// The layout information for the existing `machOne` clearly
2897
/// can't apply because we have a new element type with new fields.
2898
///
2899
/// This function is used to recursively fill in the layout for
2900
/// the fields of the `Wrapped` type, using information recorded
2901
/// when the legal wrapped buffer type was created.
2902
///
2903
static void _addFieldsToWrappedBufferElementTypeLayout(
2904
    IRBuilder* irBuilder,
2905
    IRTypeLayout* elementTypeLayout,            // layout of the original field type
2906
    IRStructTypeLayout::Builder* newTypeLayout, // layout we are filling in
2907
    LegalElementWrapping const& elementInfo,    // information on how the original type got wrapped
2908
    LegalVarChain const& varChain,              // chain of variables that is leading to this field
2909
    bool isSpecial) // should we assume a leaf field is a special (interface) type?
2910
0
{
2911
    // The way we handle things depends primary on the
2912
    // `elementInfo`, because that tells us how things
2913
    // were wrapped up when the type was legalized.
2914
2915
0
    switch (elementInfo.flavor)
2916
0
    {
2917
0
    case LegalElementWrapping::Flavor::none:
2918
        // A leaf `none` value meant there was nothing
2919
        // to encode for a particular field (probably
2920
        // had a `void` or empty structure type).
2921
0
        break;
2922
2923
0
    case LegalElementWrapping::Flavor::simple:
2924
0
        {
2925
0
            auto simpleInfo = elementInfo.getSimple();
2926
2927
            // A `simple` wrapping means we hit a leaf
2928
            // field that can be encoded directly.
2929
            // What we do here depends on whether we've
2930
            // reached an ordinary field of the original
2931
            // data type, or if we've reached a leaf
2932
            // field of interface type.
2933
            //
2934
            // We've been tracking a `varChain` that
2935
            // remembers all the parent `struct` fields
2936
            // we've navigated through to get here
2937
            //
2938
            // * The layout represents the storage
2939
            // of the buffer element type as we usually
2940
            // think of its (e.g., the bytes starting at offset zero).
2941
            //
2942
            // We will be computing layout information
2943
            // for a field of the new/wrapped buffer element type.
2944
0
            IRVarLayout* newFieldLayout =
2945
0
                createSimpleVarLayout(irBuilder, varChain.primaryChain, elementTypeLayout);
2946
2947
            // We add the new field to the struct type
2948
            // layout we are building, and also update the mapping
2949
            // information so that we can find the field layout
2950
            // based on the IR key for the struct field.
2951
            //
2952
0
            newTypeLayout->addField(simpleInfo->key, newFieldLayout);
2953
0
        }
2954
0
        break;
2955
2956
0
    case LegalElementWrapping::Flavor::implicitDeref:
2957
0
        {
2958
            // This is the case where a field in the element type
2959
            // has been legalized from `SomePtrLikeType<T>` to
2960
            // `T`, so there is a different in levels of indirection.
2961
            //
2962
            // We need to recurse and see how the type `T`
2963
            // got laid out to know what field(s) it might comprise.
2964
            //
2965
0
            auto implicitDerefInfo = elementInfo.getImplicitDeref();
2966
0
            _addFieldsToWrappedBufferElementTypeLayout(
2967
0
                irBuilder,
2968
0
                elementTypeLayout,
2969
0
                newTypeLayout,
2970
0
                implicitDerefInfo->field,
2971
0
                varChain,
2972
0
                isSpecial);
2973
0
            return;
2974
0
        }
2975
0
        break;
2976
2977
0
    case LegalElementWrapping::Flavor::pair:
2978
0
        {
2979
            // The pair case is the first main workhorse where
2980
            // if we had a type that mixed ordinary and interface-type
2981
            // fields, it would get split into an ordinary part
2982
            // and a "special" part, each of which might comprise
2983
            // zero or more fields.
2984
            //
2985
            // Here we recurse on both the ordinary and special
2986
            // sides, and the only interesting tidbit is that
2987
            // we pass along appropriate values for the `isSpecial`
2988
            // flag so that we act appropriately upon running
2989
            // into a leaf field.
2990
            //
2991
0
            auto pairElementInfo = elementInfo.getPair();
2992
0
            _addFieldsToWrappedBufferElementTypeLayout(
2993
0
                irBuilder,
2994
0
                elementTypeLayout,
2995
0
                newTypeLayout,
2996
0
                pairElementInfo->ordinary,
2997
0
                varChain,
2998
0
                false);
2999
0
            _addFieldsToWrappedBufferElementTypeLayout(
3000
0
                irBuilder,
3001
0
                elementTypeLayout,
3002
0
                newTypeLayout,
3003
0
                pairElementInfo->special,
3004
0
                varChain,
3005
0
                true);
3006
0
        }
3007
0
        break;
3008
3009
0
    case LegalElementWrapping::Flavor::tuple:
3010
0
        {
3011
0
            auto tupleInfo = elementInfo.getTuple();
3012
3013
            // There is an extremely special case that we need to deal with here,
3014
            // which is the case where the original element/field had an interface
3015
            // type, which was subject to static specialization.
3016
            //
3017
            // In such a case, the layout will show a simple type layout for
3018
            // the field/element itself (just uniform data) plus a "pending"
3019
            // layout for any fields related to the static specialization.
3020
            //
3021
            // In contrast, the actual IR type structure will have been turned
3022
            // into a `struct` type with multiple fields, one of which is a
3023
            // pseudo-pointer to the "pending" data. That field would require
3024
            // legalization, sending us down this path.
3025
            //
3026
            // The situation here is that we have an `elementTypeLayout` that
3027
            // is for a single field of interface type, but an `elementInfo`
3028
            // that corresponds to a struct with 3 or more fields (the tuple
3029
            // that was introduced to represent the interface type).
3030
            //
3031
            // We expect that `elementInfo` will represent a tuple with
3032
            // only a single element, and that element will reference the third
3033
            // field of the tuple/struct (the payload).
3034
            //
3035
            // What we want to do in this case is instead add the fields
3036
            // corresponding to the payload type, which are stored as
3037
            // the pending type layout on `elementTypeLayout`.
3038
            //
3039
0
            if (isSpecial)
3040
0
            {
3041
0
                if (auto existentialTypeLayout = as<IRExistentialTypeLayout>(elementTypeLayout))
3042
0
                {
3043
0
                    SLANG_ASSERT(tupleInfo->elements.getCount() == 1);
3044
3045
0
                    for (auto ee : tupleInfo->elements)
3046
0
                    {
3047
0
                        _addFieldsToWrappedBufferElementTypeLayout(
3048
0
                            irBuilder,
3049
0
                            existentialTypeLayout,
3050
0
                            newTypeLayout,
3051
0
                            ee.field,
3052
0
                            varChain,
3053
0
                            true);
3054
0
                    }
3055
3056
0
                    return;
3057
0
                }
3058
0
            }
3059
3060
            // A tuple comes up when we've turned an aggregate
3061
            // with one or more interface-type fields into
3062
            // distinct fields at the top level.
3063
            //
3064
            // For the most part we just recurse on each field,
3065
            // but note that we set the `isSpecial` flag on
3066
            // the recursive calls, since we never use tuples
3067
            // to store anything that isn't special.
3068
3069
0
            for (auto ee : tupleInfo->elements)
3070
0
            {
3071
0
                auto oldFieldLayout = getFieldLayout(elementTypeLayout, ee.key);
3072
0
                SLANG_ASSERT(oldFieldLayout);
3073
3074
0
                LegalVarChainLink fieldChain(varChain, oldFieldLayout);
3075
3076
0
                _addFieldsToWrappedBufferElementTypeLayout(
3077
0
                    irBuilder,
3078
0
                    oldFieldLayout->getTypeLayout(),
3079
0
                    newTypeLayout,
3080
0
                    ee.field,
3081
0
                    fieldChain,
3082
0
                    true);
3083
0
            }
3084
0
        }
3085
0
        break;
3086
3087
0
    default:
3088
0
        SLANG_UNEXPECTED("unhandled element wrapping flavor");
3089
0
        break;
3090
0
    }
3091
0
}
3092
3093
/// Place offset information from `srcResInfo` onto `dstLayout`,
3094
/// offset by whatever is in `offsetVarLayout`
3095
3096
/// Create layout information for a wrapped buffer type.
3097
///
3098
/// A wrapped buffer type encodes a buffer like `ConstantBuffer<Foo>`
3099
/// where `Foo` might have interface-type fields that have been
3100
/// specialized to a concrete type.
3101
///
3102
/// Consider:
3103
///
3104
///     struct Car { IDriver driver; int mph; };
3105
///     ConstantBuffer<Car> machOne;
3106
///
3107
/// In a case where the `machOne.driver` field has been specialized
3108
/// to the type `SpeedRacer`, we need to generate a legalized
3109
/// buffer layout something like:
3110
///
3111
///     struct Car_0 { int mph; }
3112
///     struct Wrapped { Car_0 car; SpeedRacer card_d; }
3113
///     ConstantBuffer<Wrapped> machOne;
3114
///
3115
/// The layout information for the existing `machOne` clearly
3116
/// can't apply because we have a new element type with new fields.
3117
///
3118
/// This function is used to create a layout for a legalized
3119
/// buffer type that requires wrapping, based on the original
3120
/// type layout information and the variable layout information
3121
/// of the surrounding context (e.g., the global shader parameter
3122
/// that has this type).
3123
///
3124
static IRTypeLayout* _createWrappedBufferTypeLayout(
3125
    IRBuilder* irBuilder,
3126
    IRTypeLayout* oldTypeLayout,
3127
    WrappedBufferPseudoType* wrappedBufferTypeInfo)
3128
0
{
3129
    // We shouldn't get invoked unless there was a parameter group type,
3130
    // so we will sanity check for that just to be sure.
3131
    //
3132
0
    auto oldParameterGroupTypeLayout = as<IRParameterGroupTypeLayout>(oldTypeLayout);
3133
0
    SLANG_ASSERT(oldParameterGroupTypeLayout);
3134
0
    if (!oldParameterGroupTypeLayout)
3135
0
        return oldTypeLayout;
3136
3137
    // The legalization step will have already flattened the data inside of
3138
    // the group to a single `struct` type.
3139
    //
3140
    // Our job is to compute a type layout that we can apply to that new
3141
    // element type, and to a parameter group surrounding it.
3142
    //
3143
3144
0
    IRParameterGroupTypeLayout::Builder newTypeLayoutBuilder(irBuilder);
3145
0
    newTypeLayoutBuilder.addResourceUsageFrom(oldTypeLayout);
3146
3147
    // We will start our construction of the pieces of the output
3148
    // type layout by looking at the "container" type/variable.
3149
    //
3150
    // A parameter block or constant buffer in Slang needs to
3151
    // distinguish between the resource usage of the thing in
3152
    // the block/buffer, vs. the resource usage of the block/buffer
3153
    // itself. Consider:
3154
    //
3155
    //      struct Material { float4 color; Texture2D tex; }
3156
    //      ConstantBuffer<Material> gMat;
3157
    //
3158
    // When compiling for Vulkan, the `gMat` constant buffer needs
3159
    // a `binding`, and the `tex` field does too, so the overall
3160
    // resource usage of `gMat` is two bindings, but we need a
3161
    // way to encode which of those bindings goes to `gMat.tex`
3162
    // and which to the constant buffer for `gMat` itself.
3163
    //
3164
0
    {
3165
        // We will start by extracting the "primary" part of the old
3166
        // container type/var layout, and constructing new objects
3167
        // that will represent the layout for our wrapped buffer.
3168
        //
3169
0
        auto oldPrimaryContainerVarLayout = oldParameterGroupTypeLayout->getContainerVarLayout();
3170
0
        auto oldPrimaryContainerTypeLayout = oldPrimaryContainerVarLayout->getTypeLayout();
3171
3172
0
        IRTypeLayout::Builder newContainerTypeLayoutBuilder(irBuilder);
3173
0
        newContainerTypeLayoutBuilder.addResourceUsageFrom(oldPrimaryContainerTypeLayout);
3174
3175
0
        auto newContainerTypeLayout = newContainerTypeLayoutBuilder.build();
3176
3177
3178
0
        IRVarLayout::Builder newContainerVarLayoutBuilder(irBuilder, newContainerTypeLayout);
3179
3180
        // Whatever got allocated for the primary container should get copied
3181
        // over to the new layout (e.g., if we allocated a constant buffer
3182
        // for `gMat` then we need to retain that information).
3183
        //
3184
0
        for (auto resInfo : oldPrimaryContainerVarLayout->getOffsetAttrs())
3185
0
        {
3186
0
            auto newResInfo =
3187
0
                newContainerVarLayoutBuilder.findOrAddResourceInfo(resInfo->getResourceKind());
3188
0
            newResInfo->offset = resInfo->getOffset();
3189
0
            newResInfo->space = resInfo->getSpace();
3190
0
        }
3191
3192
0
        auto newContainerVarLayout = newContainerVarLayoutBuilder.build();
3193
0
        newTypeLayoutBuilder.setContainerVarLayout(newContainerVarLayout);
3194
0
    }
3195
3196
    // Now that we've dealt with the container variable, we can turn
3197
    // our attention to the element type. This is the part that
3198
    // actually got legalized and required us to create a "wrapped"
3199
    // buffer type in the first place.
3200
    //
3201
    // Let's start by extracting the fields we care about from
3202
    // the original element type/var layout, and constructing
3203
    // the objects we'll use to represent the type/var layout for
3204
    // the new element type.
3205
    //
3206
0
    auto oldElementVarLayout = oldParameterGroupTypeLayout->getElementVarLayout();
3207
0
    auto oldElementTypeLayout = oldElementVarLayout->getTypeLayout();
3208
3209
    // Now matter what, the element type of a wrapped buffer
3210
    // will always have a structure type.
3211
    //
3212
0
    IRStructTypeLayout::Builder newElementTypeLayoutBuilder(irBuilder);
3213
3214
    // The `wrappedBufferTypeInfo` that was passed in tells
3215
    // us how the fields of the original type got turned into
3216
    // zero or more fields in the new element type, so we
3217
    // need to follow its recursive structure to build
3218
    // layout information for each of the new fields.
3219
    //
3220
    // We will track a "chain" of parent variables that
3221
    // determines how we got to each leaf field, and is
3222
    // used to add up the offsets that will be stored
3223
    // in the new `VarLayout`s that get created.
3224
    // Initialize the variable chain for element type processing.
3225
    //
3226
0
    LegalVarChain varChainForElementType;
3227
0
    varChainForElementType.primaryChain = nullptr;
3228
3229
0
    _addFieldsToWrappedBufferElementTypeLayout(
3230
0
        irBuilder,
3231
0
        oldElementTypeLayout,
3232
0
        &newElementTypeLayoutBuilder,
3233
0
        wrappedBufferTypeInfo->elementInfo,
3234
0
        varChainForElementType,
3235
0
        true);
3236
3237
0
    auto newElementTypeLayout = newElementTypeLayoutBuilder.build();
3238
3239
    // A parameter group type layout holds a `VarLayout` for the element type,
3240
    // which encodes the offset of the element type with respect to the
3241
    // start of the parameter group as a whole (e.g., to handle the case
3242
    // where a constant buffer needs a `binding`, and so does its
3243
    // element type, so the offset to the first `binding` for the element
3244
    // type is one, not zero.
3245
    //
3246
0
    LegalVarChainLink elementVarChain(
3247
0
        LegalVarChain(),
3248
0
        oldParameterGroupTypeLayout->getElementVarLayout());
3249
0
    auto newElementVarLayout = createVarLayout(irBuilder, elementVarChain, newElementTypeLayout);
3250
3251
0
    newTypeLayoutBuilder.setElementVarLayout(newElementVarLayout);
3252
3253
    // For legacy/API reasons, we also need to compute a version of the
3254
    // element type where the offset stored in the `elementVarLayout`
3255
    // gets "baked in" to the fields of the element type.
3256
    //
3257
    // TODO: For IR-based layout information the offset layout should
3258
    // not really be required, and it is only being used in a few places
3259
    // that could in principle be refactored. We need to make sure to
3260
    // do that cleanup eventually.
3261
    //
3262
0
    newTypeLayoutBuilder.setOffsetElementTypeLayout(
3263
0
        applyOffsetToTypeLayout(irBuilder, newElementTypeLayout, newElementVarLayout));
3264
3265
0
    return newTypeLayoutBuilder.build();
3266
0
}
3267
3268
static LegalVal declareVars(
3269
    IRTypeLegalizationContext* context,
3270
    IROp op,
3271
    LegalType type,
3272
    IRTypeLayout* inTypeLayout,
3273
    LegalVarChain const& inVarChain,
3274
    UnownedStringSlice nameHint,
3275
    IRInst* leafVar,
3276
    IRGlobalParamInfo* globalParamInfo,
3277
    bool isSpecial)
3278
5.93k
{
3279
5.93k
    LegalVarChain varChain = inVarChain;
3280
5.93k
    IRTypeLayout* typeLayout = inTypeLayout;
3281
3282
5.93k
    switch (type.flavor)
3283
5.93k
    {
3284
3.76k
    case LegalType::Flavor::none:
3285
3.76k
        return LegalVal();
3286
3287
1.08k
    case LegalType::Flavor::simple:
3288
1.08k
        return declareSimpleVar(
3289
1.08k
            context,
3290
1.08k
            op,
3291
1.08k
            type.getSimple(),
3292
1.08k
            typeLayout,
3293
1.08k
            varChain,
3294
1.08k
            nameHint,
3295
1.08k
            leafVar,
3296
1.08k
            globalParamInfo);
3297
0
        break;
3298
3299
150
    case LegalType::Flavor::implicitDeref:
3300
150
        {
3301
150
            auto unwrappedTypeLayout = typeLayout;
3302
150
            IRVarLayout* elementVarLayout = nullptr;
3303
3304
            // If the type layout is a ParameterGroupTypeLayout whose element
3305
            // is a resource type (not a struct, and not plain data), unwrap to
3306
            // the element layout so resource bindings propagate to the declared
3307
            // variable.
3308
            //
3309
            // We must NOT unwrap when the element is a plain data type (e.g.
3310
            // uint inside ConstantBuffer<uint>), because its type layout only
3311
            // has Uniform size attributes and createVarLayout would never apply
3312
            // binding/set offsets from the var chain.
3313
            //
3314
            // Struct elements are excluded because they get pair-decomposed, and
3315
            // the element layout refers to the full struct rather than the
3316
            // ordinary-only part.
3317
150
            if (auto paramGroupLayout = as<IRParameterGroupTypeLayout>(typeLayout))
3318
70
            {
3319
70
                auto paramGroupElementVarLayout = paramGroupLayout->getElementVarLayout();
3320
70
                auto paramGroupElementTypeLayout = paramGroupElementVarLayout->getTypeLayout();
3321
70
                bool elementHasNonUniformResourceSize = false;
3322
70
                if (!as<IRStructTypeLayout>(paramGroupElementTypeLayout))
3323
7
                {
3324
7
                    for (auto sizeAttr : paramGroupElementTypeLayout->getSizeAttrs())
3325
7
                    {
3326
7
                        if (sizeAttr->getResourceKind() != LayoutResourceKind::Uniform)
3327
4
                        {
3328
4
                            elementHasNonUniformResourceSize = true;
3329
4
                            break;
3330
4
                        }
3331
7
                    }
3332
7
                }
3333
70
                if (elementHasNonUniformResourceSize)
3334
4
                {
3335
4
                    elementVarLayout = paramGroupElementVarLayout;
3336
4
                    unwrappedTypeLayout = paramGroupElementTypeLayout;
3337
4
                }
3338
70
            }
3339
3340
            // This constructor just copies `varChain` if `elementVarLayout` is null.
3341
150
            LegalVarChainLink varChainWithElement(varChain, elementVarLayout);
3342
3343
            // Declare a variable of the pointed-to type,
3344
            // since we are removing the indirection.
3345
150
            auto val = declareVars(
3346
150
                context,
3347
150
                op,
3348
150
                type.getImplicitDeref()->valueType,
3349
150
                unwrappedTypeLayout,
3350
150
                varChainWithElement,
3351
150
                nameHint,
3352
150
                leafVar,
3353
150
                globalParamInfo,
3354
150
                isSpecial);
3355
150
            return LegalVal::implicitDeref(val);
3356
0
        }
3357
0
        break;
3358
3359
239
    case LegalType::Flavor::pair:
3360
239
        {
3361
239
            auto pairType = type.getPair();
3362
239
            auto ordinaryVal = declareVars(
3363
239
                context,
3364
239
                op,
3365
239
                pairType->ordinaryType,
3366
239
                typeLayout,
3367
239
                varChain,
3368
239
                nameHint,
3369
239
                leafVar,
3370
239
                globalParamInfo,
3371
239
                false);
3372
239
            auto specialVal = declareVars(
3373
239
                context,
3374
239
                op,
3375
239
                pairType->specialType,
3376
239
                typeLayout,
3377
239
                varChain,
3378
239
                nameHint,
3379
239
                leafVar,
3380
239
                globalParamInfo,
3381
239
                true);
3382
239
            return LegalVal::pair(ordinaryVal, specialVal, pairType->pairInfo);
3383
0
        }
3384
3385
689
    case LegalType::Flavor::tuple:
3386
689
        {
3387
            // Declare one variable for each element of the tuple
3388
689
            auto tupleType = type.getTuple();
3389
3390
689
            RefPtr<TuplePseudoVal> tupleVal = new TuplePseudoVal();
3391
3392
689
            for (auto ee : tupleType->elements)
3393
1.04k
            {
3394
1.04k
                auto fieldLayout = getFieldLayout(typeLayout, ee.key);
3395
1.04k
                IRTypeLayout* fieldTypeLayout =
3396
1.04k
                    fieldLayout ? fieldLayout->getTypeLayout() : nullptr;
3397
3398
                // If we have a type layout coming in, we really expect to have a layout for each
3399
                // field.
3400
1.04k
                SLANG_ASSERT(fieldLayout || !typeLayout);
3401
3402
                // If we are processing layout information, then
3403
                // we need to create a new link in the chain
3404
                // of variables that will determine offsets
3405
                // for the eventual leaf fields...
3406
                //
3407
1.04k
                LegalVarChainLink newVarChain(varChain, fieldLayout);
3408
3409
1.04k
                UnownedStringSlice fieldNameHint;
3410
1.04k
                String joinedNameHintStorage;
3411
1.04k
                if (nameHint.getLength())
3412
717
                {
3413
717
                    if (auto fieldNameHintDecoration =
3414
717
                            ee.key->findDecoration<IRNameHintDecoration>())
3415
709
                    {
3416
709
                        joinedNameHintStorage.append(nameHint);
3417
709
                        joinedNameHintStorage.append(".");
3418
709
                        joinedNameHintStorage.append(fieldNameHintDecoration->getName());
3419
3420
709
                        fieldNameHint = joinedNameHintStorage.getUnownedSlice();
3421
709
                    }
3422
717
                }
3423
3424
1.04k
                LegalVal fieldVal = declareVars(
3425
1.04k
                    context,
3426
1.04k
                    op,
3427
1.04k
                    ee.type,
3428
1.04k
                    fieldTypeLayout,
3429
1.04k
                    newVarChain,
3430
1.04k
                    fieldNameHint,
3431
1.04k
                    ee.key,
3432
1.04k
                    globalParamInfo,
3433
1.04k
                    true);
3434
3435
1.04k
                TuplePseudoVal::Element element;
3436
1.04k
                element.key = ee.key;
3437
1.04k
                element.val = fieldVal;
3438
1.04k
                tupleVal->elements.add(element);
3439
1.04k
            }
3440
3441
689
            if (tupleVal->elements.getCount() == 2 && tupleVal->elements[0].key &&
3442
689
                tupleVal->elements[0].key->findDecorationImpl(kIROp_CounterBufferDecoration))
3443
12
            {
3444
                // If this is a lowered struct from a structured buffer type that has an atomic
3445
                // counter, insert decorations to each element var to associate the element buffer
3446
                // with the atomic buffer. This decoration is inserted to all lowered structs in the
3447
                // slang-ir-lower-append-consume-structured-buffer pass.
3448
                //
3449
12
                if (tupleVal->elements[0].val.flavor == LegalVal::Flavor::simple &&
3450
12
                    tupleVal->elements[1].val.flavor == LegalVal::Flavor::simple)
3451
12
                {
3452
12
                    auto simpleElementVar = tupleVal->elements[0].val.getSimple();
3453
12
                    auto simpleCounterVar = tupleVal->elements[1].val.getSimple();
3454
12
                    IRBuilder builder(simpleElementVar);
3455
12
                    builder.addDecoration(
3456
12
                        simpleElementVar,
3457
12
                        kIROp_CounterBufferDecoration,
3458
12
                        simpleCounterVar);
3459
                    // Clone decorations from leafVar to both element and counter var.
3460
12
                    cloneDecorationToVar(leafVar, simpleElementVar);
3461
12
                    cloneDecorationToVar(leafVar, simpleCounterVar);
3462
12
                }
3463
12
            }
3464
3465
689
            return LegalVal::tuple(tupleVal);
3466
0
        }
3467
0
        break;
3468
3469
0
    case LegalType::Flavor::wrappedBuffer:
3470
0
        {
3471
0
            auto wrappedBuffer = type.getWrappedBuffer();
3472
3473
0
            auto wrappedTypeLayout =
3474
0
                _createWrappedBufferTypeLayout(context->builder, typeLayout, wrappedBuffer);
3475
3476
0
            auto innerVal = declareSimpleVar(
3477
0
                context,
3478
0
                op,
3479
0
                wrappedBuffer->simpleType,
3480
0
                wrappedTypeLayout,
3481
0
                varChain,
3482
0
                nameHint,
3483
0
                leafVar,
3484
0
                globalParamInfo);
3485
3486
0
            return LegalVal::wrappedBuffer(innerVal, wrappedBuffer->elementInfo);
3487
0
        }
3488
3489
0
    default:
3490
0
        SLANG_UNEXPECTED("unhandled");
3491
0
        UNREACHABLE_RETURN(LegalVal());
3492
0
        break;
3493
5.93k
    }
3494
5.93k
}
3495
3496
static LegalVal legalizeGlobalVar(IRTypeLegalizationContext* context, IRGlobalVar* irGlobalVar)
3497
2.05k
{
3498
    // Legalize the type for the variable's value
3499
2.05k
    auto originalValueType = irGlobalVar->getDataType()->getValueType();
3500
2.05k
    auto legalValueType = legalizeType(context, originalValueType);
3501
2.05k
    auto varPtrType = as<IRPtrTypeBase>(irGlobalVar->getDataType());
3502
2.05k
    switch (legalValueType.flavor)
3503
2.05k
    {
3504
2.05k
    case LegalType::Flavor::simple:
3505
        // Easy case: the type is usable as-is, and we
3506
        // should just do that.
3507
2.05k
        context->builder->setDataType(
3508
2.05k
            irGlobalVar,
3509
2.05k
            context->builder->getPtrType(
3510
2.05k
                legalValueType.getSimple(),
3511
2.05k
                varPtrType ? varPtrType->getAccessQualifier() : AccessQualifier::ReadWrite,
3512
2.05k
                varPtrType ? varPtrType->getAddressSpace() : AddressSpace::Global,
3513
2.05k
                varPtrType ? varPtrType->getDataLayout()
3514
2.05k
                           : context->builder->getDefaultBufferLayoutType()));
3515
2.05k
        return LegalVal::simple(irGlobalVar);
3516
3517
5
    default:
3518
5
        {
3519
5
            context->insertBeforeGlobal = irGlobalVar;
3520
3521
5
            UnownedStringSlice nameHint = findNameHint(irGlobalVar);
3522
5
            context->builder->setInsertBefore(irGlobalVar);
3523
5
            LegalVal newVal = declareVars(
3524
5
                context,
3525
5
                kIROp_GlobalVar,
3526
5
                legalValueType,
3527
5
                nullptr,
3528
5
                LegalVarChain(),
3529
5
                nameHint,
3530
5
                irGlobalVar,
3531
5
                nullptr,
3532
5
                context->isSpecialType(originalValueType));
3533
3534
            // Register the new value as the replacement for the old
3535
5
            registerLegalizedValue(context, irGlobalVar, newVal);
3536
3537
            // Remove the old global from the module.
3538
5
            irGlobalVar->removeFromParent();
3539
5
            context->replacedInstructions.add(irGlobalVar);
3540
3541
5
            return newVal;
3542
0
        }
3543
0
        break;
3544
2.05k
    }
3545
2.05k
    UNREACHABLE_RETURN(LegalVal());
3546
0
}
3547
3548
static LegalVal legalizeGlobalParam(
3549
    IRTypeLegalizationContext* context,
3550
    IRGlobalParam* irGlobalParam)
3551
45.4k
{
3552
    // Legalize the type for the variable's value
3553
45.4k
    auto legalValueType = legalizeType(context, irGlobalParam->getFullType());
3554
3555
45.4k
    IRVarLayout* varLayout = findVarLayout(irGlobalParam);
3556
45.4k
    IRTypeLayout* typeLayout = varLayout ? varLayout->getTypeLayout() : nullptr;
3557
3558
45.4k
    switch (legalValueType.flavor)
3559
45.4k
    {
3560
45.2k
    case LegalType::Flavor::simple:
3561
        // Easy case: the type is usable as-is, and we
3562
        // should just do that.
3563
45.2k
        irGlobalParam->setFullType(legalValueType.getSimple());
3564
45.2k
        return LegalVal::simple(irGlobalParam);
3565
3566
200
    default:
3567
200
        {
3568
200
            context->insertBeforeGlobal = irGlobalParam;
3569
3570
200
            LegalVarChainLink varChain(LegalVarChain(), varLayout);
3571
3572
200
            IRGlobalParamInfo globalParamInfo;
3573
200
            if (auto entryPointParamDecoration =
3574
200
                    irGlobalParam->findDecoration<IREntryPointParamDecoration>())
3575
48
            {
3576
48
                globalParamInfo.originatingEntryPoint = entryPointParamDecoration->getEntryPoint();
3577
48
            }
3578
3579
            // TODO: need to handle initializer here!
3580
3581
200
            UnownedStringSlice nameHint = findNameHint(irGlobalParam);
3582
200
            context->builder->setInsertBefore(irGlobalParam);
3583
200
            LegalVal newVal = declareVars(
3584
200
                context,
3585
200
                kIROp_GlobalParam,
3586
200
                legalValueType,
3587
200
                typeLayout,
3588
200
                varChain,
3589
200
                nameHint,
3590
200
                irGlobalParam,
3591
200
                &globalParamInfo,
3592
200
                context->isSpecialType(irGlobalParam->getDataType()));
3593
3594
            // Register the new value as the replacement for the old
3595
200
            registerLegalizedValue(context, irGlobalParam, newVal);
3596
3597
            // Remove the old global from the module.
3598
200
            irGlobalParam->removeFromParent();
3599
200
            context->replacedInstructions.add(irGlobalParam);
3600
3601
200
            return newVal;
3602
0
        }
3603
0
        break;
3604
45.4k
    }
3605
45.4k
    UNREACHABLE_RETURN(LegalVal());
3606
0
}
3607
3608
static constexpr int kHasBeenAddedOrProcessedScratchBitIndex = 0;
3609
static constexpr int kHasBeenAddedScratchBitIndex = 1;
3610
3611
struct IRTypeLegalizationPass
3612
{
3613
    IRTypeLegalizationContext* context = nullptr;
3614
3615
    // The goal of this pass is to ensure that legalization has been
3616
    // applied to each instruction in a module. We also want to
3617
    // ensure that an insturction doesn't get processed until after
3618
    // all of its operands have been processed.
3619
    //
3620
    // The basic idea will be to maintain a work list of instructions
3621
    // that are able to be processed, and also a set to track which
3622
    // instructions have ever been added to the work list.
3623
3624
    List<IRInst*> workList;
3625
3626
29.5k
    IRTypeLegalizationPass() { workList.reserve(8192); }
3627
3628
    bool hasBeenAddedOrProcessed(IRInst* inst)
3629
0
    {
3630
0
        if (!inst)
3631
0
            return true;
3632
0
        return (inst->scratchData & (1 << kHasBeenAddedOrProcessedScratchBitIndex)) != 0;
3633
0
    }
3634
    void setHasBeenAddedOrProcessed(IRInst* inst)
3635
83.4M
    {
3636
83.4M
        inst->scratchData |= (1 << kHasBeenAddedOrProcessedScratchBitIndex);
3637
83.4M
    }
3638
    bool addedToWorkList(IRInst* inst)
3639
152M
    {
3640
152M
        return (inst->scratchData & (1 << kHasBeenAddedScratchBitIndex)) != 0;
3641
152M
    }
3642
    void setAddedToWorkList(IRInst* inst)
3643
41.7M
    {
3644
41.7M
        inst->scratchData |= (1 << kHasBeenAddedScratchBitIndex);
3645
41.7M
    }
3646
3647
    bool hasBeenAddedToWorkListOrProcessed(IRInst* inst)
3648
211M
    {
3649
211M
        if (!inst)
3650
6.84M
            return true;
3651
204M
        return (inst->scratchData != 0);
3652
211M
    }
3653
3654
    // We will add a simple query to check whether an instruciton
3655
    // has been put on the work list before (or if it should be
3656
    // treated *as if* it has been placed on the work list).
3657
    //
3658
    bool hasBeenAddedToWorkList(IRInst* inst)
3659
110M
    {
3660
        // Sometimes we end up with instructions that have a null
3661
        // operand (mostly as the type field of key instructions
3662
        // like the module itself).
3663
        //
3664
        // We want to treat such null pointers like we would an
3665
        // already-processed instruction.
3666
        //
3667
110M
        if (!inst)
3668
0
            return true;
3669
3670
        // HACK(tfoley): In most cases it is structurally invalid for our
3671
        // IR to have a cycle where following the operands (or type) of
3672
        // instructions can lead back to the original instruction.
3673
        //
3674
        // (Note that circular dependencies are still possible, but they
3675
        // must generally be from *children* of an instruction back
3676
        // to the instruction itself. E.g., an instruction in the body
3677
        // of a function can directly or indirectly depend on that function.)
3678
        //
3679
        // The one key counterexample is with interface types, because their
3680
        // requirements and the expected types of those requirements are
3681
        // encoded as operands. A typical method on inteface `I` will have a type
3682
        // that involves a `ThisType<I>` parameter for `this`, and that creates
3683
        // a cycle back to `I`.
3684
        //
3685
        // In our type legalization pass we are going to manually break that
3686
        // cycle by treating all `IRInterfaceRequirementEntry` instructions
3687
        // as having already been processed, since there is no particular
3688
        // need for us to handle them as part of legalization.
3689
        //
3690
110M
        if (inst->getOp() == kIROp_InterfaceRequirementEntry)
3691
660
            return true;
3692
3693
110M
        return addedToWorkList(inst);
3694
110M
    }
3695
3696
    // Next we define a convenience routine for adding something to the work list.
3697
    //
3698
    void addToWorkList(IRInst* inst)
3699
41.7M
    {
3700
        // We want to avoid adding anything we've already added or processed.
3701
        //
3702
41.7M
        if (addedToWorkList(inst))
3703
0
            return;
3704
41.7M
        workList.add(inst);
3705
41.7M
        setAddedToWorkList(inst);
3706
41.7M
        setHasBeenAddedOrProcessed(inst);
3707
41.7M
    }
3708
3709
    void processModule(IRModule* module)
3710
29.5k
    {
3711
29.5k
        initializeScratchData(module->getModuleInst());
3712
3713
        // In order to process an entire module, we start by adding the
3714
        // root module insturction to our work list, and then we will
3715
        // proceed to process instructions until the work list goes dry.
3716
3717
29.5k
        addToWorkList(module->getModuleInst());
3718
447k
        while (workList.getCount() != 0)
3719
418k
        {
3720
            // The order of items in the work list is signficiant;
3721
            // later entries could depend on earlier ones. As such, we
3722
            // cannot just do something like the `fastRemoveAt(...)`
3723
            // operation that could potentially lead to instructions
3724
            // being processed in a different order than they were added.
3725
            //
3726
            // Instead, we will make a copy of the current work list
3727
            // at each step, and swap in an empty work list to be added
3728
            // to with any new instructions.
3729
            //
3730
418k
            List<IRInst*> workListCopy = _Move(workList);
3731
3732
418k
            resetScratchDataBit(module->getModuleInst(), kHasBeenAddedScratchBitIndex);
3733
3734
            // Now we simply process each instruction on the copy of
3735
            // the work list, knowing that `processInst` may add additional
3736
            // instructions to the original work list.
3737
            //
3738
418k
            for (auto inst : workListCopy)
3739
41.7M
            {
3740
41.7M
                processInst(inst);
3741
41.7M
            }
3742
418k
        }
3743
3744
        // After we are done, there might be various instructions that
3745
        // were marked for deletion, but have not yet been cleaned up.
3746
        //
3747
        // We will clean up all those unnecessary instructions now.
3748
        //
3749
29.5k
        for (auto& lv : context->replacedInstructions)
3750
35.5k
        {
3751
#if _DEBUG
3752
            for (auto use = lv->firstUse; use; use = use->nextUse)
3753
            {
3754
                auto user = use->getUser();
3755
                if (user->getModule() == nullptr)
3756
                    continue;
3757
                if (as<IRType>(user))
3758
                    continue;
3759
                if (!context->replacedInstructions.contains(user))
3760
                    SLANG_UNEXPECTED("replaced inst still has use.");
3761
                if (lv->getParent())
3762
                    SLANG_UNEXPECTED("replaced inst still in a parent.");
3763
            }
3764
#endif
3765
35.5k
            lv->removeAndDeallocate();
3766
35.5k
        }
3767
29.5k
    }
3768
3769
    void processInst(IRInst* inst)
3770
41.7M
    {
3771
        // It is possible that an insturction we
3772
        // encounterer during the legalization process
3773
        // will be one that was already removed or
3774
        // otherwise made redundant.
3775
        //
3776
        // We want to skip such instructions since there
3777
        // would not be a valid location at which to
3778
        // store their replacements.
3779
        //
3780
41.7M
        if (!inst->getParent() && inst->getOp() != kIROp_ModuleInst)
3781
32.9k
            return;
3782
3783
        // The main logic for legalizing an instruction is defined
3784
        // earlier in this file.
3785
        //
3786
        // Our primary task here is to legalize the instruction, and then
3787
        // register the result of legalization as the proper value
3788
        // for that instruction.
3789
        //
3790
41.7M
        LegalVal legalVal = legalizeInst(context, inst);
3791
41.7M
        registerLegalizedValue(context, inst, legalVal);
3792
3793
        // Once the instruction has been processed, we need to consider
3794
        // whether any other instructions might now be ready to process.
3795
        //
3796
        // An instruction `i` might have been blocked by `inst` if:
3797
        //
3798
        // * `inst` was an operand (including the type operand) of `i`, or
3799
        // * `inst` was the parent of `i`.
3800
        //
3801
        // To turn those relationships around, we want to check instructions
3802
        // `i` where:
3803
        //
3804
        // * `i` is a user of `inst`, or
3805
        // * `i` is a child of `inst`.
3806
        //
3807
41.7M
        if (legalVal.flavor == LegalVal::Flavor::simple)
3808
41.7M
        {
3809
            // The resulting inst may be different from the one we added to the
3810
            // worklist, so ensure that the appropriate flags are set.
3811
            //
3812
41.7M
            setHasBeenAddedOrProcessed(legalVal.irValue);
3813
3814
41.7M
            inst = legalVal.irValue;
3815
41.7M
        }
3816
3817
127M
        for (auto use = inst->firstUse; use; use = use->nextUse)
3818
85.9M
        {
3819
85.9M
            auto user = use->getUser();
3820
85.9M
            maybeAddToWorkList(user);
3821
85.9M
        }
3822
41.7M
        for (auto child : inst->getDecorationsAndChildren())
3823
24.8M
        {
3824
24.8M
            maybeAddToWorkList(child);
3825
24.8M
        }
3826
41.7M
    }
3827
3828
    void maybeAddToWorkList(IRInst* inst)
3829
110M
    {
3830
        // Here we have an `inst` that might be ready to go on
3831
        // the work list, but we need to check that it would
3832
        // be valid to put it on there.
3833
        //
3834
        // First, we don't want to add something if it has
3835
        // already been added.
3836
        //
3837
110M
        if (hasBeenAddedToWorkList(inst))
3838
42.9M
            return;
3839
3840
        // Next, we don't want to add something if its parent
3841
        // hasn't been added already.
3842
        //
3843
67.8M
        if (!hasBeenAddedToWorkListOrProcessed(inst->getParent()))
3844
16.5M
            return;
3845
3846
        // Finally, we don't want to add something if its
3847
        // type and/or operands haven't all been added.
3848
        //
3849
51.3M
        if (!hasBeenAddedToWorkListOrProcessed(inst->getFullType()))
3850
5.15M
            return;
3851
46.2M
        Index operandCount = (Index)inst->getOperandCount();
3852
133M
        for (Index i = 0; i < operandCount; ++i)
3853
92.2M
        {
3854
92.2M
            auto operand = inst->getOperand(i);
3855
92.2M
            if (!hasBeenAddedToWorkListOrProcessed(operand))
3856
4.53M
                return;
3857
92.2M
        }
3858
3859
        // If all those checks pass, then we are ready to
3860
        // process `inst`, and we will add it to our work list.
3861
        //
3862
41.6M
        addToWorkList(inst);
3863
41.6M
    }
3864
};
3865
3866
static void legalizeTypes(IRTypeLegalizationContext* context)
3867
29.5k
{
3868
29.5k
    IRTypeLegalizationPass pass;
3869
29.5k
    pass.context = context;
3870
3871
29.5k
    pass.processModule(context->module);
3872
29.5k
}
3873
3874
// We use the same basic type legalization machinery for both simplifying
3875
// away resource-type fields nested in `struct`s and for shuffling around
3876
// exisential-box fields to get the layout right.
3877
//
3878
// The differences between the two passes come down to some very small
3879
// distinctions about what types each pass considers "special" (e.g.,
3880
// resources in one case and existential boxes in the other), along
3881
// with what they want to do when a uniform/constant buffer needs to
3882
// be made where the element type is non-simple (that is, includes
3883
// some fields of "special" type).
3884
//
3885
// The resource case is then the simpler one:
3886
//
3887
struct IRResourceTypeLegalizationContext : IRTypeLegalizationContext
3888
{
3889
    IRResourceTypeLegalizationContext(TargetProgram* target, IRModule* module, DiagnosticSink* sink)
3890
6.10k
        : IRTypeLegalizationContext(target, module, sink)
3891
6.10k
    {
3892
6.10k
    }
3893
3894
    bool isSpecialType(IRType* type) override
3895
13.0k
    {
3896
        // For resource type legalization, the "special" types
3897
        // we are working with are resource types.
3898
        //
3899
13.0k
        return isResourceType(type);
3900
13.0k
    }
3901
3902
126k
    bool isSimpleType(IRType*) override { return false; }
3903
3904
    LegalType createLegalUniformBufferType(
3905
        IROp op,
3906
        LegalType legalElementType,
3907
        IRInst* layoutOperand) override
3908
99
    {
3909
        // The appropriate strategy for legalizing uniform buffers
3910
        // with resources inside already exists, so we can delegate to it.
3911
        //
3912
99
        return createLegalUniformBufferTypeForResources(this, op, legalElementType, layoutOperand);
3913
99
    }
3914
};
3915
3916
// The case for legalizing existential box types is then similar.
3917
//
3918
struct IRExistentialTypeLegalizationContext : IRTypeLegalizationContext
3919
{
3920
    IRExistentialTypeLegalizationContext(
3921
        TargetProgram* target,
3922
        IRModule* module,
3923
        DiagnosticSink* sink)
3924
6.10k
        : IRTypeLegalizationContext(target, module, sink)
3925
6.10k
    {
3926
6.10k
    }
3927
3928
    bool isSpecialType(IRType* inType) override
3929
16.0k
    {
3930
        // The "special" types for our purposes are existential
3931
        // boxes, or arrays thereof.
3932
        //
3933
16.0k
        auto type = unwrapArray(inType);
3934
16.0k
        return as<IRPseudoPtrType>(type) != nullptr;
3935
16.0k
    }
3936
3937
128k
    bool isSimpleType(IRType*) override { return false; }
3938
3939
    LegalType createLegalUniformBufferType(
3940
        IROp op,
3941
        LegalType legalElementType,
3942
        IRInst* layoutOperand) override
3943
0
    {
3944
        // We'll delegate the logic for creating uniform buffers
3945
        // over a mix of ordinary and existential-box types to
3946
        // a subroutine so it can live near the resource case.
3947
        //
3948
        // TODO: We should eventually try to refactor this code
3949
        // so that related functionality is grouped together.
3950
        //
3951
0
        return createLegalUniformBufferTypeForExistentials(
3952
0
            this,
3953
0
            op,
3954
0
            legalElementType,
3955
0
            layoutOperand);
3956
0
    }
3957
};
3958
3959
// This customization of type legalization is used to remove empty
3960
// structs from cpp/cuda programs if the empty type isn't used in
3961
// a public function signature.
3962
struct IREmptyTypeLegalizationContext : IRTypeLegalizationContext
3963
{
3964
    IREmptyTypeLegalizationContext(TargetProgram* target, IRModule* module, DiagnosticSink* sink)
3965
17.3k
        : IRTypeLegalizationContext(target, module, sink)
3966
17.3k
    {
3967
17.3k
    }
3968
3969
67.0k
    bool isSpecialType(IRType*) override { return false; }
3970
3971
    bool isSimpleType(IRType* type) override
3972
459k
    {
3973
459k
        if (isMetalTarget(targetProgram->getTargetReq()))
3974
42.9k
        {
3975
42.9k
            return false;
3976
42.9k
        }
3977
3978
        // If type is used as public interface, then treat it as simple.
3979
416k
        for (auto decor : type->getDecorations())
3980
174k
        {
3981
174k
            switch (decor->getOp())
3982
174k
            {
3983
8
            case kIROp_LayoutDecoration:
3984
557
            case kIROp_PublicDecoration:
3985
562
            case kIROp_ExternCppDecoration:
3986
562
            case kIROp_DllImportDecoration:
3987
562
            case kIROp_DllExportDecoration:
3988
692
            case kIROp_HLSLExportDecoration:
3989
9.82k
            case kIROp_BinaryInterfaceTypeDecoration:
3990
9.82k
                return true;
3991
174k
            }
3992
174k
        }
3993
406k
        return false;
3994
416k
    }
3995
3996
    LegalType createLegalUniformBufferType(IROp, LegalType, IRInst*) override
3997
0
    {
3998
0
        return LegalType();
3999
0
    }
4000
4001
    virtual bool shouldLegalizeParameterBlockElementType() override
4002
56
    {
4003
56
        return isMetalTarget(targetProgram->getTargetReq());
4004
56
    }
4005
};
4006
4007
// The main entry points that are used when transforming IR code
4008
// to get it ready for lower-level codegen are then simple
4009
// wrappers around `legalizeTypes()` that pick an appropriately
4010
// specialized context type to use to get the job done.
4011
4012
void legalizeResourceTypes(IRModule* module, TargetProgram* target, DiagnosticSink* sink)
4013
6.10k
{
4014
6.10k
    SLANG_PROFILE;
4015
4016
6.10k
    IRResourceTypeLegalizationContext context(target, module, sink);
4017
6.10k
    legalizeTypes(&context);
4018
6.10k
}
4019
4020
void legalizeExistentialTypeLayout(IRModule* module, TargetProgram* target, DiagnosticSink* sink)
4021
6.10k
{
4022
6.10k
    SLANG_PROFILE;
4023
4024
6.10k
    IRExistentialTypeLegalizationContext context(target, module, sink);
4025
6.10k
    legalizeTypes(&context);
4026
6.10k
}
4027
4028
void legalizeEmptyTypes(IRModule* module, TargetProgram* target, DiagnosticSink* sink)
4029
17.3k
{
4030
17.3k
    IREmptyTypeLegalizationContext context(target, module, sink);
4031
17.3k
    legalizeTypes(&context);
4032
17.3k
}
4033
4034
4035
} // namespace Slang