diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-05-31 17:20:37 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-05-31 17:20:37 -0400 |
| commit | 6cbc3929a54d37bd23cb5efa8e3320ba02f78b2f (patch) | |
| tree | 5a23cb47782e9e2a77762c90dd35da1005eba8d0 /source/slang/ir-legalize-types.cpp | |
| parent | b81ff3ef968d1cc4e954b31a1812b3c391d17b02 (diff) | |
Use slang- prefix on slang compiler and core source (#973)
* Prefixing source files in source/slang with slang-
* Prefix source in source/slang with slang- prefix.
* Rename core source files with slang- prefix.
* Update project files.
* Fix problems from automatic merge.
Diffstat (limited to 'source/slang/ir-legalize-types.cpp')
| -rw-r--r-- | source/slang/ir-legalize-types.cpp | 2626 |
1 files changed, 0 insertions, 2626 deletions
diff --git a/source/slang/ir-legalize-types.cpp b/source/slang/ir-legalize-types.cpp deleted file mode 100644 index 18039315e..000000000 --- a/source/slang/ir-legalize-types.cpp +++ /dev/null @@ -1,2626 +0,0 @@ -// ir-legalize-types.cpp - -// This file implements type legalization for the IR. -// It uses the core legalization logic in -// `legalize-types.{h,cpp}` to decide what to do with -// the types, while this file handles the actual -// rewriting of the IR to use the new types. -// -// This pass should only be applied to IR that has been -// fully specialized (no more generics/interfaces), so -// that the concrete type of everything is known. - -#include "ir.h" -#include "ir-clone.h" -#include "ir-insts.h" -#include "legalize-types.h" -#include "mangle.h" -#include "name.h" - -namespace Slang -{ - -LegalVal LegalVal::tuple(RefPtr<TuplePseudoVal> tupleVal) -{ - SLANG_ASSERT(tupleVal->elements.getCount()); - - LegalVal result; - result.flavor = LegalVal::Flavor::tuple; - result.obj = tupleVal; - return result; -} - -LegalVal LegalVal::pair(RefPtr<PairPseudoVal> pairInfo) -{ - LegalVal result; - result.flavor = LegalVal::Flavor::pair; - result.obj = pairInfo; - return result; -} - -LegalVal LegalVal::pair( - LegalVal const& ordinaryVal, - LegalVal const& specialVal, - RefPtr<PairInfo> pairInfo) -{ - if (ordinaryVal.flavor == LegalVal::Flavor::none) - return specialVal; - - if (specialVal.flavor == LegalVal::Flavor::none) - return ordinaryVal; - - - RefPtr<PairPseudoVal> obj = new PairPseudoVal(); - obj->ordinaryVal = ordinaryVal; - obj->specialVal = specialVal; - obj->pairInfo = pairInfo; - - return LegalVal::pair(obj); -} - -LegalVal LegalVal::implicitDeref(LegalVal const& val) -{ - RefPtr<ImplicitDerefVal> implicitDerefVal = new ImplicitDerefVal(); - implicitDerefVal->val = val; - - LegalVal result; - result.flavor = LegalVal::Flavor::implicitDeref; - result.obj = implicitDerefVal; - return result; -} - -LegalVal LegalVal::getImplicitDeref() -{ - SLANG_ASSERT(flavor == Flavor::implicitDeref); - return as<ImplicitDerefVal>(obj)->val; -} - -LegalVal LegalVal::wrappedBuffer( - LegalVal const& baseVal, - LegalElementWrapping const& elementInfo) -{ - RefPtr<WrappedBufferPseudoVal> obj = new WrappedBufferPseudoVal(); - obj->base = baseVal; - obj->elementInfo = elementInfo; - - LegalVal result; - result.flavor = LegalVal::Flavor::wrappedBuffer; - result.obj = obj; - return result; -} - -// - -IRTypeLegalizationContext::IRTypeLegalizationContext( - IRModule* inModule) -{ - session = inModule->getSession(); - module = inModule; - - auto sharedBuilder = &sharedBuilderStorage; - sharedBuilder->session = session; - sharedBuilder->module = module; - - builder = &builderStorage; - builder->sharedBuilder = sharedBuilder; -} - -static void registerLegalizedValue( - IRTypeLegalizationContext* context, - IRInst* irValue, - LegalVal const& legalVal) -{ - context->mapValToLegalVal[irValue] = legalVal; -} - -struct IRGlobalNameInfo -{ - IRInst* globalVar; - UInt counter; -}; - -static LegalVal declareVars( - IRTypeLegalizationContext* context, - IROp op, - LegalType type, - TypeLayout* typeLayout, - LegalVarChain const& varChain, - UnownedStringSlice nameHint, - IRInst* leafVar, - IRGlobalNameInfo* globalNameInfo, - bool isSpecial); - - /// Unwrap a value with flavor `wrappedBuffer` - /// - /// The original `legalPtrOperand` has a wrapped-buffer type - /// which encodes the way that, e.g., a `ConstantBuffer<Foo>` - /// where `Foo` includes interface types, got legalized - /// into a buffer that stores a `Foo` value plus addition - /// fields for the concrete types that got plugged in. - /// - /// The `elementInfo` is the layout information for the - /// modified ("wrapped") buffer type, and specifies how - /// the logical element type was expanded into actual fields. - /// - /// This function returns a new value that undoes all of - /// the wrapping and produces a new `LegalVal` that matches - /// the nominal type of the original buffer. - /// -static LegalVal unwrapBufferValue( - IRTypeLegalizationContext* context, - LegalVal legalPtrOperand, - LegalElementWrapping const& elementInfo); - - /// Perform any actions required to materialize `val` into a usable value. - /// - /// Certain case of `LegalVal` (currently just the `wrappedBuffer` case) are - /// suitable for use to represent a variable, but cannot be used directly - /// in computations, because their structured needs to be "unwrapped." - /// - /// This function unwraps any `val` that needs it, which may involve - /// emitting additional IR instructions, and returns the unmodified - /// `val` otherwise. - /// -static LegalVal maybeMaterializeWrappedValue( - IRTypeLegalizationContext* context, - LegalVal val) -{ - if(val.flavor != LegalVal::Flavor::wrappedBuffer) - return val; - - auto wrappedBufferVal = val.getWrappedBuffer(); - return unwrapBufferValue( - context, - wrappedBufferVal->base, - wrappedBufferVal->elementInfo); -} - -// Take a value that is being used as an operand, -// and turn it into the equivalent legalized value. -static LegalVal legalizeOperand( - IRTypeLegalizationContext* context, - IRInst* irValue) -{ - LegalVal legalVal; - if( context->mapValToLegalVal.TryGetValue(irValue, legalVal) ) - { - return maybeMaterializeWrappedValue(context, legalVal); - } - - // For now, assume that anything not covered - // by the mapping is legal as-is. - - return LegalVal::simple(irValue); -} - -static void getArgumentValues( - List<IRInst*> & instArgs, - LegalVal val) -{ - switch (val.flavor) - { - case LegalVal::Flavor::none: - break; - - case LegalVal::Flavor::simple: - instArgs.add(val.getSimple()); - break; - - case LegalVal::Flavor::implicitDeref: - getArgumentValues(instArgs, val.getImplicitDeref()); - break; - - case LegalVal::Flavor::pair: - { - auto pairVal = val.getPair(); - getArgumentValues(instArgs, pairVal->ordinaryVal); - getArgumentValues(instArgs, pairVal->specialVal); - } - break; - - case LegalVal::Flavor::tuple: - { - auto tuplePsuedoVal = val.getTuple(); - for (auto elem : val.getTuple()->elements) - { - getArgumentValues(instArgs, elem.val); - } - } - break; - - default: - SLANG_UNEXPECTED("uhandled val flavor"); - break; - } -} - -static LegalVal legalizeCall( - IRTypeLegalizationContext* context, - IRCall* callInst) -{ - auto retType = legalizeType(context, callInst->getFullType()); - IRType* retIRType = nullptr; - switch (retType.flavor) - { - case LegalType::Flavor::simple: - retIRType = retType.getSimple(); - break; - case LegalType::Flavor::none: - retIRType = context->builder->getVoidType(); - break; - default: - // TODO: implement legalization of non-simple return types - SLANG_UNEXPECTED("unimplemented legalized return type for IRInstCall."); - } - - List<IRInst*> instArgs; - for (auto i = 1u; i < callInst->getOperandCount(); i++) - getArgumentValues(instArgs, legalizeOperand(context, callInst->getOperand(i))); - - return LegalVal::simple(context->builder->emitCallInst( - retIRType, - callInst->getCallee(), - instArgs.getCount(), - instArgs.getBuffer())); -} - -static LegalVal legalizeRetVal(IRTypeLegalizationContext* context, - LegalVal retVal) -{ - switch (retVal.flavor) - { - case LegalVal::Flavor::simple: - return LegalVal::simple(context->builder->emitReturn(retVal.getSimple())); - case LegalVal::Flavor::none: - return LegalVal::simple(context->builder->emitReturn()); - default: - // TODO: implement legalization of non-simple return types - SLANG_UNEXPECTED("unimplemented legalized return type for IRReturnVal."); - } -} - -static LegalVal legalizeLoad( - IRTypeLegalizationContext* context, - LegalVal legalPtrVal) -{ - switch (legalPtrVal.flavor) - { - case LegalVal::Flavor::none: - return LegalVal(); - - case LegalVal::Flavor::simple: - { - return LegalVal::simple( - context->builder->emitLoad(legalPtrVal.getSimple())); - } - break; - - case LegalVal::Flavor::implicitDeref: - // We have turne a pointer(-like) type into its pointed-to (value) - // type, and so the operation of loading goes away; we just use - // the underlying value. - return legalPtrVal.getImplicitDeref(); - - case LegalVal::Flavor::pair: - { - auto ptrPairVal = legalPtrVal.getPair(); - - auto ordinaryVal = legalizeLoad(context, ptrPairVal->ordinaryVal); - auto specialVal = legalizeLoad(context, ptrPairVal->specialVal); - return LegalVal::pair(ordinaryVal, specialVal, ptrPairVal->pairInfo); - } - - case LegalVal::Flavor::tuple: - { - // We need to emit a load for each element of - // the tuple. - auto ptrTupleVal = legalPtrVal.getTuple(); - RefPtr<TuplePseudoVal> tupleVal = new TuplePseudoVal(); - - for (auto ee : legalPtrVal.getTuple()->elements) - { - TuplePseudoVal::Element element; - element.key = ee.key; - element.val = legalizeLoad(context, ee.val); - - tupleVal->elements.add(element); - } - return LegalVal::tuple(tupleVal); - } - break; - - default: - SLANG_UNEXPECTED("unhandled case"); - break; - } -} - -static LegalVal legalizeStore( - IRTypeLegalizationContext* context, - LegalVal legalPtrVal, - LegalVal legalVal) -{ - switch (legalPtrVal.flavor) - { - case LegalVal::Flavor::none: - return LegalVal(); - - case LegalVal::Flavor::simple: - { - context->builder->emitStore(legalPtrVal.getSimple(), legalVal.getSimple()); - return legalVal; - } - break; - - case LegalVal::Flavor::implicitDeref: - // TODO: what is the right behavior here? - // - // The crux of the problem is that we may legalize a pointer-to-pointer - // type in cases where one of the two needs to become an implicit-deref, - // so that we have `PtrA<PtrB<Thing>>` become, say, `PtrA<Thing>` with - // an `implicitDeref` wrapper. When we encounter a store to that - // wrapped value, we seemingly need to know whether the original code - // meant to store to `*ptrPtr` or `**ptrPtr`, and need to legalize - // the result accordingly... - // - if( legalVal.flavor == LegalVal::Flavor::implicitDeref ) - return legalizeStore(context, legalPtrVal.getImplicitDeref(), legalVal.getImplicitDeref()); - else - return legalizeStore(context, legalPtrVal.getImplicitDeref(), legalVal); - - case LegalVal::Flavor::pair: - { - auto destPair = legalPtrVal.getPair(); - auto valPair = legalVal.getPair(); - legalizeStore(context, destPair->ordinaryVal, valPair->ordinaryVal); - legalizeStore(context, destPair->specialVal, valPair->specialVal); - return LegalVal(); - } - - case LegalVal::Flavor::tuple: - { - // We need to emit a store for each element of - // the tuple. - auto destTuple = legalPtrVal.getTuple(); - auto valTuple = legalVal.getTuple(); - SLANG_ASSERT(destTuple->elements.getCount() == valTuple->elements.getCount()); - - for (Index i = 0; i < valTuple->elements.getCount(); i++) - { - legalizeStore(context, destTuple->elements[i].val, valTuple->elements[i].val); - } - return legalVal; - } - break; - - default: - SLANG_UNEXPECTED("unhandled case"); - break; - } -} - -static LegalVal legalizeFieldExtract( - IRTypeLegalizationContext* context, - LegalType type, - LegalVal legalStructOperand, - IRStructKey* fieldKey) -{ - auto builder = context->builder; - - if (type.flavor == LegalType::Flavor::none) - return LegalVal(); - - switch (legalStructOperand.flavor) - { - case LegalVal::Flavor::none: - return LegalVal(); - - case LegalVal::Flavor::simple: - return LegalVal::simple( - builder->emitFieldExtract( - type.getSimple(), - legalStructOperand.getSimple(), - fieldKey)); - - case LegalVal::Flavor::pair: - { - // There are two sides, the ordinary and the special, - // and we basically just dispatch to both of them. - auto pairVal = legalStructOperand.getPair(); - auto pairInfo = pairVal->pairInfo; - auto pairElement = pairInfo->findElement(fieldKey); - if (!pairElement) - { - SLANG_UNEXPECTED("didn't find tuple element"); - UNREACHABLE_RETURN(LegalVal()); - } - - // If the field we are extracting has a pair type, - // that means it exists on both the ordinary and - // special sides. - RefPtr<PairInfo> fieldPairInfo; - LegalType ordinaryType = type; - LegalType specialType = type; - if (type.flavor == LegalType::Flavor::pair) - { - auto fieldPairType = type.getPair(); - fieldPairInfo = fieldPairType->pairInfo; - ordinaryType = fieldPairType->ordinaryType; - specialType = fieldPairType->specialType; - } - - LegalVal ordinaryVal; - LegalVal specialVal; - - if (pairElement->flags & PairInfo::kFlag_hasOrdinary) - { - ordinaryVal = legalizeFieldExtract( - context, - ordinaryType, - pairVal->ordinaryVal, - fieldKey); - } - - if (pairElement->flags & PairInfo::kFlag_hasSpecial) - { - specialVal = legalizeFieldExtract( - context, - specialType, - pairVal->specialVal, - fieldKey); - } - return LegalVal::pair(ordinaryVal, specialVal, fieldPairInfo); - } - break; - - case LegalVal::Flavor::tuple: - { - // The operand is a tuple of pointer-like - // values, we want to extract the element - // corresponding to a field. We will handle - // this by simply returning the corresponding - // element from the operand. - auto ptrTupleInfo = legalStructOperand.getTuple(); - for (auto ee : ptrTupleInfo->elements) - { - if (ee.key == fieldKey) - { - return ee.val; - } - } - - // TODO: we can legally reach this case now - // when the field is "ordinary". - - SLANG_UNEXPECTED("didn't find tuple element"); - UNREACHABLE_RETURN(LegalVal()); - } - - default: - SLANG_UNEXPECTED("unhandled"); - UNREACHABLE_RETURN(LegalVal()); - } -} - -static LegalVal legalizeFieldExtract( - IRTypeLegalizationContext* context, - LegalType type, - LegalVal legalPtrOperand, - LegalVal legalFieldOperand) -{ - // We don't expect any legalization to affect - // the "field" argument. - auto fieldKey = legalFieldOperand.getSimple(); - - return legalizeFieldExtract( - context, - type, - legalPtrOperand, - (IRStructKey*) fieldKey); -} - - /// Take a value of some buffer/pointer type and unwrap it according to provided info. -static LegalVal unwrapBufferValue( - IRTypeLegalizationContext* context, - LegalVal legalPtrOperand, - LegalElementWrapping const& elementInfo) -{ - // The `elementInfo` tells us how a non-simple element - // type was wrapped up into a new structure types used - // as the element type of the buffer. - // - // This function will recurse through the structure of - // `elementInfo` to pull out all the required data from - // the buffer represented by `legalPtrOperand`. - - switch( elementInfo.flavor ) - { - default: - SLANG_UNEXPECTED("unhandled"); - UNREACHABLE_RETURN(LegalVal()); - break; - - case LegalElementWrapping::Flavor::none: - return LegalVal(); - - case LegalElementWrapping::Flavor::simple: - { - // In the leaf case, we just had to store some - // data of a simple type in the buffer. We can - // produce a valid result by computing the - // address of the field used to represent the - // element, and then returning *that* as if - // it were the buffer type itself. - // - // (Basically instead of `someBuffer` we will - // end up with `&(someBuffer->field)`. - // - auto builder = context->getBuilder(); - - auto simpleElementInfo = elementInfo.getSimple(); - auto valPtr = builder->emitFieldAddress( - builder->getPtrType(simpleElementInfo->type), - legalPtrOperand.getSimple(), - simpleElementInfo->key); - - return LegalVal::simple(valPtr); - } - - case LegalElementWrapping::Flavor::implicitDeref: - { - // If the element type was logically `ImplicitDeref<T>`, - // then we declared actual fields based on `T`, and - // we need to extract references to those fields and - // wrap them up in an `implicitDeref` value. - // - auto derefField = elementInfo.getImplicitDeref(); - auto baseVal = unwrapBufferValue(context, legalPtrOperand, derefField->field); - return LegalVal::implicitDeref(baseVal); - } - - case LegalElementWrapping::Flavor::pair: - { - // If the element type was logically a `Pair<O,S>` - // then we encoded fields for both `O` and `S` into - // the actual element type, and now we need to - // extract references to both and pair them up. - // - auto pairField = elementInfo.getPair(); - auto pairInfo = pairField->pairInfo; - - auto ordinaryVal = unwrapBufferValue(context, legalPtrOperand, pairField->ordinary); - auto specialVal = unwrapBufferValue(context, legalPtrOperand, pairField->special); - return LegalVal::pair(ordinaryVal, specialVal, pairInfo); - } - - case LegalElementWrapping::Flavor::tuple: - { - // If the element type was logically a `Tuple<E0, E1, ...>` - // then we encoded fields for each of the `Ei` and - // need to extract references to all of them and - // encode them as a tuple. - // - auto tupleField = elementInfo.getTuple(); - - RefPtr<TuplePseudoVal> obj = new TuplePseudoVal(); - for( auto ee : tupleField->elements ) - { - auto elementVal = unwrapBufferValue( - context, - legalPtrOperand, - ee.field); - - TuplePseudoVal::Element element; - element.key = ee.key; - element.val = unwrapBufferValue( - context, - legalPtrOperand, - ee.field); - obj->elements.add(element); - } - - return LegalVal::tuple(obj); - } - } -} - -static IRType* getPointedToType( - IRTypeLegalizationContext* context, - IRType* ptrType) -{ - auto valueType = tryGetPointedToType(context->builder, ptrType); - if( !valueType ) - { - SLANG_UNEXPECTED("expected a pointer type during type legalization"); - } - return valueType; -} - -static LegalType getPointedToType( - IRTypeLegalizationContext* context, - LegalType type) -{ - switch( type.flavor ) - { - case LegalType::Flavor::none: - return LegalType(); - - case LegalType::Flavor::simple: - return LegalType::simple(getPointedToType(context, type.getSimple())); - - case LegalType::Flavor::implicitDeref: - return type.getImplicitDeref()->valueType; - - case LegalType::Flavor::pair: - { - auto pairType = type.getPair(); - auto ordinary = getPointedToType(context, pairType->ordinaryType); - auto special = getPointedToType(context, pairType->specialType); - return LegalType::pair(ordinary, special, pairType->pairInfo); - } - - case LegalType::Flavor::tuple: - { - auto tupleType = type.getTuple(); - RefPtr<TuplePseudoType> resultTuple = new TuplePseudoType(); - for( auto ee : tupleType->elements ) - { - TuplePseudoType::Element resultElement; - resultElement.key = ee.key; - resultElement.type = getPointedToType(context, ee.type); - resultTuple->elements.add(resultElement); - } - return LegalType::tuple(resultTuple); - } - - default: - SLANG_UNEXPECTED("unhandled case in type legalization"); - UNREACHABLE_RETURN(LegalType()); - } -} - -static LegalVal legalizeFieldAddress( - IRTypeLegalizationContext* context, - LegalType type, - LegalVal legalPtrOperand, - IRStructKey* fieldKey) -{ - auto builder = context->builder; - if (type.flavor == LegalType::Flavor::none) - return LegalVal(); - - switch (legalPtrOperand.flavor) - { - case LegalVal::Flavor::none: - return LegalVal(); - - case LegalVal::Flavor::simple: - switch( type.flavor ) - { - case LegalType::Flavor::implicitDeref: - // TODO: Should this case be needed? - return legalizeFieldAddress( - context, - type.getImplicitDeref()->valueType, - legalPtrOperand, - fieldKey); - - default: - return LegalVal::simple( - builder->emitFieldAddress( - type.getSimple(), - legalPtrOperand.getSimple(), - fieldKey)); - } - - case LegalVal::Flavor::pair: - { - // There are two sides, the ordinary and the special, - // and we basically just dispatch to both of them. - auto pairVal = legalPtrOperand.getPair(); - auto pairInfo = pairVal->pairInfo; - auto pairElement = pairInfo->findElement(fieldKey); - if (!pairElement) - { - SLANG_UNEXPECTED("didn't find tuple element"); - UNREACHABLE_RETURN(LegalVal()); - } - - // If the field we are extracting has a pair type, - // that means it exists on both the ordinary and - // special sides. - RefPtr<PairInfo> fieldPairInfo; - LegalType ordinaryType = type; - LegalType specialType = type; - if (type.flavor == LegalType::Flavor::pair) - { - auto fieldPairType = type.getPair(); - fieldPairInfo = fieldPairType->pairInfo; - ordinaryType = fieldPairType->ordinaryType; - specialType = fieldPairType->specialType; - } - - LegalVal ordinaryVal; - LegalVal specialVal; - - if (pairElement->flags & PairInfo::kFlag_hasOrdinary) - { - ordinaryVal = legalizeFieldAddress( - context, - ordinaryType, - pairVal->ordinaryVal, - fieldKey); - } - - if (pairElement->flags & PairInfo::kFlag_hasSpecial) - { - specialVal = legalizeFieldAddress( - context, - specialType, - pairVal->specialVal, - fieldKey); - } - return LegalVal::pair(ordinaryVal, specialVal, fieldPairInfo); - } - break; - - case LegalVal::Flavor::tuple: - { - // The operand is a tuple of pointer-like - // values, we want to extract the element - // corresponding to a field. We will handle - // this by simply returning the corresponding - // element from the operand. - auto ptrTupleInfo = legalPtrOperand.getTuple(); - for (auto ee : ptrTupleInfo->elements) - { - if (ee.key == fieldKey) - { - return ee.val; - } - } - - // TODO: we can legally reach this case now - // when the field is "ordinary". - - SLANG_UNEXPECTED("didn't find tuple element"); - UNREACHABLE_RETURN(LegalVal()); - } - - case LegalVal::Flavor::implicitDeref: - { - // The original value had a level of indirection - // that is now being removed, so should not be - // able to get at the *address* of the field any - // more, and need to resign ourselves to just - // getting at the field *value* and then - // adding an implicit dereference on top of that. - // - auto implicitDerefVal = legalPtrOperand.getImplicitDeref(); - auto valueType = getPointedToType(context, type); - return LegalVal::implicitDeref(legalizeFieldExtract(context, valueType, implicitDerefVal, fieldKey)); - } - - default: - SLANG_UNEXPECTED("unhandled"); - UNREACHABLE_RETURN(LegalVal()); - } -} - -static LegalVal legalizeFieldAddress( - IRTypeLegalizationContext* context, - LegalType type, - LegalVal legalPtrOperand, - LegalVal legalFieldOperand) -{ - // We don't expect any legalization to affect - // the "field" argument. - auto fieldKey = legalFieldOperand.getSimple(); - - return legalizeFieldAddress( - context, - type, - legalPtrOperand, - (IRStructKey*) fieldKey); -} - -static LegalVal legalizeGetElement( - IRTypeLegalizationContext* context, - LegalType type, - LegalVal legalPtrOperand, - IRInst* indexOperand) -{ - auto builder = context->builder; - - switch (legalPtrOperand.flavor) - { - case LegalVal::Flavor::none: - return LegalVal(); - - case LegalVal::Flavor::simple: - return LegalVal::simple( - builder->emitElementExtract( - type.getSimple(), - legalPtrOperand.getSimple(), - indexOperand)); - - case LegalVal::Flavor::pair: - { - // There are two sides, the ordinary and the special, - // and we basically just dispatch to both of them. - auto pairVal = legalPtrOperand.getPair(); - auto pairInfo = pairVal->pairInfo; - - LegalType ordinaryType = type; - LegalType specialType = type; - if (type.flavor == LegalType::Flavor::pair) - { - auto pairType = type.getPair(); - ordinaryType = pairType->ordinaryType; - specialType = pairType->specialType; - } - - LegalVal ordinaryVal = legalizeGetElement( - context, - ordinaryType, - pairVal->ordinaryVal, - indexOperand); - - LegalVal specialVal = legalizeGetElement( - context, - specialType, - pairVal->specialVal, - indexOperand); - - return LegalVal::pair(ordinaryVal, specialVal, pairInfo); - } - break; - - case LegalVal::Flavor::tuple: - { - // The operand is a tuple of pointer-like - // values, we want to extract the element - // corresponding to a field. We will handle - // this by simply returning the corresponding - // element from the operand. - auto ptrTupleInfo = legalPtrOperand.getTuple(); - - RefPtr<TuplePseudoVal> resTupleInfo = new TuplePseudoVal(); - - auto tupleType = type.getTuple(); - SLANG_ASSERT(tupleType); - - auto elemCount = ptrTupleInfo->elements.getCount(); - SLANG_ASSERT(elemCount == tupleType->elements.getCount()); - - for(Index ee = 0; ee < elemCount; ++ee) - { - auto ptrElem = ptrTupleInfo->elements[ee]; - auto elemType = tupleType->elements[ee].type; - - TuplePseudoVal::Element resElem; - resElem.key = ptrElem.key; - resElem.val = legalizeGetElement( - context, - elemType, - ptrElem.val, - indexOperand); - - resTupleInfo->elements.add(resElem); - } - - return LegalVal::tuple(resTupleInfo); - } - - default: - SLANG_UNEXPECTED("unhandled"); - UNREACHABLE_RETURN(LegalVal()); - } -} - -static LegalVal legalizeGetElement( - IRTypeLegalizationContext* context, - LegalType type, - LegalVal legalPtrOperand, - LegalVal legalIndexOperand) -{ - // We don't expect any legalization to affect - // the "index" argument. - auto indexOperand = legalIndexOperand.getSimple(); - - return legalizeGetElement( - context, - type, - legalPtrOperand, - indexOperand); -} - -static LegalVal legalizeGetElementPtr( - IRTypeLegalizationContext* context, - LegalType type, - LegalVal legalPtrOperand, - IRInst* indexOperand) -{ - auto builder = context->builder; - - switch (legalPtrOperand.flavor) - { - case LegalVal::Flavor::none: - return LegalVal(); - - case LegalVal::Flavor::simple: - return LegalVal::simple( - builder->emitElementAddress( - type.getSimple(), - legalPtrOperand.getSimple(), - indexOperand)); - - case LegalVal::Flavor::pair: - { - // There are two sides, the ordinary and the special, - // and we basically just dispatch to both of them. - auto pairVal = legalPtrOperand.getPair(); - auto pairInfo = pairVal->pairInfo; - - LegalType ordinaryType = type; - LegalType specialType = type; - if (type.flavor == LegalType::Flavor::pair) - { - auto pairType = type.getPair(); - ordinaryType = pairType->ordinaryType; - specialType = pairType->specialType; - } - - LegalVal ordinaryVal = legalizeGetElementPtr( - context, - ordinaryType, - pairVal->ordinaryVal, - indexOperand); - - LegalVal specialVal = legalizeGetElementPtr( - context, - specialType, - pairVal->specialVal, - indexOperand); - - return LegalVal::pair(ordinaryVal, specialVal, pairInfo); - } - break; - - case LegalVal::Flavor::tuple: - { - // The operand is a tuple of pointer-like - // values, we want to extract the element - // corresponding to a field. We will handle - // this by simply returning the corresponding - // element from the operand. - auto ptrTupleInfo = legalPtrOperand.getTuple(); - - RefPtr<TuplePseudoVal> resTupleInfo = new TuplePseudoVal(); - - auto tupleType = type.getTuple(); - SLANG_ASSERT(tupleType); - - auto elemCount = ptrTupleInfo->elements.getCount(); - SLANG_ASSERT(elemCount == tupleType->elements.getCount()); - - for(Index ee = 0; ee < elemCount; ++ee) - { - auto ptrElem = ptrTupleInfo->elements[ee]; - auto elemType = tupleType->elements[ee].type; - - TuplePseudoVal::Element resElem; - resElem.key = ptrElem.key; - resElem.val = legalizeGetElementPtr( - context, - elemType, - ptrElem.val, - indexOperand); - - resTupleInfo->elements.add(resElem); - } - - return LegalVal::tuple(resTupleInfo); - } - - case LegalVal::Flavor::implicitDeref: - { - // The original value used to be a pointer to an array, - // and somebody is trying to get at an element pointer. - // Now we just have an array (wrapped with an implicit - // dereference) and need to just fetch the chosen element - // instead (and then wrap the element value with an - // implicit dereference). - // - // The result type for our `getElement` instruction needs - // to be the type *pointed to* by `type`, and not `type. - // - auto valueType = getPointedToType(context, type); - - auto implicitDerefVal = legalPtrOperand.getImplicitDeref(); - return LegalVal::implicitDeref(legalizeGetElement( - context, - valueType, - implicitDerefVal, - indexOperand)); - } - - default: - SLANG_UNEXPECTED("unhandled"); - UNREACHABLE_RETURN(LegalVal()); - } -} - -static LegalVal legalizeGetElementPtr( - IRTypeLegalizationContext* context, - LegalType type, - LegalVal legalPtrOperand, - LegalVal legalIndexOperand) -{ - // We don't expect any legalization to affect - // the "index" argument. - auto indexOperand = legalIndexOperand.getSimple(); - - return legalizeGetElementPtr( - context, - type, - legalPtrOperand, - indexOperand); -} - -static LegalVal legalizeMakeStruct( - IRTypeLegalizationContext* context, - LegalType legalType, - LegalVal const* legalArgs, - UInt argCount) -{ - auto builder = context->builder; - - switch(legalType.flavor) - { - case LegalType::Flavor::none: - return LegalVal(); - - case LegalType::Flavor::simple: - { - List<IRInst*> args; - for(UInt aa = 0; aa < argCount; ++aa) - { - // Note: we assume that all the arguments - // must be simple here, because otherwise - // the `struct` type with them as fields - // would not be simple... - // - args.add(legalArgs[aa].getSimple()); - } - return LegalVal::simple( - builder->emitMakeStruct( - legalType.getSimple(), - argCount, - args.getBuffer())); - } - - case LegalType::Flavor::pair: - { - // There are two sides, the ordinary and the special, - // and we basically just dispatch to both of them. - auto pairType = legalType.getPair(); - auto pairInfo = pairType->pairInfo; - LegalType ordinaryType = pairType->ordinaryType; - LegalType specialType = pairType->specialType; - - List<LegalVal> ordinaryArgs; - List<LegalVal> specialArgs; - UInt argCounter = 0; - for(auto ee : pairInfo->elements) - { - UInt argIndex = argCounter++; - LegalVal arg = legalArgs[argIndex]; - - if( arg.flavor == LegalVal::Flavor::pair ) - { - // The argument is itself a pair - auto argPair = arg.getPair(); - ordinaryArgs.add(argPair->ordinaryVal); - specialArgs.add(argPair->specialVal); - } - else if(ee.flags & Slang::PairInfo::kFlag_hasOrdinary) - { - ordinaryArgs.add(arg); - } - else if(ee.flags & Slang::PairInfo::kFlag_hasSpecial) - { - specialArgs.add(arg); - } - } - - LegalVal ordinaryVal = legalizeMakeStruct( - context, - ordinaryType, - ordinaryArgs.getBuffer(), - ordinaryArgs.getCount()); - - LegalVal specialVal = legalizeMakeStruct( - context, - specialType, - specialArgs.getBuffer(), - specialArgs.getCount()); - - return LegalVal::pair(ordinaryVal, specialVal, pairInfo); - } - break; - - case LegalType::Flavor::tuple: - { - // We are constructing a tuple of values from - // the individual fields. We need to identify - // for each tuple element what field it uses, - // and then extract that field's value. - - auto tupleType = legalType.getTuple(); - - RefPtr<TuplePseudoVal> resTupleInfo = new TuplePseudoVal(); - UInt argCounter = 0; - for(auto typeElem : tupleType->elements) - { - auto elemKey = typeElem.key; - UInt argIndex = argCounter++; - SLANG_ASSERT(argIndex < argCount); - - LegalVal argVal = legalArgs[argIndex]; - - TuplePseudoVal::Element resElem; - resElem.key = elemKey; - resElem.val = argVal; - - resTupleInfo->elements.add(resElem); - } - return LegalVal::tuple(resTupleInfo); - } - - default: - SLANG_UNEXPECTED("unhandled"); - UNREACHABLE_RETURN(LegalVal()); - } -} - -static LegalVal legalizeConstruct(IRTypeLegalizationContext* context, - LegalType type) -{ - switch (type.flavor) - { - case LegalType::Flavor::none: - return LegalVal(); - case LegalType::Flavor::simple: - return LegalVal::simple(context->builder->emitConstructorInst(type.getSimple(), 0, nullptr)); - default: - SLANG_UNEXPECTED("unhandled legalization case for construct inst."); - UNREACHABLE_RETURN(LegalVal()); - } -} - -static LegalVal legalizeInst( - IRTypeLegalizationContext* context, - IRInst* inst, - LegalType type, - LegalVal const* args) -{ - switch (inst->op) - { - case kIROp_Load: - return legalizeLoad(context, args[0]); - - case kIROp_FieldAddress: - return legalizeFieldAddress(context, type, args[0], args[1]); - - case kIROp_FieldExtract: - return legalizeFieldExtract(context, type, args[0], args[1]); - - case kIROp_getElement: - return legalizeGetElement(context, type, args[0], args[1]); - - case kIROp_getElementPtr: - return legalizeGetElementPtr(context, type, args[0], args[1]); - - case kIROp_Store: - return legalizeStore(context, args[0], args[1]); - - case kIROp_Call: - return legalizeCall(context, (IRCall*)inst); - case kIROp_ReturnVal: - return legalizeRetVal(context, args[0]); - case kIROp_makeStruct: - return legalizeMakeStruct( - context, - type, - args, - inst->getOperandCount()); - case kIROp_Construct: - return legalizeConstruct(context, type); - case kIROp_undefined: - return LegalVal(); - default: - // TODO: produce a user-visible diagnostic here - SLANG_UNEXPECTED("non-simple operand(s)!"); - break; - } -} - -RefPtr<VarLayout> findVarLayout(IRInst* value) -{ - if (auto layoutDecoration = value->findDecoration<IRLayoutDecoration>()) - return as<VarLayout>(layoutDecoration->getLayout()); - return nullptr; -} - -static UnownedStringSlice findNameHint(IRInst* inst) -{ - if( auto nameHintDecoration = inst->findDecoration<IRNameHintDecoration>() ) - { - return nameHintDecoration->getName(); - } - return UnownedStringSlice(); -} - -static LegalVal legalizeLocalVar( - IRTypeLegalizationContext* context, - IRVar* irLocalVar) -{ - // Legalize the type for the variable's value - auto originalValueType = irLocalVar->getDataType()->getValueType(); - auto legalValueType = legalizeType( - context, - originalValueType); - - auto originalRate = irLocalVar->getRate(); - - RefPtr<VarLayout> varLayout = findVarLayout(irLocalVar); - RefPtr<TypeLayout> typeLayout = varLayout ? varLayout->typeLayout : nullptr; - - // If we've decided to do implicit deref on the type, - // then go ahead and declare a value of the pointed-to type. - LegalType maybeSimpleType = legalValueType; - while (maybeSimpleType.flavor == LegalType::Flavor::implicitDeref) - { - maybeSimpleType = maybeSimpleType.getImplicitDeref()->valueType; - } - - switch (maybeSimpleType.flavor) - { - case LegalType::Flavor::simple: - { - // Easy case: the type is usable as-is, and we - // should just do that. - auto type = maybeSimpleType.getSimple(); - type = context->builder->getPtrType(type); - if( originalRate ) - { - type = context->builder->getRateQualifiedType( - originalRate, - type); - } - irLocalVar->setFullType(type); - return LegalVal::simple(irLocalVar); - } - - default: - { - // TODO: We don't handle rates in this path. - - context->insertBeforeLocalVar = irLocalVar; - - LegalVarChainLink varChain(LegalVarChain(), varLayout); - - UnownedStringSlice nameHint = findNameHint(irLocalVar); - context->builder->setInsertBefore(irLocalVar); - LegalVal newVal = declareVars(context, kIROp_Var, legalValueType, typeLayout, varChain, nameHint, irLocalVar, nullptr, context->isSpecialType(originalValueType)); - - // Remove the old local var. - irLocalVar->removeFromParent(); - // add old local var to list - context->replacedInstructions.add(irLocalVar); - return newVal; - } - break; - } -} - -static LegalVal legalizeParam( - IRTypeLegalizationContext* context, - IRParam* originalParam) -{ - auto legalParamType = legalizeType(context, originalParam->getFullType()); - if (legalParamType.flavor == LegalType::Flavor::simple) - { - // Simple case: things were legalized to a simple type, - // so we can just use the original parameter as-is. - originalParam->setFullType(legalParamType.getSimple()); - return LegalVal::simple(originalParam); - } - else - { - // Complex case: we need to insert zero or more new parameters, - // which will replace the old ones. - - context->insertBeforeParam = originalParam; - - UnownedStringSlice nameHint = findNameHint(originalParam); - - context->builder->setInsertBefore(originalParam); - auto newVal = declareVars(context, kIROp_Param, legalParamType, nullptr, LegalVarChain(), nameHint, originalParam, nullptr, context->isSpecialType(originalParam->getDataType())); - - originalParam->removeFromParent(); - context->replacedInstructions.add(originalParam); - return newVal; - } -} - -static LegalVal legalizeFunc( - IRTypeLegalizationContext* context, - IRFunc* irFunc); - -static LegalVal legalizeGlobalVar( - IRTypeLegalizationContext* context, - IRGlobalVar* irGlobalVar); - -static LegalVal legalizeGlobalConstant( - IRTypeLegalizationContext* context, - IRGlobalConstant* irGlobalConstant); - -static LegalVal legalizeGlobalParam( - IRTypeLegalizationContext* context, - IRGlobalParam* irGlobalParam); - -static LegalVal legalizeInst( - IRTypeLegalizationContext* context, - IRInst* inst) -{ - // Any additional instructions we need to emit - // in the process of legalizing `inst` should - // by default be insertied right before `inst`. - // - context->builder->setInsertBefore(inst); - - // Special-case certain operations - switch (inst->op) - { - case kIROp_Var: - return legalizeLocalVar(context, cast<IRVar>(inst)); - - case kIROp_Param: - return legalizeParam(context, cast<IRParam>(inst)); - - case kIROp_WitnessTable: - // Just skip these. - break; - - case kIROp_Func: - return legalizeFunc(context, cast<IRFunc>(inst)); - - case kIROp_GlobalVar: - return legalizeGlobalVar(context, cast<IRGlobalVar>(inst)); - - case kIROp_GlobalConstant: - return legalizeGlobalConstant(context, cast<IRGlobalConstant>(inst)); - - case kIROp_GlobalParam: - return legalizeGlobalParam(context, cast<IRGlobalParam>(inst)); - - default: - break; - } - - // We will iterate over all the operands, extract the legalized - // value of each, and collect them in an array for subsequent use. - // - auto argCount = inst->getOperandCount(); - List<LegalVal> legalArgs; - // - // Along the way we will also note whether there were any operands - // with non-simple legalized values. - // - bool anyComplex = false; - for (UInt aa = 0; aa < argCount; ++aa) - { - auto oldArg = inst->getOperand(aa); - auto legalArg = legalizeOperand(context, oldArg); - legalArgs.add(legalArg); - - if (legalArg.flavor != LegalVal::Flavor::simple) - anyComplex = true; - } - - // We must also legalize the type of the instruction, since that - // is implicitly one of its operands. - // - LegalType legalType = legalizeType(context, inst->getFullType()); - - // If there was nothing interesting that occured for the operands - // then we can re-use this instruction as-is. - // - if (!anyComplex && legalType.flavor == LegalType::Flavor::simple) - { - // While the operands are all "simple," they might not necessarily - // be equal to the operands we started with. - // - for (UInt aa = 0; aa < argCount; ++aa) - { - auto legalArg = legalArgs[aa]; - inst->setOperand(aa, legalArg.getSimple()); - } - - inst->setFullType(legalType.getSimple()); - - return LegalVal::simple(inst); - } - - // We have at least one "complex" operand, and we - // need to figure out what to do with it. The anwer - // will, in general, depend on what we are doing. - - // We will set up the IR builder so that any new - // instructions generated will be placed before - // the location of the original instruction. - auto builder = context->builder; - builder->setInsertBefore(inst); - - LegalVal legalVal = legalizeInst( - context, - inst, - legalType, - legalArgs.getBuffer()); - - // After we are done, we will eliminate the - // original instruction by removing it from - // the IR. - // - inst->removeFromParent(); - context->replacedInstructions.add(inst); - - // The value to be used when referencing - // the original instruction will now be - // whatever value(s) we created to replace it. - return legalVal; -} - -static void addParamType(List<IRType*>& ioParamTypes, LegalType t) -{ - switch (t.flavor) - { - case LegalType::Flavor::none: - break; - - case LegalType::Flavor::simple: - ioParamTypes.add(t.getSimple()); - break; - - case LegalType::Flavor::implicitDeref: - { - auto imp = t.getImplicitDeref(); - addParamType(ioParamTypes, imp->valueType); - break; - } - case LegalType::Flavor::pair: - { - auto pairInfo = t.getPair(); - addParamType(ioParamTypes, pairInfo->ordinaryType); - addParamType(ioParamTypes, pairInfo->specialType); - } - break; - case LegalType::Flavor::tuple: - { - auto tup = t.getTuple(); - for (auto & elem : tup->elements) - addParamType(ioParamTypes, elem.type); - } - break; - default: - SLANG_UNEXPECTED("unknown legalized type flavor"); - } -} - -static void legalizeInstsInParent( - IRTypeLegalizationContext* context, - IRInst* parent) -{ - IRInst* nextChild = nullptr; - for(auto child = parent->getFirstDecorationOrChild(); child; child = nextChild) - { - nextChild = child->getNextInst(); - - if (auto block = as<IRBlock>(child)) - { - legalizeInstsInParent(context, block); - } - else - { - LegalVal legalVal = legalizeInst(context, child); - registerLegalizedValue(context, child, legalVal); - } - } -} - -static LegalVal legalizeFunc( - IRTypeLegalizationContext* context, - IRFunc* irFunc) -{ - // Overwrite the function's type with the result of legalization. - - IRFuncType* oldFuncType = irFunc->getDataType(); - UInt oldParamCount = oldFuncType->getParamCount(); - - // TODO: we should give an error message when the result type of a function - // can't be legalized (e.g., trying to return a texture, or a structue that - // contains one). - auto legalReturnType = legalizeType(context, oldFuncType->getResultType()); - IRType* newResultType = nullptr; - switch (legalReturnType.flavor) - { - case LegalType::Flavor::simple: - newResultType = legalReturnType.getSimple(); - break; - case LegalType::Flavor::none: - newResultType = context->builder->getVoidType(); - break; - default: - SLANG_UNEXPECTED("unknown legalized function return type."); - } - List<IRType*> newParamTypes; - for (UInt pp = 0; pp < oldParamCount; ++pp) - { - auto legalParamType = legalizeType(context, oldFuncType->getParamType(pp)); - addParamType(newParamTypes, legalParamType); - } - - auto newFuncType = context->builder->getFuncType( - newParamTypes.getCount(), - newParamTypes.getBuffer(), - newResultType); - - context->builder->setDataType(irFunc, newFuncType); - - legalizeInstsInParent(context, irFunc); - return LegalVal::simple(irFunc); -} - -static LegalVal declareSimpleVar( - IRTypeLegalizationContext* context, - IROp op, - IRType* type, - TypeLayout* typeLayout, - LegalVarChain const& varChain, - UnownedStringSlice nameHint, - IRInst* leafVar, - IRGlobalNameInfo* globalNameInfo) -{ - SLANG_UNUSED(globalNameInfo); - - RefPtr<VarLayout> varLayout = createVarLayout(varChain, typeLayout); - - DeclRef<VarDeclBase> varDeclRef = varChain.getLeafVarDeclRef(); - - IRBuilder* builder = context->builder; - - IRInst* irVar = nullptr; - LegalVal legalVarVal; - - switch (op) - { - case kIROp_GlobalVar: - { - auto globalVar = builder->createGlobalVar(type); - globalVar->removeFromParent(); - globalVar->insertBefore(context->insertBeforeGlobal); - - irVar = globalVar; - legalVarVal = LegalVal::simple(irVar); - } - break; - - case kIROp_GlobalConstant: - { - auto globalConst = builder->createGlobalConstant(type); - globalConst->removeFromParent(); - globalConst->insertBefore(context->insertBeforeGlobal); - - irVar = globalConst; - legalVarVal = LegalVal::simple(globalConst); - } - break; - - case kIROp_GlobalParam: - { - auto globalParam = builder->createGlobalParam(type); - globalParam->removeFromParent(); - globalParam->insertBefore(context->insertBeforeGlobal); - - irVar = globalParam; - legalVarVal = LegalVal::simple(globalParam); - } - break; - - case kIROp_Var: - { - builder->setInsertBefore(context->insertBeforeLocalVar); - auto localVar = builder->emitVar(type); - - irVar = localVar; - legalVarVal = LegalVal::simple(irVar); - - } - break; - - case kIROp_Param: - { - auto param = builder->emitParam(type); - param->insertBefore(context->insertBeforeParam); - - irVar = param; - legalVarVal = LegalVal::simple(irVar); - } - break; - - default: - SLANG_UNEXPECTED("unexpected IR opcode"); - break; - } - - if (irVar) - { - if (varLayout) - { - builder->addLayoutDecoration(irVar, varLayout); - } - - if (varDeclRef) - { - builder->addHighLevelDeclDecoration(irVar, varDeclRef.getDecl()); - } - - if( nameHint.size() ) - { - context->builder->addNameHintDecoration(irVar, nameHint); - } - - if( leafVar ) - { - for( auto decoration : leafVar->getDecorations() ) - { - switch( decoration->op ) - { - case kIROp_FormatDecoration: - cloneDecoration(decoration, irVar); - break; - - default: - break; - } - } - } - - } - - return legalVarVal; -} - - /// Add layout information for the fields of a wrapped buffer type. - /// - /// A wrapped buffer type encodes a buffer like `ConstantBuffer<Foo>` - /// where `Foo` might have interface-type fields that have been - /// specialized to a concrete type. E.g.: - /// - /// struct Car { IDriver driver; int mph; }; - /// ConstantBuffer<Car> machOne; - /// - /// In a case where the `machOne.driver` field has been specialized - /// to the type `SpeedRacer`, we need to generate a legalized - /// buffer layout something like: - /// - /// struct Car_0 { int mph; } - /// struct Wrapped { Car_0 car; SpeedRacer card_d; } - /// ConstantBuffer<Wrapped> machOne; - /// - /// The layout information for the existing `machOne` clearly - /// can't apply because we have a new element type with new fields. - /// - /// This function is used to recursively fill in the layout for - /// the fields of the `Wrapped` type, using information recorded - /// when the legal wrapped buffer type was created. - /// -static void _addFieldsToWrappedBufferElementTypeLayout( - TypeLayout* elementTypeLayout, // layout of the original field type - StructTypeLayout* newTypeLayout, // layout we are filling in - LegalElementWrapping const& elementInfo, // information on how the original type got wrapped - LegalVarChain const& varChain, // chain of variables that is leading to this field - bool isSpecial) // should we assume a leaf field is a special (interface) type? -{ - // The way we handle things depends primary on the - // `elementInfo`, because that tells us how things - // were wrapped up when the type was legalized. - - switch( elementInfo.flavor ) - { - case LegalElementWrapping::Flavor::none: - // A leaf `none` value meant there was nothing - // to encode for a particular field (probably - // had a `void` or empty structure type). - break; - - case LegalElementWrapping::Flavor::simple: - { - auto simpleInfo = elementInfo.getSimple(); - - // A `simple` wrapping means we hit a leaf - // field that can be encoded directly. - // What we do here depends on whether we've - // reached an ordinary field of the original - // data type, or if we've reached a leaf - // field of interface type. - // - // We've been tracking a `varChain` that - // remembers all the parent `struct` fields - // we've navigated through to get here, and - // that information has been tracking two - // different pieces of layout: - // - // * The "primary" layout represents the storage - // of the buffer element type as we usually - // think of its (e.g., the bytes starting at offset zero). - // - // * The "pending" layout tells us where all the - // fields representing concrete types plugged in - // for interface-type slots got placed. - // - // We have tunneled down info to tell us which case - // we should use (`isSpecial`). - // - // Most of the logic is the same between the two - // cases. We will be computing layout information - // for a field of the new/wrapped buffer element type. - // - RefPtr<VarLayout> newFieldLayout; - if(isSpecial) - { - // In the special case, that field will be laid out - // based on the "pending" var chain, and the type - // of the pending data for the element. - // - newFieldLayout = createSimpleVarLayout(varChain.pendingChain, elementTypeLayout->pendingDataTypeLayout); - } - else - { - // The ordinary case just uses the primary layout - // information and the primary/nominal type of - // the field. - // - newFieldLayout = createSimpleVarLayout(varChain.primaryChain, elementTypeLayout); - } - - // Either way, we add the new field to the struct type - // layout we are building, and also update the mapping - // information so that we can find the field layout - // based on the IR key for the struct field. - // - newTypeLayout->fields.add(newFieldLayout); - newTypeLayout->mapKeyToLayout.Add(simpleInfo->key, newFieldLayout); - } - break; - - case LegalElementWrapping::Flavor::implicitDeref: - { - // This is the case where a field in the element type - // has been legalized from `SomePtrLikeType<T>` to - // `T`, so there is a different in levels of indirection. - // - // We need to recurse and see how the type `T` - // got laid out to know what field(s) it might comprise. - // - auto implicitDerefInfo = elementInfo.getImplicitDeref(); - _addFieldsToWrappedBufferElementTypeLayout( - elementTypeLayout, - newTypeLayout, - implicitDerefInfo->field, - varChain, - isSpecial); - return; - } - break; - - case LegalElementWrapping::Flavor::pair: - { - // The pair case is the first main workhorse where - // if we had a type that mixed ordinary and interface-type - // fields, it would get split into an ordinary part - // and a "special" part, each of which might comprise - // zero or more fields. - // - // Here we recurse on both the ordinary and special - // sides, and the only interesting tidbit is that - // we pass along appropriate values for the `isSpecial` - // flag so that we act appropriately upon running - // into a leaf field. - // - auto pairElementInfo = elementInfo.getPair(); - _addFieldsToWrappedBufferElementTypeLayout( - elementTypeLayout, - newTypeLayout, - pairElementInfo->ordinary, - varChain, - false); - _addFieldsToWrappedBufferElementTypeLayout( - elementTypeLayout, - newTypeLayout, - pairElementInfo->special, - varChain, - true); - } - break; - - case LegalElementWrapping::Flavor::tuple: - { - // A tuple comes up when we've turned an aggregate - // with one or more interface-type fields into - // distinct fields at the top level. - // - // For the most part we just recurse on each field, - // but note that we set the `isSpecial` flag on - // the recursive calls, since we never use tuples - // to store anything that isn't special. - - auto tupleInfo = elementInfo.getTuple(); - for( auto ee : tupleInfo->elements ) - { - auto oldFieldLayout = getFieldLayout(elementTypeLayout, ee.key); - SLANG_ASSERT(oldFieldLayout); - - LegalVarChainLink fieldChain(varChain, oldFieldLayout); - - _addFieldsToWrappedBufferElementTypeLayout( - oldFieldLayout->typeLayout, - newTypeLayout, - ee.field, - fieldChain, - true); - } - } - break; - - default: - SLANG_UNEXPECTED("unhandled element wrapping flavor"); - break; - } -} - - /// Add offset information for `kind` to `resultVarLayout`, - /// if it doesn't already exist, and adjust the offset so - /// that it will represent an offset relative to the - /// "primary" data for the surrounding type, rather than - /// being relative to the "pending" data. - /// -static void _addOffsetVarLayoutEntry( - VarLayout* resultVarLayout, - LegalVarChain const& varChain, - LayoutResourceKind kind) -{ - // If the target already has an offset for this kind, bail out. - // - if(resultVarLayout->FindResourceInfo(kind)) - return; - - // Add the `ResourceInfo` that will represent the offset for - // this resource kind (it will be initialized to zero by default) - // - auto resultResInfo = resultVarLayout->findOrAddResourceInfo(kind); - - // Add in any contributions from the "pending" var chain, since - // that chain of offsets will accumulate to get the leaf offset - // within the pending data, which in this case we assume amounts - // to an *absolute* offset. - // - for(auto vv = varChain.pendingChain; vv; vv = vv->next ) - { - if( auto chainResInfo = vv->varLayout->FindResourceInfo(kind) ) - { - resultResInfo->index += chainResInfo->index; - resultResInfo->space += chainResInfo->space; - } - } - - // Subtract any contributions from the primary var chain, since - // we want the resulting offset to be relative to the same - // base as that chain. - // - for(auto vv = varChain.primaryChain; vv; vv = vv->next ) - { - if( auto chainResInfo = vv->varLayout->FindResourceInfo(kind) ) - { - resultResInfo->index -= chainResInfo->index; - resultResInfo->space -= chainResInfo->space; - } - } -} - - /// Create a variable layout for an field with "pending" type. - /// - /// The given `typeLayout` should represent the type of a field - /// that is being stored in "pending" data, but that now needs - /// to be made relative to the "primary" data, because we are - /// legalizing the pending data out of the code. - /// -static RefPtr<VarLayout> _createOffsetVarLayout( - LegalVarChain const& varChain, - TypeLayout* typeLayout) -{ - RefPtr<VarLayout> resultVarLayout = new VarLayout(); - - // For every resource kind the type consumes, we will - // compute an adjusted offset for the variable that - // encodes the (absolute) offset of the pending data - // in `varChain` relative to its primary data. - // - for( auto resInfo : typeLayout->resourceInfos ) - { - _addOffsetVarLayoutEntry(resultVarLayout, varChain, resInfo.kind); - } - - return resultVarLayout; -} - - /// Place offset information from `srcResInfo` onto `dstLayout`, - /// offset by whatever is in `offsetVarLayout` -static void addOffsetResInfo( - VarLayout* dstLayout, - VarLayout::ResourceInfo const& srcResInfo, - VarLayout* offsetVarLayout) -{ - auto kind = srcResInfo.kind; - auto dstResInfo = dstLayout->findOrAddResourceInfo(kind); - - dstResInfo->index = srcResInfo.index; - dstResInfo->space = srcResInfo.space; - - if( auto offsetResInfo = offsetVarLayout->findOrAddResourceInfo(kind) ) - { - dstResInfo->index += offsetResInfo->index; - dstResInfo->space += offsetResInfo->space; - } -} - - /// Create layout information for a wrapped buffer type. - /// - /// A wrapped buffer type encodes a buffer like `ConstantBuffer<Foo>` - /// where `Foo` might have interface-type fields that have been - /// specialized to a concrete type. - /// - /// Consider: - /// - /// struct Car { IDriver driver; int mph; }; - /// ConstantBuffer<Car> machOne; - /// - /// In a case where the `machOne.driver` field has been specialized - /// to the type `SpeedRacer`, we need to generate a legalized - /// buffer layout something like: - /// - /// struct Car_0 { int mph; } - /// struct Wrapped { Car_0 car; SpeedRacer card_d; } - /// ConstantBuffer<Wrapped> machOne; - /// - /// The layout information for the existing `machOne` clearly - /// can't apply because we have a new element type with new fields. - /// - /// This function is used to create a layout for a legalized - /// buffer type that requires wrapping, based on the original - /// type layout information and the variable layout information - /// of the surrounding context (e.g., the global shader parameter - /// that has this type). - /// -static RefPtr<TypeLayout> _createWrappedBufferTypeLayout( - TypeLayout* oldTypeLayout, - WrappedBufferPseudoType* wrappedBufferTypeInfo, - LegalVarChain const& outerVarChain) -{ - // We shouldn't get invoked unless there was a parameter group type, - // so we will sanity check for that just to be sure. - // - auto oldParameterGroupTypeLayout = as<ParameterGroupTypeLayout>(oldTypeLayout); - SLANG_ASSERT(oldParameterGroupTypeLayout); - if(!oldParameterGroupTypeLayout) - return oldTypeLayout; - - // The original type must have been split between the direct/primary - // data and some amount of "pending" data to deal with interface-type - // data in the element type of the parameter group. - // - // The legalization step will have already flattened the data inside of - // the group to a single `struct` type, which places the primary data first, - // and then any pending data into additional fields. - // - // Our job is to compute a type layout that we can apply to that new - // element type, and to a parameter group surrounding it, that will - // re-create the original intention of the split layout (both primary - // and pending data) for a type that now only has the "primary" data. - // - RefPtr<ParameterGroupTypeLayout> newTypeLayout = new ParameterGroupTypeLayout(); - newTypeLayout->type = oldTypeLayout->type; - newTypeLayout->rules = oldTypeLayout->rules; - newTypeLayout->uniformAlignment = oldTypeLayout->uniformAlignment; - for(auto resInfo : oldTypeLayout->resourceInfos) - newTypeLayout->addResourceUsage(resInfo); - - // Any fields in the "pending" data will have offset information - // that is relative to the pending data for their parent, and so on. - // We need to compute layout information that only includes primary - // data, so any offset information that is relative to the pending data - // needs to instead be relative to the primary data. That amounts to - // computing the absolute offset of each pending field, and then - // subtracting off the absolute offset of the primary data. - // - // We will compute the offset that needs to be added up front, - // and store it in the form of a `VarLayout`. The offsets we need - // can be computed from the `outerVarChain`, and we only need to - // store offset information for resource kinds actually consumed - // by the pending data type for the buffer as a whole (e.g., we - // don't need to apply offsetting to uniform bytes, because - // those don't show up in the resource usage of a constant buffer - // itself, and so the offsets already *are* relative to the start - // of the buffer). - // - auto offsetVarLayout = _createOffsetVarLayout(outerVarChain, oldTypeLayout->pendingDataTypeLayout); - LegalVarChainLink offsetVarChain(LegalVarChain(), offsetVarLayout); - - // We will start our construction of the pieces of the output - // type layout by looking at the "container" type/variable. - // - // A parameter block or constant buffer in Slang needs to - // distinguish between the resource usage of the thing in - // the block/buffer, vs. the resource usage of the block/buffer - // itself. Consider: - // - // struct Material { float4 color; Texture2D tex; } - // ConstantBuffer<Material> gMat; - // - // When compiling for Vulkan, the `gMat` constant buffer needs - // a `binding`, and the `tex` field does too, so the overall - // resource usage of `gMat` is two bindings, but we need a - // way to encode which of those bindings goes to `gMat.tex` - // and which to the constant buffer for `gMat` itself. - // - { - // We will start by extracting the "primary" part of the old - // container type/var layout, and constructing new objects - // that will represent the layout for our wrapped buffer. - // - auto oldPrimaryContainerVarLayout = oldParameterGroupTypeLayout->containerVarLayout; - auto oldPrimaryContainerTypeLayout = oldPrimaryContainerVarLayout->typeLayout; - - RefPtr<TypeLayout> newContainerTypeLayout = new TypeLayout(); - newContainerTypeLayout->type = oldPrimaryContainerTypeLayout->type; - - RefPtr<VarLayout> newContainerVarLayout = new VarLayout(); - newContainerVarLayout->typeLayout = newContainerTypeLayout; - - newTypeLayout->containerVarLayout = newContainerVarLayout; - - // Whatever got allocated for the primary container should get copied - // over to the new layout (e.g., if we allocated a constant buffer - // for `gMat` then we need to retain that information). - // - newContainerTypeLayout->addResourceUsageFrom(oldPrimaryContainerTypeLayout); - for( auto resInfo : oldPrimaryContainerVarLayout->resourceInfos ) - { - auto newResInfo = newContainerVarLayout->findOrAddResourceInfo(resInfo.kind); - newResInfo->index = resInfo.index; - newResInfo->space = resInfo.space; - } - - // It is possible that a constant buffer and/or space didn't get - // allocated for the "primary" data, but ended up being required for - // the "pending" data (this would happen if, e.g., a constant buffer - // didn't appear to have any uniform data in it, but then once we - // plugged in concrete types for interface fields it did...), so - // we need to account for that case and copy over the relevant - // resource usage from the pending data, if there is any. - // - if( auto oldPendingContainerVarLayout = oldPrimaryContainerVarLayout->pendingVarLayout ) - { - // Whatever resources were allocated for the pending data type, - // our new combined container type needs to account for them - // (e.g., if we didn't have a constant buffer in the primary - // data, but one got allocated in the pending data, we need - // to end up with type layout information that includes a - // constnat buffer). - // - auto oldPendingContainerTypeLayout = oldPendingContainerVarLayout->typeLayout; - newContainerTypeLayout->addResourceUsageFrom(oldPendingContainerTypeLayout); - - // We also need to add offset information based on the "pending" - // var layout, but we need to deal with the fact that this information - // is currently stored relative to the pending var layout for the surrounding - // context (passed in as `outerVarChain.pendingChain`), but we need it to be - // relative to the primary layout for the surrounding context (`outerVarChain.primaryChain`). - // This is where the `offsetVarLayout` we computed above comes - // in handy, because it represents the value(s) we need to - // add to each of the per-resource-kind offsets. - // - for( auto resInfo : oldPendingContainerVarLayout->resourceInfos ) - { - addOffsetResInfo(newContainerVarLayout, resInfo, offsetVarLayout); - } - } - } - - // Now that we've dealt with the container variable, we can turn - // our attention to the element type. This is the part that - // actually got legalized and required us to create a "wrapped" - // buffer type in the first place, so we know that it will - // have both primary and "pending" parts. - // - // Let's start by extracting the fields we care about from - // the original element type/var layout, and constructing - // the objects we'll use to represent the type/var layout for - // the new element type. - // - auto oldElementVarLayout = oldParameterGroupTypeLayout->elementVarLayout; - auto oldElementTypeLayout = oldElementVarLayout->typeLayout; - - // Now matter what, the element type of a wrapped buffer - // will always have a structure type. - // - RefPtr<StructTypeLayout> newElementTypeLayout = new StructTypeLayout(); - newElementTypeLayout->type = oldElementTypeLayout->type; - - // The `wrappedBufferTypeInfo` that was passed in tells - // us how the fields of the original type got turned into - // zero or more fields in the new element type, so we - // need to follow its recursive structure to build - // layout information for each of the new fields. - // - // We will track a "chain" of parent variables that - // determines how we got to each leaf field, and is - // used to add up the offsets that will be stored - // in the new `VarLayout`s that get created. - // We know we need to add in some offsets (usually - // negative) to any fields that were pending data, - // so we will account for that in the initial - // chain of outer variables that we pass in. - // - LegalVarChain varChainForElementType; - varChainForElementType.primaryChain = nullptr; - varChainForElementType.pendingChain = offsetVarChain.primaryChain; - - _addFieldsToWrappedBufferElementTypeLayout( - oldElementTypeLayout, - newElementTypeLayout, - wrappedBufferTypeInfo->elementInfo, - varChainForElementType, - true); - - // A parameter group type layout holds a `VarLayout` for the element type, - // which encodes the offset of the element type with respect to the - // start of the parameter group as a whole (e.g., to handle the case - // where a constant buffer needs a `binding`, and so does its - // element type, so the offset to the first `binding` for the element - // type is one, not zero. - // - LegalVarChainLink elementVarChain(LegalVarChain(), oldParameterGroupTypeLayout->elementVarLayout); - auto newElementVarLayout = createVarLayout(elementVarChain, newElementTypeLayout); - newTypeLayout->elementVarLayout = newElementVarLayout; - - // For legacy/API reasons, we also need to compute a version of the - // element type where the offset stored in the `elementVarLayout` - // gets "baked in" to the fields of the element type. - // - newTypeLayout->offsetElementTypeLayout = applyOffsetToTypeLayout( - newElementTypeLayout, - newElementVarLayout); - - return newTypeLayout; -} - -static LegalVal declareVars( - IRTypeLegalizationContext* context, - IROp op, - LegalType type, - TypeLayout* inTypeLayout, - LegalVarChain const& inVarChain, - UnownedStringSlice nameHint, - IRInst* leafVar, - IRGlobalNameInfo* globalNameInfo, - bool isSpecial) -{ - LegalVarChain varChain = inVarChain; - TypeLayout* typeLayout = inTypeLayout; - if( isSpecial ) - { - if( varChain.pendingChain ) - { - varChain.primaryChain = varChain.pendingChain; - varChain.pendingChain = nullptr; - } - if( typeLayout ) - { - if( auto pendingTypeLayout = typeLayout->pendingDataTypeLayout ) - { - typeLayout = pendingTypeLayout; - } - } - } - - switch (type.flavor) - { - case LegalType::Flavor::none: - return LegalVal(); - - case LegalType::Flavor::simple: - return declareSimpleVar(context, op, type.getSimple(), typeLayout, varChain, nameHint, leafVar, globalNameInfo); - break; - - case LegalType::Flavor::implicitDeref: - { - // Just declare a variable of the pointed-to type, - // since we are removing the indirection. - - auto val = declareVars( - context, - op, - type.getImplicitDeref()->valueType, - typeLayout, - varChain, - nameHint, - leafVar, - globalNameInfo, - isSpecial); - return LegalVal::implicitDeref(val); - } - break; - - case LegalType::Flavor::pair: - { - auto pairType = type.getPair(); - auto ordinaryVal = declareVars(context, op, pairType->ordinaryType, typeLayout, varChain, nameHint, leafVar, globalNameInfo, false); - auto specialVal = declareVars(context, op, pairType->specialType, typeLayout, varChain, nameHint, leafVar, globalNameInfo, true); - return LegalVal::pair(ordinaryVal, specialVal, pairType->pairInfo); - } - - case LegalType::Flavor::tuple: - { - // Declare one variable for each element of the tuple - auto tupleType = type.getTuple(); - - RefPtr<TuplePseudoVal> tupleVal = new TuplePseudoVal(); - - for (auto ee : tupleType->elements) - { - auto fieldLayout = getFieldLayout(typeLayout, ee.key); - RefPtr<TypeLayout> fieldTypeLayout = fieldLayout ? fieldLayout->typeLayout : nullptr; - - // If we have a type layout coming in, we really expect to have a layout for each field. - SLANG_ASSERT(fieldLayout || !typeLayout); - - // If we are processing layout information, then - // we need to create a new link in the chain - // of variables that will determine offsets - // for the eventual leaf fields... - // - LegalVarChainLink newVarChain(varChain, fieldLayout); - - UnownedStringSlice fieldNameHint; - String joinedNameHintStorage; - if( nameHint.size() ) - { - if( auto fieldNameHintDecoration = ee.key->findDecoration<IRNameHintDecoration>() ) - { - joinedNameHintStorage.append(nameHint); - joinedNameHintStorage.append("."); - joinedNameHintStorage.append(fieldNameHintDecoration->getName()); - - fieldNameHint = joinedNameHintStorage.getUnownedSlice(); - } - - } - - LegalVal fieldVal = declareVars( - context, - op, - ee.type, - fieldTypeLayout, - newVarChain, - fieldNameHint, - ee.key, - globalNameInfo, - true); - - TuplePseudoVal::Element element; - element.key = ee.key; - element.val = fieldVal; - tupleVal->elements.add(element); - } - - return LegalVal::tuple(tupleVal); - } - break; - - case LegalType::Flavor::wrappedBuffer: - { - auto wrappedBuffer = type.getWrappedBuffer(); - - auto wrappedTypeLayout = _createWrappedBufferTypeLayout(typeLayout, wrappedBuffer, varChain); - - auto innerVal = declareSimpleVar( - context, - op, - wrappedBuffer->simpleType, - wrappedTypeLayout, - varChain, - nameHint, - leafVar, - globalNameInfo); - - return LegalVal::wrappedBuffer(innerVal, wrappedBuffer->elementInfo); - } - - default: - SLANG_UNEXPECTED("unhandled"); - UNREACHABLE_RETURN(LegalVal()); - break; - } -} - -static LegalVal legalizeGlobalVar( - IRTypeLegalizationContext* context, - IRGlobalVar* irGlobalVar) -{ - // Legalize the type for the variable's value - auto originalValueType = irGlobalVar->getDataType()->getValueType(); - auto legalValueType = legalizeType( - context, - originalValueType); - - switch (legalValueType.flavor) - { - case LegalType::Flavor::simple: - // Easy case: the type is usable as-is, and we - // should just do that. - context->builder->setDataType( - irGlobalVar, - context->builder->getPtrType( - legalValueType.getSimple())); - return LegalVal::simple(irGlobalVar); - - default: - { - context->insertBeforeGlobal = irGlobalVar->getNextInst(); - - IRGlobalNameInfo globalNameInfo; - globalNameInfo.globalVar = irGlobalVar; - globalNameInfo.counter = 0; - - UnownedStringSlice nameHint = findNameHint(irGlobalVar); - context->builder->setInsertBefore(irGlobalVar); - LegalVal newVal = declareVars(context, kIROp_GlobalVar, legalValueType, nullptr, LegalVarChain(), nameHint, irGlobalVar, &globalNameInfo, context->isSpecialType(originalValueType)); - - // Register the new value as the replacement for the old - registerLegalizedValue(context, irGlobalVar, newVal); - - // Remove the old global from the module. - irGlobalVar->removeFromParent(); - context->replacedInstructions.add(irGlobalVar); - - return newVal; - } - break; - } -} - -static LegalVal legalizeGlobalConstant( - IRTypeLegalizationContext* context, - IRGlobalConstant* irGlobalConstant) -{ - // Legalize the type for the variable's value - auto legalValueType = legalizeType( - context, - irGlobalConstant->getFullType()); - - switch (legalValueType.flavor) - { - case LegalType::Flavor::simple: - // Easy case: the type is usable as-is, and we - // should just do that. - irGlobalConstant->setFullType(legalValueType.getSimple()); - return LegalVal::simple(irGlobalConstant); - - default: - { - context->insertBeforeGlobal = irGlobalConstant->getNextInst(); - - IRGlobalNameInfo globalNameInfo; - globalNameInfo.globalVar = irGlobalConstant; - globalNameInfo.counter = 0; - - // TODO: need to handle initializer here! - - UnownedStringSlice nameHint = findNameHint(irGlobalConstant); - context->builder->setInsertBefore(irGlobalConstant); - LegalVal newVal = declareVars(context, kIROp_GlobalConstant, legalValueType, nullptr, LegalVarChain(), nameHint, irGlobalConstant, &globalNameInfo, context->isSpecialType(irGlobalConstant->getDataType())); - - // Register the new value as the replacement for the old - registerLegalizedValue(context, irGlobalConstant, newVal); - - // Remove the old global from the module. - irGlobalConstant->removeFromParent(); - context->replacedInstructions.add(irGlobalConstant); - - return newVal; - } - break; - } -} - -static LegalVal legalizeGlobalParam( - IRTypeLegalizationContext* context, - IRGlobalParam* irGlobalParam) -{ - // Legalize the type for the variable's value - auto legalValueType = legalizeType( - context, - irGlobalParam->getFullType()); - - RefPtr<VarLayout> varLayout = findVarLayout(irGlobalParam); - RefPtr<TypeLayout> typeLayout = varLayout ? varLayout->typeLayout : nullptr; - - switch (legalValueType.flavor) - { - case LegalType::Flavor::simple: - // Easy case: the type is usable as-is, and we - // should just do that. - irGlobalParam->setFullType(legalValueType.getSimple()); - return LegalVal::simple(irGlobalParam); - - default: - { - context->insertBeforeGlobal = irGlobalParam->getNextInst(); - - LegalVarChainLink varChain(LegalVarChain(), varLayout); - - IRGlobalNameInfo globalNameInfo; - globalNameInfo.globalVar = irGlobalParam; - globalNameInfo.counter = 0; - - // TODO: need to handle initializer here! - - UnownedStringSlice nameHint = findNameHint(irGlobalParam); - context->builder->setInsertBefore(irGlobalParam); - LegalVal newVal = declareVars(context, kIROp_GlobalParam, legalValueType, typeLayout, varChain, nameHint, irGlobalParam, &globalNameInfo, context->isSpecialType(irGlobalParam->getDataType())); - - // Register the new value as the replacement for the old - registerLegalizedValue(context, irGlobalParam, newVal); - - // Remove the old global from the module. - irGlobalParam->removeFromParent(); - context->replacedInstructions.add(irGlobalParam); - - return newVal; - } - break; - } -} - - -static void legalizeTypes( - IRTypeLegalizationContext* context) -{ - // Legalize all the top-level instructions in the module - auto module = context->module; - legalizeInstsInParent(context, module->moduleInst); - - // Clean up after any instructions we replaced along the way. - for (auto& lv : context->replacedInstructions) - { - lv->removeAndDeallocate(); - } -} - -// We use the same basic type legalization machinery for both simplifying -// away resource-type fields nested in `struct`s and for shuffling around -// exisential-box fields to get the layout right. -// -// The differences between the two passes come down to some very small -// distinctions about what types each pass considers "special" (e.g., -// resources in one case and existential boxes in the other), along -// with what they want to do when a uniform/constant buffer needs to -// be made where the element type is non-simple (that is, includes -// some fields of "special" type). -// -// The resource case is then the simpler one: -// -struct IRResourceTypeLegalizationContext : IRTypeLegalizationContext -{ - IRResourceTypeLegalizationContext(IRModule* module) - : IRTypeLegalizationContext(module) - {} - - bool isSpecialType(IRType* type) override - { - // For resource type legalization, the "special" types - // we are working with are resource types. - // - return isResourceType(type); - } - - LegalType createLegalUniformBufferType( - IROp op, - LegalType legalElementType) override - { - // The appropriate strategy for legalizing uniform buffers - // with resources inside already exists, so we can delegate to it. - // - return createLegalUniformBufferTypeForResources( - this, - op, - legalElementType); - } -}; - -// The case for legalizing existential box types is then similar. -// -struct IRExistentialTypeLegalizationContext : IRTypeLegalizationContext -{ - IRExistentialTypeLegalizationContext(IRModule* module) - : IRTypeLegalizationContext(module) - {} - - bool isSpecialType(IRType* inType) override - { - // The "special" types for our purposes are existential - // boxes, or arrays thereof. - // - auto type = unwrapArray(inType); - return as<IRExistentialBoxType>(type) != nullptr; - } - - LegalType createLegalUniformBufferType( - IROp op, - LegalType legalElementType) override - { - // We'll delegate the logic for creating uniform buffers - // over a mix of ordinary and existential-box types to - // a subroutine so it can live near the resource case. - // - // TODO: We should eventually try to refactor this code - // so that related functionality is grouped together. - // - return createLegalUniformBufferTypeForExistentials( - this, - op, - legalElementType); - } -}; - -// The main entry points that are used when transforming IR code -// to get it ready for lower-level codegen are then simple -// wrappers around `legalizeTypes()` that pick an appropriately -// specialized context type to use to get the job done. - -void legalizeResourceTypes( - IRModule* module, - DiagnosticSink* sink) -{ - SLANG_UNUSED(sink); - - IRResourceTypeLegalizationContext context(module); - legalizeTypes(&context); -} - -void legalizeExistentialTypeLayout( - IRModule* module, - DiagnosticSink* sink) -{ - SLANG_UNUSED(module); - SLANG_UNUSED(sink); - - IRExistentialTypeLegalizationContext context(module); - legalizeTypes(&context); -} - - -} |
