/__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 |